diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index e26783af102..c1337b1786b 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -11,6 +11,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released [Commits](https://github.com/scalableminds/webknossos/compare/23.09.0...HEAD) ### Added +- Datasets and annotations can now be more than 3-dimensional, using additional coordinates. [#7136](https://github.com/scalableminds/webknossos/pull/7136) - Added disabled drag handles to volume and skeleton layers for visual consistency. These layer cannot be dragged or reordered. [#7295](https://github.com/scalableminds/webknossos/pull/7295) - Dataset thumbnails for grayscale layers can now be colored using the value in the view configuration. [#7255](https://github.com/scalableminds/webknossos/pull/7255) - OpenID Connect authorization is now compatible with Providers that send the user information in an id_token. [#7294](https://github.com/scalableminds/webknossos/pull/7294) diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index 35fa800d71d..239b699cc93 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -9,3 +9,5 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). [Commits](https://github.com/scalableminds/webknossos/compare/23.09.0...HEAD) ### Postgres Evolutions: +- [108-additional-coordinates](conf/evolutions/108-additional-coordinates.sql) + diff --git a/app/controllers/AnnotationController.scala b/app/controllers/AnnotationController.scala index 54d425674f4..9c19e116545 100755 --- a/app/controllers/AnnotationController.scala +++ b/app/controllers/AnnotationController.scala @@ -8,6 +8,7 @@ import com.scalableminds.util.time.Instant import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.models.annotation.AnnotationLayerType.AnnotationLayerType import com.scalableminds.webknossos.datastore.models.annotation.{AnnotationLayer, AnnotationLayerType} +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.scalableminds.webknossos.tracingstore.tracings.volume.ResolutionRestrictions import com.scalableminds.webknossos.tracingstore.tracings.{TracingIds, TracingType} import io.swagger.annotations._ @@ -39,7 +40,8 @@ case class AnnotationLayerParameters(typ: AnnotationLayerType, autoFallbackLayer: Boolean = false, mappingName: Option[String] = None, resolutionRestrictions: Option[ResolutionRestrictions], - name: Option[String]) + name: Option[String], + additionalAxes: Option[Seq[AdditionalAxis]]) object AnnotationLayerParameters { implicit val jsonFormat: OFormat[AnnotationLayerParameters] = Json.using[WithDefaultValues].format[AnnotationLayerParameters] diff --git a/app/controllers/LegacyApiController.scala b/app/controllers/LegacyApiController.scala index 6db5297c0e1..5c1ca245e0d 100644 --- a/app/controllers/LegacyApiController.scala +++ b/app/controllers/LegacyApiController.scala @@ -431,7 +431,8 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, autoFallbackLayer = false, None, request.body.resolutionRestrictions, - name = Some(AnnotationLayer.defaultSkeletonLayerName) + name = Some(AnnotationLayer.defaultSkeletonLayerName), + None )) val volumeParameters = if (request.body.typ == "skeleton") None @@ -443,7 +444,8 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, autoFallbackLayer = false, None, request.body.resolutionRestrictions, - name = Some(AnnotationLayer.defaultVolumeLayerName) + name = Some(AnnotationLayer.defaultVolumeLayerName), + None )) List(skeletonParameters, volumeParameters).flatten } diff --git a/app/models/annotation/AnnotationService.scala b/app/models/annotation/AnnotationService.scala index 341792f4d36..8d05a1e57d0 100755 --- a/app/models/annotation/AnnotationService.scala +++ b/app/models/annotation/AnnotationService.scala @@ -11,6 +11,7 @@ import com.scalableminds.util.tools.{BoxImplicits, Fox, FoxImplicits, TextUtils} import com.scalableminds.webknossos.datastore.SkeletonTracing._ import com.scalableminds.webknossos.datastore.VolumeTracing.{VolumeTracing, VolumeTracingOpt, VolumeTracings} import com.scalableminds.webknossos.datastore.geometry.{ + AdditionalCoordinateProto, ColorProto, NamedBoundingBoxProto, Vec3DoubleProto, @@ -24,6 +25,7 @@ import com.scalableminds.webknossos.datastore.models.annotation.{ FetchedAnnotationLayer } import com.scalableminds.webknossos.datastore.models.datasource.{ + AdditionalAxis, ElementClass, DataSourceLike => DataSource, SegmentationLayerLike => SegmentationLayer @@ -77,7 +79,8 @@ case class RedundantTracingProperties( editPosition: Vec3IntProto, editRotation: Vec3DoubleProto, zoomLevel: Double, - userBoundingBoxes: Seq[NamedBoundingBoxProto] + userBoundingBoxes: Seq[NamedBoundingBoxProto], + editPositionAdditionalCoordinates: Seq[AdditionalCoordinateProto], ) class AnnotationService @Inject()( @@ -144,6 +147,8 @@ class AnnotationService @Inject()( ): Fox[VolumeTracing] = { val resolutions = VolumeTracingDownsampling.magsForVolumeTracing(dataSource, fallbackLayer) val resolutionsRestricted = resolutionRestrictions.filterAllowed(resolutions) + val additionalCoordinates = + fallbackLayer.map(_.additionalAxes).getOrElse(dataSource.additionalAxesUnion) for { _ <- bool2Fox(resolutionsRestricted.nonEmpty) ?~> "annotation.volume.resolutionRestrictionsTooTight" } yield @@ -163,7 +168,8 @@ class AnnotationService @Inject()( organizationName = Some(datasetOrganizationName), mappingName = mappingName, resolutions = resolutionsRestricted.map(vec3IntToProto), - hasSegmentIndex = Some(fallbackLayer.isEmpty) + hasSegmentIndex = Some(fallbackLayer.isEmpty), + additionalAxes = AdditionalAxis.toProto(additionalCoordinates) ) } @@ -240,13 +246,15 @@ class AnnotationService @Inject()( dataSetName = dataSet.name, editPosition = dataSource.center, organizationName = Some(datasetOrganizationName), + additionalAxes = AdditionalAxis.toProto(dataSource.additionalAxesUnion) ) val skeletonAdapted = oldPrecedenceLayerProperties.map { p => skeleton.copy( editPosition = p.editPosition, editRotation = p.editRotation, zoomLevel = p.zoomLevel, - userBoundingBoxes = p.userBoundingBoxes + userBoundingBoxes = p.userBoundingBoxes, + editPositionAdditionalCoordinates = p.editPositionAdditionalCoordinates ) }.getOrElse(skeleton) for { @@ -273,7 +281,8 @@ class AnnotationService @Inject()( editPosition = p.editPosition, editRotation = p.editRotation, zoomLevel = p.zoomLevel, - userBoundingBoxes = p.userBoundingBoxes + userBoundingBoxes = p.userBoundingBoxes, + editPositionAdditionalCoordinates = p.editPositionAdditionalCoordinates ) }.getOrElse(volumeTracing) volumeTracingId <- client.saveVolumeTracing(volumeTracingAdapted) @@ -305,7 +314,8 @@ class AnnotationService @Inject()( s.editRotation, s.zoomLevel, s.userBoundingBoxes ++ s.userBoundingBox.map( - com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)) + com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)), + s.editPositionAdditionalCoordinates ) case Right(v) => RedundantTracingProperties( @@ -313,7 +323,8 @@ class AnnotationService @Inject()( v.editRotation, v.zoomLevel, v.userBoundingBoxes ++ v.userBoundingBox.map( - com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)) + com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto(0, None, None, None, _)), + v.editPositionAdditionalCoordinates ) } @@ -384,7 +395,8 @@ class AnnotationService @Inject()( autoFallbackLayer = false, None, Some(ResolutionRestrictions.empty), - Some(AnnotationLayer.defaultNameForType(newAnnotationLayerType)) + Some(AnnotationLayer.defaultNameForType(newAnnotationLayerType)), + None ) _ <- addAnnotationLayer(annotation, organizationName, newAnnotationLayerParameters) ?~> "makeHybrid.createTracings.failed" } yield () diff --git a/app/models/annotation/nml/NmlParser.scala b/app/models/annotation/nml/NmlParser.scala index 012c1ee4478..14a34e7a88e 100755 --- a/app/models/annotation/nml/NmlParser.scala +++ b/app/models/annotation/nml/NmlParser.scala @@ -4,7 +4,14 @@ import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} import com.scalableminds.util.tools.ExtendedTypes.{ExtendedDouble, ExtendedString} import com.scalableminds.webknossos.datastore.SkeletonTracing._ import com.scalableminds.webknossos.datastore.VolumeTracing.{Segment, SegmentGroup, VolumeTracing} -import com.scalableminds.webknossos.datastore.geometry.{ColorProto, NamedBoundingBoxProto, Vec3IntProto} +import com.scalableminds.webknossos.datastore.geometry.{ + AdditionalAxisProto, + AdditionalCoordinateProto, + ColorProto, + NamedBoundingBoxProto, + Vec2IntProto, + Vec3IntProto +} import com.scalableminds.webknossos.datastore.helpers.{NodeDefaults, ProtoGeometryImplicits, SkeletonTracingDefaults} import com.scalableminds.webknossos.datastore.models.datasource.ElementClass import com.scalableminds.webknossos.tracingstore.tracings.ColorGenerator @@ -14,12 +21,12 @@ import com.scalableminds.webknossos.tracingstore.tracings.volume.VolumeSegmentIn import com.typesafe.scalalogging.LazyLogging import models.annotation.UploadedVolumeLayer import net.liftweb.common.Box._ -import net.liftweb.common.{Box, Empty, Failure} +import net.liftweb.common.{Box, Empty, Failure, Full} import play.api.i18n.{Messages, MessagesProvider} import java.io.InputStream import scala.collection.{immutable, mutable} -import scala.xml.{NodeSeq, XML, Node => XMLNode} +import scala.xml.{Attribute, NodeSeq, XML, Node => XMLNode} object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGenerator { @@ -51,6 +58,7 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener treesSplit = treesAndGroupsAfterSplitting._1 treeGroupsAfterSplit = treesAndGroupsAfterSplitting._2 _ <- TreeValidator.validateTrees(treesSplit, treeGroupsAfterSplit, branchPoints, comments) + additionalAxisProtos <- parseAdditionalAxes(parameters \ "additionalAxes") } yield { val dataSetName = overwritingDataSetName.getOrElse(parseDataSetName(parameters \ "experiment")) val description = parseDescription(parameters \ "experiment") @@ -58,8 +66,8 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener val organizationName = if (overwritingDataSetName.isDefined) None else parseOrganizationName(parameters \ "experiment") val activeNodeId = parseActiveNode(parameters \ "activeNode") - val editPosition = - parseEditPosition(parameters \ "editPosition").getOrElse(SkeletonTracingDefaults.editPosition) + val (editPosition, editPositionAdditionalCoordinates) = + parseEditPosition(parameters \ "editPosition").getOrElse((SkeletonTracingDefaults.editPosition, Seq())) val editRotation = parseEditRotation(parameters \ "editRotation").getOrElse(SkeletonTracingDefaults.editRotation) val zoomLevel = parseZoomLevel(parameters \ "zoomLevel").getOrElse(SkeletonTracingDefaults.zoomLevel) @@ -92,7 +100,9 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener organizationName, segments = v.segments, segmentGroups = v.segmentGroups, - hasSegmentIndex = VolumeSegmentIndexService.canHaveSegmentIndex(v.fallbackLayerName) + hasSegmentIndex = VolumeSegmentIndexService.canHaveSegmentIndex(v.fallbackLayerName), + editPositionAdditionalCoordinates = editPositionAdditionalCoordinates, + additionalAxes = additionalAxisProtos ), basePath.getOrElse("") + v.dataZipPath, v.name, @@ -116,7 +126,9 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener None, treeGroupsAfterSplit, userBoundingBoxes, - organizationName + organizationName, + editPositionAdditionalCoordinates, + additionalAxes = additionalAxisProtos ) ) @@ -189,13 +201,15 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener case (Some(x), Some(y), Some(z)) => Some(Vec3IntProto(x.toInt, y.toInt, z.toInt)) case _ => None } + val anchorPositionAdditionalCoordinates = parseAdditionalCoordinateValues(node) Segment( segmentId = getSingleAttribute(node, "id").toLong, anchorPosition = anchorPosition, name = getSingleAttributeOpt(node, "name"), creationTime = getSingleAttributeOpt(node, "created").flatMap(_.toLongOpt), color = parseColorOpt(node), - groupId = getSingleAttribute(node, "groupId").toIntOpt + groupId = getSingleAttribute(node, "groupId").toIntOpt, + anchorPositionAdditionalCoordinates = anchorPositionAdditionalCoordinates ) }) @@ -248,6 +262,33 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener depth <- getSingleAttribute(node, "depth").toIntOpt } yield BoundingBox(Vec3Int(topLeftX, topLeftY, topLeftZ), width, height, depth) + private def parseAdditionalAxes(nodes: NodeSeq)(implicit m: MessagesProvider) = { + val additionalAxes = nodes.headOption.map( + _.child.flatMap( + additionalAxisNode => { + for { + name <- getSingleAttributeOpt(additionalAxisNode, "name") + indexStr <- getSingleAttributeOpt(additionalAxisNode, "index") + index <- indexStr.toIntOpt + minStr <- getSingleAttributeOpt(additionalAxisNode, "min") + min <- minStr.toIntOpt + maxStr <- getSingleAttributeOpt(additionalAxisNode, "max") + max <- maxStr.toIntOpt + } yield new AdditionalAxisProto(name, index, Vec2IntProto(min, max)) + } + ) + ) + additionalAxes match { + case Some(axes) => + if (axes.map(_.name).distinct.size == axes.size) { + Full(axes) + } else { + Failure(Messages("nml.additionalCoordinates.notUnique")) + } + case None => Full(Seq()) + } + } + private def parseDataSetName(nodes: NodeSeq): String = nodes.headOption.map(node => getSingleAttribute(node, "name")).getOrElse("") @@ -266,8 +307,12 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener private def parseTime(nodes: NodeSeq): Long = nodes.headOption.flatMap(node => getSingleAttribute(node, "ms").toLongOpt).getOrElse(DEFAULT_TIME) - private def parseEditPosition(nodes: NodeSeq): Option[Vec3Int] = - nodes.headOption.flatMap(parseVec3Int) + private def parseEditPosition(nodes: NodeSeq): Option[(Vec3Int, Seq[AdditionalCoordinateProto])] = + nodes.headOption.flatMap(n => { + val xyz = parseVec3Int(n) + val additionalCoordinates = parseAdditionalCoordinateValues(n) + xyz.map(value => (value, additionalCoordinates)) + }) private def parseEditRotation(nodes: NodeSeq): Option[Vec3Double] = nodes.headOption.flatMap(parseRotationForParams) @@ -451,6 +496,7 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener for { id <- nodeIdText.toIntOpt ?~ Messages("nml.node.id.invalid", "", nodeIdText) radius = getSingleAttribute(node, "radius").toFloatOpt.getOrElse(NodeDefaults.radius) + additionalCoordinates = parseAdditionalCoordinateValues(node) position <- parseVec3Int(node) ?~ Messages("nml.node.attribute.invalid", "position", id) } yield { val viewport = parseViewport(node) @@ -459,8 +505,33 @@ object NmlParser extends LazyLogging with ProtoGeometryImplicits with ColorGener val bitDepth = parseBitDepth(node) val interpolation = parseInterpolation(node) val rotation = parseRotationForNode(node).getOrElse(NodeDefaults.rotation) - Node(id, position, rotation, radius, viewport, resolution, bitDepth, interpolation, timestamp) + Node(id, + position, + rotation, + radius, + viewport, + resolution, + bitDepth, + interpolation, + timestamp, + additionalCoordinates) } } + private def parseAdditionalCoordinateValues(node: XMLNode): Seq[AdditionalCoordinateProto] = { + val regex = "additionalCoordinate-(\\w)".r("name") + node.attributes.flatMap { + case attribute: Attribute => { + if (attribute.key.startsWith("additionalCoordinate")) { + Some( + new AdditionalCoordinateProto(regex.findAllIn(attribute.key).group("name"), + attribute.value.toString().toInt)) + } else { + None + } + } + case _ => None + }.toSeq + } + } diff --git a/app/models/annotation/nml/NmlWriter.scala b/app/models/annotation/nml/NmlWriter.scala index 2ad1ff8b831..763582f07f3 100644 --- a/app/models/annotation/nml/NmlWriter.scala +++ b/app/models/annotation/nml/NmlWriter.scala @@ -30,7 +30,9 @@ case class NmlParameters( zoomLevel: Double, activeNodeId: Option[Int], userBoundingBoxes: Seq[NamedBoundingBoxProto], - taskBoundingBox: Option[BoundingBoxProto] + taskBoundingBox: Option[BoundingBoxProto], + additionalAxisProtos: Seq[AdditionalAxisProto], + editPositionAdditionalCoordinates: Seq[AdditionalCoordinateProto] ) class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { @@ -127,7 +129,9 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { s.zoomLevel, s.activeNodeId, s.userBoundingBoxes ++ s.userBoundingBox.map(NamedBoundingBoxProto(0, None, None, None, _)), - s.boundingBox + s.boundingBox, + s.additionalAxes, + s.editPositionAdditionalCoordinates ) case Right(v) => NmlParameters( @@ -142,7 +146,9 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { v.zoomLevel, None, v.userBoundingBoxes ++ v.userBoundingBox.map(NamedBoundingBoxProto(0, None, None, None, _)), - if (annotation.exists(_._task.isDefined)) Some(v.boundingBox) else None + if (annotation.exists(_._task.isDefined)) Some(v.boundingBox) else None, + v.additionalAxes, + v.editPositionAdditionalCoordinates ) } } yield nmlParameters @@ -181,6 +187,7 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { writer.writeAttribute("x", parameters.editPosition.x.toString) writer.writeAttribute("y", parameters.editPosition.y.toString) writer.writeAttribute("z", parameters.editPosition.z.toString) + parameters.editPositionAdditionalCoordinates.foreach(writeAdditionalCoordinateValue) } Xml.withinElementSync("editRotation") { writer.writeAttribute("xRot", parameters.editRotation.x.toString) @@ -207,6 +214,18 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { parameters.taskBoundingBox.foreach { b => Xml.withinElementSync("taskBoundingBox")(writeBoundingBox(b)) } + if (parameters.additionalAxisProtos.nonEmpty) { + Xml.withinElementSync("additionalAxes") { + parameters.additionalAxisProtos.foreach(a => { + Xml.withinElementSync("additionalAxis") { + writer.writeAttribute("name", a.name) + writer.writeAttribute("index", a.index.toString) + writer.writeAttribute("min", a.bounds.x.toString) + writer.writeAttribute("max", a.bounds.y.toString) + } + }) + } + } } // Write volume things from FetchedAnnotationLayer. Caller must ensure that it is a volume annotation layer @@ -249,6 +268,7 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { writer.writeAttribute("anchorPositionX", a.x.toString) writer.writeAttribute("anchorPositionY", a.y.toString) writer.writeAttribute("anchorPositionZ", a.z.toString) + s.anchorPositionAdditionalCoordinates.foreach(writeAdditionalCoordinateValue) } s.color.foreach(_ => writeColor(s.color)) s.groupId.foreach(groupId => writer.writeAttribute("groupId", groupId.toString)) @@ -293,6 +313,7 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { writer.writeAttribute("bitDepth", n.bitDepth.toString) writer.writeAttribute("interpolation", n.interpolation.toString) writer.writeAttribute("time", n.createdTimestamp.toString) + n.additionalCoordinates.foreach(writeAdditionalCoordinateValue) } } @@ -387,4 +408,8 @@ class NmlWriter @Inject()(implicit ec: ExecutionContext) extends FoxImplicits { writer.writeAttribute("color.b", color.map(_.b.toString).getOrElse("")) writer.writeAttribute("color.a", color.map(_.a.toString).getOrElse("")) } + + def writeAdditionalCoordinateValue(additionalCoordinate: AdditionalCoordinateProto)( + implicit writer: XMLStreamWriter): Unit = + writer.writeAttribute(s"additionalCoordinate-${additionalCoordinate.name}", additionalCoordinate.value.toString) } diff --git a/app/models/binary/DataSet.scala b/app/models/binary/DataSet.scala index d60dd1ecfcb..7bec4de3b5f 100755 --- a/app/models/binary/DataSet.scala +++ b/app/models/binary/DataSet.scala @@ -10,6 +10,7 @@ import com.scalableminds.webknossos.datastore.models.datasource.inbox.{InboxData import com.scalableminds.webknossos.datastore.models.datasource.{ AbstractDataLayer, AbstractSegmentationLayer, + AdditionalAxis, Category, CoordinateTransformation, CoordinateTransformationType, @@ -602,13 +603,7 @@ class DataSetResolutionsDAO @Inject()(sqlClient: SqlClient)(implicit ec: Executi } } } - for { - _ <- run( - DBIO.sequence(List(clearQuery) ++ insertQueries).transactionally.withTransactionIsolation(Serializable), - retryCount = 50, - retryIfErrorContains = List(transactionSerializationError) - ) - } yield () + replaceSequentiallyAsTransaction(clearQuery, insertQueries) } } @@ -616,7 +611,8 @@ class DataSetResolutionsDAO @Inject()(sqlClient: SqlClient)(implicit ec: Executi class DataSetDataLayerDAO @Inject()( sqlClient: SqlClient, dataSetResolutionsDAO: DataSetResolutionsDAO, - datasetCoordinateTransformationsDAO: DatasetCoordinateTransformationsDAO)(implicit ec: ExecutionContext) + datasetCoordinateTransformationsDAO: DatasetCoordinateTransformationsDAO, + datasetLayerAdditionalAxesDAO: DatasetLayerAdditionalAxesDAO)(implicit ec: ExecutionContext) extends SimpleSQLDAO(sqlClient) { private def parseRow(row: DatasetLayersRow, dataSetId: ObjectId): Fox[DataLayer] = { @@ -634,6 +630,8 @@ class DataSetDataLayerDAO @Inject()( coordinateTransformations <- datasetCoordinateTransformationsDAO.findCoordinateTransformationsForLayer(dataSetId, row.name) coordinateTransformationsOpt = if (coordinateTransformations.isEmpty) None else Some(coordinateTransformations) + additionalAxes <- datasetLayerAdditionalAxesDAO.findAllForDataSetAndDataLayerName(dataSetId, row.name) + additionalAxesOpt = if (additionalAxes.isEmpty) None else Some(additionalAxes) } yield { category match { case Category.segmentation => @@ -649,7 +647,8 @@ class DataSetDataLayerDAO @Inject()( mappingsAsSet.flatMap(m => if (m.isEmpty) None else Some(m)), defaultViewConfigurationOpt, adminViewConfigurationOpt, - coordinateTransformationsOpt + coordinateTransformationsOpt, + additionalAxesOpt )) case Category.color => Fox.successful( @@ -661,7 +660,8 @@ class DataSetDataLayerDAO @Inject()( elementClass, defaultViewConfigurationOpt, adminViewConfigurationOpt, - coordinateTransformationsOpt + coordinateTransformationsOpt, + additionalAxesOpt )) case _ => Fox.failure(s"Could not match dataset layer with category $category") } @@ -722,6 +722,7 @@ class DataSetDataLayerDAO @Inject()( _ <- dataSetResolutionsDAO.updateResolutions(dataSetId, source.toUsable.map(_.dataLayers)) _ <- datasetCoordinateTransformationsDAO.updateCoordinateTransformations(dataSetId, source.toUsable.map(_.dataLayers)) + _ <- datasetLayerAdditionalAxesDAO.updateAdditionalAxes(dataSetId, source.toUsable.map(_.dataLayers)) } yield () } @@ -812,13 +813,38 @@ class DatasetCoordinateTransformationsDAO @Inject()(sqlClient: SqlClient)(implic } } } - for { - _ <- run( - DBIO.sequence(List(clearQuery) ++ insertQueries).transactionally.withTransactionIsolation(Serializable), - retryCount = 50, - retryIfErrorContains = List(transactionSerializationError) - ) - } yield () + replaceSequentiallyAsTransaction(clearQuery, insertQueries) } +} + +class DatasetLayerAdditionalAxesDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext) + extends SimpleSQLDAO(sqlClient) { + + private def parseRow(row: DatasetLayerAdditionalaxesRow): AdditionalAxis = + AdditionalAxis(row.name, Array(row.lowerbound, row.upperbound), row.index) + def findAllForDataSetAndDataLayerName(dataSetId: ObjectId, dataLayerName: String): Fox[Seq[AdditionalAxis]] = + for { + rows <- run(q"""SELECT * + FROM webknossos.dataSet_layer_additionalAxes + WHERE _dataSet = $dataSetId AND layerName = $dataLayerName""".as[DatasetLayerAdditionalaxesRow]) + additionalAxes = rows.map(parseRow) + } yield additionalAxes + + def updateAdditionalAxes(dataSetId: ObjectId, dataLayersOpt: Option[List[DataLayer]]): Fox[Unit] = { + val clearQuery = + q"DELETE FROM webknossos.dataSet_layer_additionalAxes WHERE _dataSet = $dataSetId".asUpdate + val insertQueries = dataLayersOpt.getOrElse(List.empty).flatMap { layer: DataLayer => + layer.additionalAxes.getOrElse(List.empty).map { additionalAxis => + { + q"""INSERT INTO webknossos.dataSet_layer_additionalAxes(_dataSet, layerName, name, lowerBound, upperBound, index) + values( + $dataSetId, ${layer.name}, ${additionalAxis.name}, ${additionalAxis.lowerBound}, ${additionalAxis.upperBound}, ${additionalAxis.index}) + """.asUpdate + } + + } + } + replaceSequentiallyAsTransaction(clearQuery, insertQueries) + } } diff --git a/app/models/binary/DataSetService.scala b/app/models/binary/DataSetService.scala index ef300a2b02e..5daf91a4b0a 100644 --- a/app/models/binary/DataSetService.scala +++ b/app/models/binary/DataSetService.scala @@ -36,6 +36,7 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO, teamDAO: TeamDAO, workerDAO: WorkerDAO, folderDAO: FolderDAO, + additionalAxesDAO: DatasetLayerAdditionalAxesDAO, dataStoreService: DataStoreService, teamService: TeamService, thumbnailCachingService: ThumbnailCachingService, diff --git a/app/models/binary/explore/NgffExplorer.scala b/app/models/binary/explore/NgffExplorer.scala index 7813e535e87..c3f5427d4af 100644 --- a/app/models/binary/explore/NgffExplorer.scala +++ b/app/models/binary/explore/NgffExplorer.scala @@ -9,7 +9,12 @@ import com.scalableminds.webknossos.datastore.datareaders.AxisOrder import com.scalableminds.webknossos.datastore.datareaders.zarr._ import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.datasource.LayerViewConfiguration.LayerViewConfiguration -import com.scalableminds.webknossos.datastore.models.datasource.{Category, ElementClass, LayerViewConfiguration} +import com.scalableminds.webknossos.datastore.models.datasource.{ + AdditionalAxis, + Category, + ElementClass, + LayerViewConfiguration +} import play.api.libs.json.{JsArray, JsNumber} import scala.concurrent.ExecutionContext @@ -88,24 +93,51 @@ class NgffExplorer(implicit val ec: ExecutionContext) extends RemoteLayerExplore } boundingBox = boundingBoxFromMags(magsWithAttributes) + additionalAxes <- getAdditionalAxes(multiscale, remotePath) layer: ZarrLayer = if (looksLikeSegmentationLayer(name, elementClass) || isSegmentation) { - ZarrSegmentationLayer(channelName, - boundingBox, - elementClass, - magsWithAttributes.map(_.mag), - largestSegmentId = None, - defaultViewConfiguration = Some(viewConfig)) + ZarrSegmentationLayer( + channelName, + boundingBox, + elementClass, + magsWithAttributes.map(_.mag), + largestSegmentId = None, + additionalAxes = Some(additionalAxes), + defaultViewConfiguration = Some(viewConfig) + ) } else - ZarrDataLayer(channelName, - Category.color, - boundingBox, - elementClass, - magsWithAttributes.map(_.mag), - defaultViewConfiguration = Some(viewConfig)) + ZarrDataLayer( + channelName, + Category.color, + boundingBox, + elementClass, + magsWithAttributes.map(_.mag), + additionalAxes = Some(additionalAxes), + defaultViewConfiguration = Some(viewConfig) + ) } yield (layer, voxelSizeNanometers) }) } yield layerTuples + private def getAdditionalAxes(multiscale: NgffMultiscalesItem, remotePath: VaultPath): Fox[Seq[AdditionalAxis]] = { + val defaultAxes = List("c", "x", "y", "z") + for { + // Selecting shape of first mag, assuming no mags for additional coordinates + dataset <- Fox.option2Fox(multiscale.datasets.headOption) + zarrHeader <- getZarrHeader(dataset, remotePath) + shape = zarrHeader.shape + } yield { + multiscale.axes.zipWithIndex.flatMap(axisAndIndex => + if (!defaultAxes.contains(axisAndIndex._1.name)) { + Some( + AdditionalAxis(name = axisAndIndex._1.name, + bounds = Array(0, shape(axisAndIndex._2) - 1), + index = axisAndIndex._2)) + } else { + None + }) + } + } + private case class ChannelAttributes(color: Option[Color], name: Option[String]) private def getChannelAttributes( @@ -155,17 +187,23 @@ class NgffExplorer(implicit val ec: ExecutionContext) extends RemoteLayerExplore case _ => e } + private def getZarrHeader(ngffDataset: NgffDataset, layerPath: VaultPath) = { + val magPath = layerPath / ngffDataset.path + val zarrayPath = magPath / ZarrHeader.FILENAME_DOT_ZARRAY + parseJsonFromPath[ZarrHeader](zarrayPath) ?~> s"failed to read zarr header at $zarrayPath" + } + private def zarrMagFromNgffDataset(ngffDataset: NgffDataset, layerPath: VaultPath, voxelSizeInAxisUnits: Vec3Double, axisOrder: AxisOrder, credentialId: Option[String], - channelIndex: Option[Int]): Fox[MagWithAttributes] = + channelIndex: Option[Int])(implicit ec: ExecutionContext): Fox[MagWithAttributes] = for { mag <- magFromTransforms(ngffDataset.coordinateTransformations, voxelSizeInAxisUnits, axisOrder) ?~> "Could not extract mag from scale transforms" magPath = layerPath / ngffDataset.path zarrayPath = magPath / ZarrHeader.FILENAME_DOT_ZARRAY - zarrHeader <- parseJsonFromPath[ZarrHeader](zarrayPath) ?~> s"failed to read zarr header at $zarrayPath" + zarrHeader <- getZarrHeader(ngffDataset, layerPath) elementClass <- zarrHeader.elementClass ?~> s"failed to read element class from zarr header at $zarrayPath" boundingBox <- zarrHeader.boundingBox(axisOrder) ?~> s"failed to read bounding box from zarr header at $zarrayPath" } yield diff --git a/app/models/binary/explore/WebknossosZarrExplorer.scala b/app/models/binary/explore/WebknossosZarrExplorer.scala index 092ba11872c..9e87088e3df 100644 --- a/app/models/binary/explore/WebknossosZarrExplorer.scala +++ b/app/models/binary/explore/WebknossosZarrExplorer.scala @@ -40,7 +40,8 @@ class WebknossosZarrExplorer(implicit val ec: ExecutionContext) extends RemoteLa Category.color, l.boundingBox, zarrLayer._1.elementClass, - zarrLayer._1.mags), + zarrLayer._1.mags, + additionalAxes = None), zarrLayer._2)) case layer => Fox.failure(s"Only remote Zarr layers are supported, got ${layer.getClass}.") } diff --git a/app/models/binary/explore/ZarrArrayExplorer.scala b/app/models/binary/explore/ZarrArrayExplorer.scala index b4a2b8fc95c..e1902be3381 100644 --- a/app/models/binary/explore/ZarrArrayExplorer.scala +++ b/app/models/binary/explore/ZarrArrayExplorer.scala @@ -31,7 +31,8 @@ class ZarrArrayExplorer(implicit val ec: ExecutionContext) extends RemoteLayerEx credentialId) layer: ZarrLayer = if (looksLikeSegmentationLayer(name, elementClass)) { ZarrSegmentationLayer(name, boundingBox, elementClass, List(magLocator), largestSegmentId = None) - } else ZarrDataLayer(name, Category.color, boundingBox, elementClass, List(magLocator)) + } else + ZarrDataLayer(name, Category.color, boundingBox, elementClass, List(magLocator), additionalAxes = None) } yield List((layer, Vec3Double(1.0, 1.0, 1.0))) } diff --git a/app/models/team/Team.scala b/app/models/team/Team.scala index fad881a3481..8a12b67ccb7 100755 --- a/app/models/team/Team.scala +++ b/app/models/team/Team.scala @@ -14,8 +14,6 @@ import models.task.TaskTypeDAO import models.user.User import play.api.i18n.{Messages, MessagesProvider} import play.api.libs.json._ -import slick.jdbc.PostgresProfile.api._ -import slick.jdbc.TransactionIsolation.Serializable import slick.lifted.Rep import utils.sql.{SQLDAO, SqlClient, SqlToken} import utils.ObjectId @@ -215,12 +213,7 @@ class TeamDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext) val insertQueries = allowedTeams.map(teamId => q"""INSERT INTO webknossos.dataSet_allowedTeams(_dataSet, _team) VALUES($datasetId, $teamId)""".asUpdate) - val composedQuery = DBIO.sequence(List(clearQuery) ++ insertQueries) - for { - _ <- run(composedQuery.transactionally.withTransactionIsolation(Serializable), - retryCount = 50, - retryIfErrorContains = List(transactionSerializationError)) - } yield () + replaceSequentiallyAsTransaction(clearQuery, insertQueries) } def updateAllowedTeamsForFolder(folderId: ObjectId, allowedTeams: List[ObjectId]): Fox[Unit] = { @@ -228,12 +221,7 @@ class TeamDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext) val insertQueries = allowedTeams.map(teamId => q"""INSERT INTO webknossos.folder_allowedTeams(_folder, _team) VALUES($folderId, $teamId)""".asUpdate) - val composedQuery = DBIO.sequence(List(clearQuery) ++ insertQueries) - for { - _ <- run(composedQuery.transactionally.withTransactionIsolation(Serializable), - retryCount = 50, - retryIfErrorContains = List(transactionSerializationError)) - } yield () + replaceSequentiallyAsTransaction(clearQuery, insertQueries) } def removeTeamFromAllDatasetsAndFolders(teamId: ObjectId): Fox[Unit] = diff --git a/app/utils/sql/SimpleSQLDAO.scala b/app/utils/sql/SimpleSQLDAO.scala index ceb0d5326c4..f6c495c14b3 100644 --- a/app/utils/sql/SimpleSQLDAO.scala +++ b/app/utils/sql/SimpleSQLDAO.scala @@ -2,7 +2,10 @@ package utils.sql import com.scalableminds.util.tools.{Fox, FoxImplicits, TextUtils} import com.typesafe.scalalogging.LazyLogging -import slick.dbio.{DBIOAction, NoStream} +import slick.dbio.{DBIO, DBIOAction, Effect, NoStream} +import slick.jdbc.PostgresProfile.api._ +import slick.jdbc.TransactionIsolation.Serializable +import slick.sql.SqlAction import slick.util.{Dumpable, TreePrinter} import utils.sql.SqlInterpolation.sqlInterpolation @@ -65,4 +68,16 @@ class SimpleSQLDAO @Inject()(sqlClient: SqlClient)(implicit ec: ExecutionContext treePrinter.print(query, new PrintWriter(os)) new String(os.toByteArray, StandardCharsets.UTF_8) } + + def replaceSequentiallyAsTransaction(clearQuery: SqlAction[Int, NoStream, Effect], + insertQueries: Seq[SqlAction[Int, NoStream, Effect]]): Fox[Unit] = { + val composedQuery = DBIO.sequence(List(clearQuery) ++ insertQueries) + for { + _ <- run( + composedQuery.transactionally.withTransactionIsolation(Serializable), + retryCount = 50, + retryIfErrorContains = List(transactionSerializationError) + ) + } yield () + } } diff --git a/conf/evolutions/108-additional-coordinates.sql b/conf/evolutions/108-additional-coordinates.sql new file mode 100644 index 00000000000..f23b7fe59ab --- /dev/null +++ b/conf/evolutions/108-additional-coordinates.sql @@ -0,0 +1,17 @@ +START TRANSACTION; + +CREATE TABLE webknossos.dataSet_layer_additionalAxes( + _dataSet CHAR(24) NOT NULL, + layerName VARCHAR(256) NOT NULL, + name VARCHAR(256) NOT NULL, + lowerBound INT NOT NULL, + upperBound INT NOT NULL, + index INT NOT NULL +); + +ALTER TABLE webknossos.dataSet_layer_additionalAxes + ADD CONSTRAINT dataSet_ref FOREIGN KEY(_dataSet) REFERENCES webknossos.dataSets(_id) DEFERRABLE; + +UPDATE webknossos.releaseInformation SET schemaVersion = 108; + +COMMIT TRANSACTION; diff --git a/conf/evolutions/reversions/108-additional-coordinates.sql b/conf/evolutions/reversions/108-additional-coordinates.sql new file mode 100644 index 00000000000..83cc1133a50 --- /dev/null +++ b/conf/evolutions/reversions/108-additional-coordinates.sql @@ -0,0 +1,7 @@ +START TRANSACTION; + +DROP TABLE webknossos.dataSet_layer_additionalAxes; + +UPDATE webknossos.releaseInformation SET schemaVersion = 107; + +COMMIT TRANSACTION; diff --git a/conf/messages b/conf/messages index 8baf21ada53..5e2a3948fef 100644 --- a/conf/messages +++ b/conf/messages @@ -112,6 +112,7 @@ dataSet.upload.storageExceeded=Cannot upload dataset because the storage quota o dataSet.explore.failed.readFile=Failed to read remote file dataSet.explore.magDtypeMismatch=Element class must be the same for all mags of a layer. Got {0} dataSet.explore.autoAdd.failed=Failed to automatically import the explored dataset. +dataSet.additionalCoordinates.different=Additional coordinates differ in merged units. dataVault.insert.failed=Failed to store remote file system credential dataVault.setup.failed=Failed to set up remote file system @@ -157,6 +158,7 @@ nml.tree.elements.invalid=Invalid {0} in tree {1} nml.treegroup.id.invalid=Invalid tree group id {0} nml.segmentGroup.id.invalid=Invalid segment group id {0} nml.boundingbox.id.invalid=Invalid user bounding box id {0} +nml.additionalCoordinates.notUnique=Additional coordinates do not have unique names project.name.alreadyTaken=The name “{0}” is already being used for a different project. Please choose a different name. project.name.invalidChars=The project name contains invalid characters. Please use only letters and numbers. diff --git a/docs/sharing.md b/docs/sharing.md index 647e948d327..2a2173d7dcc 100644 --- a/docs/sharing.md +++ b/docs/sharing.md @@ -157,10 +157,13 @@ The information is JSON-encoded in the URL fragment and has the following format type MappingType = "JSON" | "HDF5"; type ViewMode = "orthogonal" | "oblique" | "flight" | "volume"; type Vector3 = [number, number, number]; + // For datasets with more than 3 dimensions + type AdditionalCoordinate = { name: string; value: number }; type BaseMeshUrlDescriptor = {| +segmentId: number, +seedPosition: Vector3, + +seedAdditionalCoordinates?: AdditionalCoordinate[]; |}; type AdHocMeshUrlDescriptor = {| ...BaseMeshUrlDescriptor, @@ -201,6 +204,7 @@ The information is JSON-encoded in the URL fragment and has the following format activeNode?: number, rotation?: Vector3, stateByLayer?: UrlStateByLayer, + additionalCoordinates?: AdditionalCoordinate[]; |}; ``` diff --git a/frontend/javascripts/libs/vector_input.tsx b/frontend/javascripts/libs/vector_input.tsx index b63ce4474d4..882555ab500 100644 --- a/frontend/javascripts/libs/vector_input.tsx +++ b/frontend/javascripts/libs/vector_input.tsx @@ -6,12 +6,16 @@ import InputComponent from "oxalis/view/components/input_component"; import * as Utils from "libs/utils"; import { InputProps } from "antd"; +const CHARACTER_WIDTH_PX = 8; + type BaseProps = Omit & { value: T | string; onChange: (value: T) => void; changeOnlyOnBlur?: boolean; allowDecimals?: boolean; autoSize?: boolean; + // Only used in ArbitraryVectorInput case + vectorLength?: number; }; type State = { isEditing: boolean; @@ -19,9 +23,8 @@ type State = { text: string; }; // Accepts both a string or a VectorX as input and always outputs a valid VectorX -class BaseVector extends React.PureComponent, State> { - // @ts-expect-error ts-migrate(2564) FIXME: Property 'defaultValue' has no initializer and is ... Remove this comment to see the full error message - defaultValue: T; +abstract class BaseVector extends React.PureComponent, State> { + abstract get defaultValue(): T; static defaultProps = { value: "", onChange: () => {}, @@ -119,35 +122,89 @@ class BaseVector extends React.PureComponent) => { + /* Increment/decrement current value when using arrow up/down */ + if (event.key !== "ArrowUp" && event.key !== "ArrowDown") { + return; + } + + event.preventDefault(); + const { selectionStart, value } = event.target as HTMLInputElement; + const vec = Utils.stringToNumberArray(value) as T; + + // Count commas before the selection to obtain the index of the current element + const vectorIndex = Array.from((value as string).slice(0, selectionStart || 0)).filter( + (el: string) => el === ",", + ).length; + if (event.key === "ArrowUp") { + vec[vectorIndex] += 1; + } else { + vec[vectorIndex] -= 1; + } + this.props.onChange(vec); + const text = vec.join(", "); + this.setState({ text }); + }; + render() { - const { style, autoSize, ...props } = _.omit(this.props, [ - "onChange", - "value", - "changeOnlyOnBlur", - "allowDecimals", - ]); + const { + style, + autoSize, + vectorLength: _vectorLength, + ...props + } = _.omit(this.props, ["onChange", "value", "changeOnlyOnBlur", "allowDecimals"]); + + const { addonBefore } = props; + const addonBeforeLength = + typeof addonBefore === "string" ? 20 + CHARACTER_WIDTH_PX * addonBefore.length : 0; return ( ); } } export class Vector3Input extends BaseVector { - defaultValue: Vector3 = [0, 0, 0]; + get defaultValue(): Vector3 { + return [0, 0, 0]; + } } export class Vector6Input extends BaseVector { - defaultValue: Vector6 = [0, 0, 0, 0, 0, 0]; + get defaultValue(): Vector6 { + return [0, 0, 0, 0, 0, 0]; + } } + +export class ArbitraryVectorInput extends BaseVector { + get defaultValue(): number[] { + return Array(this.props.vectorLength).fill(0); + } +} + type BoundingBoxInputProps = Omit & { value: ServerBoundingBoxTypeTuple; onChange: (arg0: ServerBoundingBoxTypeTuple) => void; diff --git a/frontend/javascripts/oxalis/api/api_latest.ts b/frontend/javascripts/oxalis/api/api_latest.ts index be543b89388..759a57a75e6 100644 --- a/frontend/javascripts/oxalis/api/api_latest.ts +++ b/frontend/javascripts/oxalis/api/api_latest.ts @@ -123,6 +123,7 @@ import type { Vector4, AnnotationTool, TypedArray, + BucketAddress, } from "oxalis/constants"; import Constants, { ControlModeEnum, @@ -160,6 +161,7 @@ import window, { location } from "libs/window"; import { coalesce } from "libs/utils"; import { setLayerTransformsAction } from "oxalis/model/actions/dataset_actions"; import { ResolutionInfo } from "oxalis/model/helpers/resolution_info"; +import { type AdditionalCoordinate } from "types/api_flow_types"; type TransformSpec = | { type: "scale"; args: [Vector3, Vector3] } @@ -1389,6 +1391,7 @@ class DataApi { layerName: string, position: Vector3, _zoomStep: number | null | undefined = null, + additionalCoordinates: AdditionalCoordinate[] | null = null, ): Promise { let zoomStep; @@ -1401,10 +1404,11 @@ class DataApi { } const cube = this.model.getCubeByLayerName(layerName); - const bucketAddress = cube.positionToZoomedAddress(position, zoomStep); + additionalCoordinates = additionalCoordinates || Store.getState().flycam.additionalCoordinates; + const bucketAddress = cube.positionToZoomedAddress(position, additionalCoordinates, zoomStep); await this.getLoadedBucket(layerName, bucketAddress); // Bucket has been loaded by now or was loaded already - const dataValue = cube.getDataValue(position, null, zoomStep); + const dataValue = cube.getDataValue(position, additionalCoordinates, null, zoomStep); return dataValue; } @@ -1423,7 +1427,7 @@ class DataApi { return this.model.getUltimatelyRenderedZoomStepAtPosition(layerName, position); } - async getLoadedBucket(layerName: string, bucketAddress: Vector4): Promise { + async getLoadedBucket(layerName: string, bucketAddress: BucketAddress): Promise { const cube = this.model.getCubeByLayerName(layerName); const bucket = await cube.getLoadedBucket(bucketAddress); return bucket; @@ -1449,6 +1453,7 @@ class DataApi { layerName: string, mag1Bbox: BoundingBoxType, _zoomStep: number | null | undefined = null, + additionalCoordinates: AdditionalCoordinate[] | null = null, ) { const layer = getLayerByName(Store.getState().dataset, layerName); const resolutionInfo = getResolutionInfo(layer.resolutions); @@ -1461,7 +1466,12 @@ class DataApi { } const resolutions = resolutionInfo.getDenseResolutions(); - const bucketAddresses = this.getBucketAddressesInCuboid(mag1Bbox, resolutions, zoomStep); + const bucketAddresses = this.getBucketAddressesInCuboid( + mag1Bbox, + resolutions, + zoomStep, + additionalCoordinates, + ); if (bucketAddresses.length > 15000) { console.warn( @@ -1480,6 +1490,7 @@ class DataApi { viewport: OrthoView, layerName: string, maybeResolutionIndex: number | null | undefined, + additionalCoordinates: AdditionalCoordinate[] | null, ) { const state = Store.getState(); const [curU, curV, curW] = dimensions.transDim( @@ -1523,6 +1534,7 @@ class DataApi { max, }, zoomStep, + additionalCoordinates, ); return cuboid; } @@ -1531,18 +1543,24 @@ class DataApi { bbox: BoundingBoxType, resolutions: Array, zoomStep: number, - ): Array { + additionalCoordinates: AdditionalCoordinate[] | null, + ): Array { const buckets = []; const bottomRight = bbox.max; - const minBucket = globalPositionToBucketPosition(bbox.min, resolutions, zoomStep); + const minBucket = globalPositionToBucketPosition( + bbox.min, + resolutions, + zoomStep, + additionalCoordinates, + ); - const topLeft = (bucketAddress: Vector4) => + const topLeft = (bucketAddress: BucketAddress) => bucketPositionToGlobalAddress(bucketAddress, new ResolutionInfo(resolutions)); - const nextBucketInDim = (bucket: Vector4, dim: 0 | 1 | 2) => { - const copy = bucket.slice(); + const nextBucketInDim = (bucket: BucketAddress, dim: 0 | 1 | 2) => { + const copy = bucket.slice() as BucketAddress; copy[dim]++; - return copy as any as Vector4; + return copy; }; let bucket = minBucket; @@ -1710,12 +1728,20 @@ class DataApi { * @example // Set the segmentation id for some voxels to 1337 * await api.data.labelVoxels([[1,1,1], [1,2,1], [2,1,1], [2,2,1]], 1337); */ - async labelVoxels(voxels: Array, label: number): Promise { + async labelVoxels( + voxels: Array, + label: number, + additionalCoordinates: AdditionalCoordinate[] | null = null, + ): Promise { assertVolume(Store.getState()); const segmentationLayer = this.model.getEnforcedSegmentationTracingLayer(); await Promise.all( voxels.map((voxel) => - segmentationLayer.cube._labelVoxelInAllResolutions_DEPRECATED(voxel, label), + segmentationLayer.cube._labelVoxelInAllResolutions_DEPRECATED( + voxel, + additionalCoordinates, + label, + ), ), ); segmentationLayer.cube.pushQueue.push(); @@ -1919,6 +1945,7 @@ class DataApi { segmentId: number, seedPosition: Vector3, layerName: string | null | undefined, + seedAdditionalCoordinates?: AdditionalCoordinate[], ) { const state = Store.getState(); const effectiveLayerName = getNameOfRequestedOrVisibleSegmentationLayer(state, layerName); @@ -1959,7 +1986,13 @@ class DataApi { } Store.dispatch( - loadPrecomputedMeshAction(segmentId, seedPosition, meshFileName, effectiveLayerName), + loadPrecomputedMeshAction( + segmentId, + seedPosition, + seedAdditionalCoordinates, + meshFileName, + effectiveLayerName, + ), ); } @@ -1971,8 +2004,12 @@ class DataApi { * const segmentId = await api.data.getDataValue("segmentation", currentPosition); * api.data.computeMeshOnDemand(segmentId, currentPosition); */ - computeMeshOnDemand(segmentId: number, seedPosition: Vector3) { - Store.dispatch(loadAdHocMeshAction(segmentId, seedPosition)); + computeMeshOnDemand( + segmentId: number, + seedPosition: Vector3, + seedAdditionalCoordinates?: AdditionalCoordinate[], + ) { + Store.dispatch(loadAdHocMeshAction(segmentId, seedPosition, seedAdditionalCoordinates)); } /** diff --git a/frontend/javascripts/oxalis/api/api_v2.ts b/frontend/javascripts/oxalis/api/api_v2.ts index 71bab6f53f3..097f26cb452 100644 --- a/frontend/javascripts/oxalis/api/api_v2.ts +++ b/frontend/javascripts/oxalis/api/api_v2.ts @@ -57,6 +57,7 @@ import { coalesce } from "libs/utils"; import { assertExists, assertSkeleton, assertVolume } from "./api_latest"; import { getLayerBoundingBox } from "oxalis/model/accessors/dataset_accessor"; +import { type AdditionalCoordinate } from "types/api_flow_types"; function makeTreeBackwardsCompatible(tree: TreeMap) { return update(tree, { @@ -593,7 +594,7 @@ class DataApi { async getDataValue(layerName: string, position: Vector3, zoomStep: number = 0): Promise { const cube = this.model.getCubeByLayerName(layerName); const pullQueue = this.model.getPullQueueByLayerName(layerName); - const bucketAddress = cube.positionToZoomedAddress(position, zoomStep); + const bucketAddress = cube.positionToZoomedAddress(position, null, zoomStep); const bucket = cube.getOrCreateBucket(bucketAddress); if (bucket.type === "null") return 0; let needsToAwaitBucket = false; @@ -616,7 +617,7 @@ class DataApi { } // Bucket has been loaded by now or was loaded already - return cube.getDataValue(position, null, zoomStep); + return cube.getDataValue(position, null, null, zoomStep); } /** @@ -644,12 +645,20 @@ class DataApi { * @example // Set the segmentation id for some voxels to 1337 * api.data.labelVoxels([[1,1,1], [1,2,1], [2,1,1], [2,2,1]], 1337); */ - async labelVoxels(voxels: Array, label: number): Promise { + async labelVoxels( + voxels: Array, + label: number, + additionalCoordinates: AdditionalCoordinate[] | null = null, + ): Promise { assertVolume(Store.getState()); const segmentationLayer = this.model.getEnforcedSegmentationTracingLayer(); await Promise.all( voxels.map((voxel) => - segmentationLayer.cube._labelVoxelInAllResolutions_DEPRECATED(voxel, label), + segmentationLayer.cube._labelVoxelInAllResolutions_DEPRECATED( + voxel, + additionalCoordinates, + label, + ), ), ); segmentationLayer.cube.pushQueue.push(); diff --git a/frontend/javascripts/oxalis/constants.ts b/frontend/javascripts/oxalis/constants.ts index c0b5f4392d3..08829347c21 100644 --- a/frontend/javascripts/oxalis/constants.ts +++ b/frontend/javascripts/oxalis/constants.ts @@ -1,3 +1,5 @@ +import { type AdditionalCoordinate } from "types/api_flow_types"; + export const ViewModeValues = ["orthogonal", "flight", "oblique"] as ViewMode[]; export const ViewModeValuesIndices = { @@ -12,6 +14,13 @@ export type Vector3 = [number, number, number]; export type Vector4 = [number, number, number, number]; export type Vector5 = [number, number, number, number, number]; export type Vector6 = [number, number, number, number, number, number]; + +// For 3D data BucketAddress = x, y, z, mag +// For higher dimensional data, BucketAddress = x, y, z, mag, [{name: "t", value: t}, ...] +export type BucketAddress = + | Vector4 + | [number, number, number, number, AdditionalCoordinate[] | null]; + export type Point2 = { x: number; y: number; @@ -263,11 +272,12 @@ export const Unicode = { // to a 2D slice of labeled voxels. These labeled voxels // are stored in a Uint8Array in a binary way (which cell // id the voxels should be changed to is not encoded). -export type LabeledVoxelsMap = Map; +export type LabeledVoxelsMap = Map; + // LabelMasksByBucketAndW is similar to LabeledVoxelsMap with the difference // that it can hold multiple slices per bucket (keyed by the W component, // e.g., z in XY viewport). -export type LabelMasksByBucketAndW = Map>; +export type LabelMasksByBucketAndW = Map>; export type ShowContextMenuFunction = ( arg0: number, arg1: number, diff --git a/frontend/javascripts/oxalis/controller/combinations/segmentation_handlers.ts b/frontend/javascripts/oxalis/controller/combinations/segmentation_handlers.ts index c6851b11ab4..9c0ce835173 100644 --- a/frontend/javascripts/oxalis/controller/combinations/segmentation_handlers.ts +++ b/frontend/javascripts/oxalis/controller/combinations/segmentation_handlers.ts @@ -79,8 +79,11 @@ export function handleClickSegment(clickPosition: Point2) { const state = Store.getState(); const globalPosition = calculateGlobalPos(state, clickPosition); const segmentId = getSegmentIdForPosition(globalPosition); + const { additionalCoordinates } = state.flycam; if (segmentId > 0) { - Store.dispatch(clickSegmentAction(segmentId, globalPosition)); + Store.dispatch( + clickSegmentAction(segmentId, globalPosition, additionalCoordinates || undefined), + ); } } diff --git a/frontend/javascripts/oxalis/controller/combinations/skeleton_handlers.ts b/frontend/javascripts/oxalis/controller/combinations/skeleton_handlers.ts index 62aea5d7d72..988b64a41de 100644 --- a/frontend/javascripts/oxalis/controller/combinations/skeleton_handlers.ts +++ b/frontend/javascripts/oxalis/controller/combinations/skeleton_handlers.ts @@ -277,6 +277,7 @@ function addNode( Store.dispatch( createNodeAction( position, + state.flycam.additionalCoordinates, rotation, OrthoViewToNumber[Store.getState().viewModeData.plane.activeViewport], // This is the magnification index at which the node was created. Since diff --git a/frontend/javascripts/oxalis/controller/combinations/tool_controls.ts b/frontend/javascripts/oxalis/controller/combinations/tool_controls.ts index 09f6f861a60..164c697a35d 100644 --- a/frontend/javascripts/oxalis/controller/combinations/tool_controls.ts +++ b/frontend/javascripts/oxalis/controller/combinations/tool_controls.ts @@ -802,14 +802,17 @@ export class ProofreadTool { return; } - const globalPosition = calculateGlobalPos(Store.getState(), pos); + const state = Store.getState(); + const globalPosition = calculateGlobalPos(state, pos); if (event.shiftKey) { Store.dispatch(proofreadMerge(globalPosition)); } else if (event.ctrlKey) { Store.dispatch(minCutAgglomerateWithPositionAction(globalPosition)); } else { - Store.dispatch(proofreadAtPosition(globalPosition)); + Store.dispatch( + proofreadAtPosition(globalPosition, state.flycam.additionalCoordinates || undefined), + ); VolumeHandlers.handlePickCell(pos); } } diff --git a/frontend/javascripts/oxalis/controller/combinations/volume_handlers.ts b/frontend/javascripts/oxalis/controller/combinations/volume_handlers.ts index 2751cbc966f..7f86115ca23 100644 --- a/frontend/javascripts/oxalis/controller/combinations/volume_handlers.ts +++ b/frontend/javascripts/oxalis/controller/combinations/volume_handlers.ts @@ -12,6 +12,7 @@ import { } from "oxalis/model/actions/volumetracing_actions"; import { Model, Store, api } from "oxalis/singletons"; import { updateUserSettingAction } from "oxalis/model/actions/settings_actions"; +import { type AdditionalCoordinate } from "types/api_flow_types"; export function handleDrawStart(pos: Point2, plane: OrthoView) { const state = Store.getState(); @@ -34,13 +35,15 @@ export function handleEndForDrawOrErase() { export function handlePickCell(pos: Point2) { const storeState = Store.getState(); const globalPos = calculateGlobalPos(storeState, pos); - return handlePickCellFromGlobalPosition(globalPos); + + return handlePickCellFromGlobalPosition(globalPos, storeState.flycam.additionalCoordinates || []); } export function getSegmentIdForPosition(globalPos: Vector3) { // This function will return the currently loaded segment ID for a given position. // If the corresponding bucket is not loaded at the moment, the return value will be 0. // See getSegmentIdForPositionAsync if the bucket loading should be awaited before returning the ID. const layer = Model.getVisibleSegmentationLayer(); + const { additionalCoordinates } = Store.getState().flycam; if (!layer) { return 0; @@ -52,13 +55,18 @@ export function getSegmentIdForPosition(globalPos: Vector3) { segmentationLayerName, globalPos, ); - return segmentationCube.getMappedDataValue(globalPos, renderedZoomStepForCameraPosition); + return segmentationCube.getMappedDataValue( + globalPos, + additionalCoordinates, + renderedZoomStepForCameraPosition, + ); } export async function getSegmentIdForPositionAsync(globalPos: Vector3) { // This function will return the segment ID for a given position, awaiting the loading // of the corresponding bucket. // See getSegmentIdForPosition if the bucket loading should not be awaited. const layer = Model.getVisibleSegmentationLayer(); + const { additionalCoordinates } = Store.getState().flycam; if (!layer) { return 0; @@ -71,14 +79,26 @@ export async function getSegmentIdForPositionAsync(globalPos: Vector3) { globalPos, ); // Make sure the corresponding bucket is loaded - await api.data.getDataValue(segmentationLayerName, globalPos, renderedZoomStepForCameraPosition); - return segmentationCube.getMappedDataValue(globalPos, renderedZoomStepForCameraPosition); + await api.data.getDataValue( + segmentationLayerName, + globalPos, + renderedZoomStepForCameraPosition, + additionalCoordinates, + ); + return segmentationCube.getMappedDataValue( + globalPos, + additionalCoordinates, + renderedZoomStepForCameraPosition, + ); } -export function handlePickCellFromGlobalPosition(globalPos: Vector3) { +export function handlePickCellFromGlobalPosition( + globalPos: Vector3, + additionalCoordinates: AdditionalCoordinate[], +) { const cellId = getSegmentIdForPosition(globalPos); if (cellId > 0) { - Store.dispatch(setActiveCellAction(cellId, globalPos)); + Store.dispatch(setActiveCellAction(cellId, globalPos, additionalCoordinates)); } } export function handleFloodFill(pos: Point2, plane: OrthoView) { diff --git a/frontend/javascripts/oxalis/controller/url_manager.ts b/frontend/javascripts/oxalis/controller/url_manager.ts index 974eaf28947..5dbd22e87f7 100644 --- a/frontend/javascripts/oxalis/controller/url_manager.ts +++ b/frontend/javascripts/oxalis/controller/url_manager.ts @@ -19,10 +19,14 @@ import messages from "messages"; import { validateUrlStateJSON } from "types/validation"; import { APIAnnotationType, APICompoundTypeEnum } from "types/api_flow_types"; import { coalesce } from "libs/utils"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + const MAX_UPDATE_INTERVAL = 1000; + type BaseMeshUrlDescriptor = { readonly segmentId: number; readonly seedPosition: Vector3; + readonly seedAdditionalCoordinates?: AdditionalCoordinate[]; }; type AdHocMeshUrlDescriptor = BaseMeshUrlDescriptor & { readonly isPrecomputed: false; @@ -82,6 +86,7 @@ export type UrlManagerState = { activeNode?: number; rotation?: Vector3; stateByLayer?: UrlStateByLayer; + additionalCoordinates?: AdditionalCoordinate[] | null; }; export type PartialUrlManagerState = Partial; @@ -291,6 +296,7 @@ class UrlManager { position, mode, zoomStep, + additionalCoordinates: state.flycam.additionalCoordinates, ...rotationOptional, ...activeNodeOptional, ...stateByLayerOptional, diff --git a/frontend/javascripts/oxalis/controller/viewmodes/arbitrary_controller.tsx b/frontend/javascripts/oxalis/controller/viewmodes/arbitrary_controller.tsx index 9cb4c2500e4..bde028f109c 100644 --- a/frontend/javascripts/oxalis/controller/viewmodes/arbitrary_controller.tsx +++ b/frontend/javascripts/oxalis/controller/viewmodes/arbitrary_controller.tsx @@ -360,7 +360,10 @@ class ArbitraryController extends React.PureComponent { const position = getPosition(Store.getState().flycam); const rotation = getRotation(Store.getState().flycam); - Store.dispatch(createNodeAction(position, rotation, constants.ARBITRARY_VIEW, 0)); + const additionalCoordinates = Store.getState().flycam.additionalCoordinates; + Store.dispatch( + createNodeAction(position, additionalCoordinates, rotation, constants.ARBITRARY_VIEW, 0), + ); } changeMoveValue(delta: number): void { diff --git a/frontend/javascripts/oxalis/controller/viewmodes/plane_controller.tsx b/frontend/javascripts/oxalis/controller/viewmodes/plane_controller.tsx index 79757dae3bc..09d51adf50e 100644 --- a/frontend/javascripts/oxalis/controller/viewmodes/plane_controller.tsx +++ b/frontend/javascripts/oxalis/controller/viewmodes/plane_controller.tsx @@ -471,6 +471,7 @@ class PlaneController extends React.PureComponent { const baseControls = { "ctrl + i": (event: React.KeyboardEvent) => { const segmentationLayer = Model.getVisibleSegmentationLayer(); + const { additionalCoordinates } = Store.getState().flycam; if (!segmentationLayer) { return; @@ -488,6 +489,7 @@ class PlaneController extends React.PureComponent { const mapping = event.altKey ? cube.getMapping() : null; const hoveredId = cube.getDataValue( globalMousePosition, + additionalCoordinates, mapping, getActiveMagIndexForLayer(Store.getState(), segmentationLayer.name), ); diff --git a/frontend/javascripts/oxalis/default_state.ts b/frontend/javascripts/oxalis/default_state.ts index d5747a131fe..08190ede669 100644 --- a/frontend/javascripts/oxalis/default_state.ts +++ b/frontend/javascripts/oxalis/default_state.ts @@ -157,6 +157,7 @@ const defaultState: OxalisState = { type: "readonly", version: 0, tracingId: "", + additionalAxes: [], }, volumes: [], mappings: [], @@ -193,6 +194,7 @@ const defaultState: OxalisState = { currentMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], spaceDirectionOrtho: [1, 1, 1], direction: [0, 0, 0], + additionalCoordinates: [], }, viewModeData: { plane: { diff --git a/frontend/javascripts/oxalis/geometries/materials/edge_shader.ts b/frontend/javascripts/oxalis/geometries/materials/edge_shader.ts index fe70c17b4f9..f2a19dd3872 100644 --- a/frontend/javascripts/oxalis/geometries/materials/edge_shader.ts +++ b/frontend/javascripts/oxalis/geometries/materials/edge_shader.ts @@ -1,20 +1,25 @@ import * as THREE from "three"; import { COLOR_TEXTURE_WIDTH_FIXED } from "oxalis/geometries/materials/node_shader"; import type { Uniforms } from "oxalis/geometries/materials/plane_material_factory"; +import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; +import shaderEditor from "oxalis/model/helpers/shader_editor"; +import { Store } from "oxalis/singletons"; +import _ from "lodash"; class EdgeShader { material: THREE.RawShaderMaterial; - // @ts-expect-error ts-migrate(2564) FIXME: Property 'uniforms' has no initializer and is not ... Remove this comment to see the full error message - uniforms: Uniforms; + uniforms: Uniforms = {}; constructor(treeColorTexture: THREE.DataTexture) { this.setupUniforms(treeColorTexture); this.material = new THREE.RawShaderMaterial({ - // @ts-expect-error ts-migrate(2565) FIXME: Property 'uniforms' is used before being assigned. uniforms: this.uniforms, vertexShader: this.getVertexShader(), fragmentShader: this.getFragmentShader(), + transparent: true, + glslVersion: THREE.GLSL3, }); + shaderEditor.addMaterial("edge", this.material); } setupUniforms(treeColorTexture: THREE.DataTexture): void { @@ -26,6 +31,23 @@ class EdgeShader { value: treeColorTexture, }, }; + const { additionalCoordinates } = Store.getState().flycam; + + _.each(additionalCoordinates, (_val, idx) => { + this.uniforms[`currentAdditionalCoord_${idx}`] = { + value: 0, + }; + }); + + listenToStoreProperty( + (storeState) => storeState.flycam.additionalCoordinates, + (additionalCoordinates) => { + _.each(additionalCoordinates, (coord, idx) => { + this.uniforms[`currentAdditionalCoord_${idx}`].value = coord.value; + }); + }, + true, + ); } getMaterial(): THREE.RawShaderMaterial { @@ -33,27 +55,45 @@ class EdgeShader { } getVertexShader(): string { - return ` + const { additionalCoordinates } = Store.getState().flycam; + + return _.template(` precision highp float; precision highp int; -varying vec3 color; +out vec3 color; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform float activeTreeId; - uniform sampler2D treeColors; -attribute vec3 position; -attribute float treeId; +<% _.each(additionalCoordinates || [], (_coord, idx) => { %> + uniform float currentAdditionalCoord_<%= idx %>; +<% }) %> + +in vec3 position; +in float treeId; + +<% _.each(additionalCoordinates || [], (_coord, idx) => { %> + in float additionalCoord_<%= idx %>; +<% }) %> + +out float alpha; void main() { + alpha = 1.0; + <% _.each(additionalCoordinates || [], (_coord, idx) => { %> + if (additionalCoord_<%= idx %> != currentAdditionalCoord_<%= idx %>) { + alpha = 0.; + } + <% }) %> + vec2 treeIdToTextureCoordinate = vec2(fract( treeId / ${COLOR_TEXTURE_WIDTH_FIXED}), treeId / (${COLOR_TEXTURE_WIDTH_FIXED} * ${COLOR_TEXTURE_WIDTH_FIXED} )); - bool isVisible = texture2D(treeColors, treeIdToTextureCoordinate).a == 1.0; + bool isVisible = texture(treeColors, treeIdToTextureCoordinate).a == 1.0; if (!isVisible) { gl_Position = vec4(-1.0, -1.0, -1.0, -1.0); @@ -61,19 +101,21 @@ void main() { } gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - color = texture2D(treeColors, treeIdToTextureCoordinate).rgb; -}`; + color = texture(treeColors, treeIdToTextureCoordinate).rgb; +}`)({ additionalCoordinates }); } getFragmentShader(): string { return ` precision highp float; -varying vec3 color; +out vec4 fragColor; +in vec3 color; +in float alpha; void main() { - gl_FragColor = vec4(color, 1.0); + fragColor = vec4(color, alpha); }`; } } diff --git a/frontend/javascripts/oxalis/geometries/materials/node_shader.ts b/frontend/javascripts/oxalis/geometries/materials/node_shader.ts index 8cad154e50a..23bc32c0a4a 100644 --- a/frontend/javascripts/oxalis/geometries/materials/node_shader.ts +++ b/frontend/javascripts/oxalis/geometries/materials/node_shader.ts @@ -4,8 +4,9 @@ import type { Uniforms } from "oxalis/geometries/materials/plane_material_factor import { getBaseVoxel } from "oxalis/model/scaleinfo"; import { getZoomValue } from "oxalis/model/accessors/flycam_accessor"; import { listenToStoreProperty } from "oxalis/model/helpers/listener_helpers"; -import Store from "oxalis/store"; +import { Store } from "oxalis/singletons"; import shaderEditor from "oxalis/model/helpers/shader_editor"; +import _ from "lodash"; import { formatNumberAsGLSLFloat } from "oxalis/shaders/utils.glsl"; export const NodeTypes = { INVALID: 0.0, @@ -17,24 +18,23 @@ export const COLOR_TEXTURE_WIDTH_FIXED = COLOR_TEXTURE_WIDTH.toFixed(1); class NodeShader { material: THREE.RawShaderMaterial; - // @ts-expect-error ts-migrate(2564) FIXME: Property 'uniforms' has no initializer and is not ... Remove this comment to see the full error message - uniforms: Uniforms; + uniforms: Uniforms = {}; constructor(treeColorTexture: THREE.DataTexture) { this.setupUniforms(treeColorTexture); this.material = new THREE.RawShaderMaterial({ - // @ts-expect-error ts-migrate(2565) FIXME: Property 'uniforms' is used before being assigned. uniforms: this.uniforms, vertexShader: this.getVertexShader(), fragmentShader: this.getFragmentShader(), transparent: true, glslVersion: THREE.GLSL3, }); - shaderEditor.addMaterial("nodeFragment", this.material); + shaderEditor.addMaterial("node", this.material); } setupUniforms(treeColorTexture: THREE.DataTexture): void { const state = Store.getState(); + const { additionalCoordinates } = state.flycam; this.uniforms = { planeZoomFactor: { // The flycam zoom is typically decomposed into an x- and y-factor @@ -77,6 +77,13 @@ class NodeShader { value: 0, }, }; + + _.each(additionalCoordinates, (_val, idx) => { + this.uniforms[`currentAdditionalCoord_${idx}`] = { + value: 0, + }; + }); + listenToStoreProperty( (_state) => _state.userConfiguration.highlightCommentedNodes, (highlightCommentedNodes) => { @@ -90,6 +97,15 @@ class NodeShader { }, true, ); + listenToStoreProperty( + (storeState) => storeState.flycam.additionalCoordinates, + (additionalCoordinates) => { + _.each(additionalCoordinates, (coord, idx) => { + this.uniforms[`currentAdditionalCoord_${idx}`].value = coord.value; + }); + }, + true, + ); } getMaterial(): THREE.RawShaderMaterial { @@ -97,7 +113,9 @@ class NodeShader { } getVertexShader(): string { - return ` + const { additionalCoordinates } = Store.getState().flycam; + + return _.template(` precision highp float; precision highp int; @@ -117,10 +135,20 @@ uniform int isTouch; // bool that is used during picking and indicates whether t uniform float highlightCommentedNodes; uniform float viewMode; +<% _.each(additionalCoordinates || [], (_coord, idx) => { %> + uniform float currentAdditionalCoord_<%= idx %>; +<% }) %> + uniform sampler2D treeColors; in float radius; in vec3 position; + +<% _.each(additionalCoordinates || [], (_coord, idx) => { %> + in float additionalCoord_<%= idx %>; +<% }) %> + + in float type; in float isCommented; // Since attributes are only supported in vertex shader, we pass the attribute into a @@ -157,6 +185,13 @@ vec3 shiftHue(vec3 color, float shiftValue) { } void main() { + <% _.each(additionalCoordinates || [], (_coord, idx) => { %> + if (additionalCoord_<%= idx %> != currentAdditionalCoord_<%= idx %>) { + return; + } + <% }) %> + + vec2 treeIdToTextureCoordinate = vec2(fract( treeId / ${COLOR_TEXTURE_WIDTH_FIXED}), treeId / (${COLOR_TEXTURE_WIDTH_FIXED} * ${COLOR_TEXTURE_WIDTH_FIXED} @@ -226,7 +261,7 @@ void main() { gl_PointSize *= 2.0; } -}`; +}`)({ additionalCoordinates }); } getFragmentShader(): string { diff --git a/frontend/javascripts/oxalis/geometries/skeleton.ts b/frontend/javascripts/oxalis/geometries/skeleton.ts index 6dfcdb925f5..b13fca24078 100644 --- a/frontend/javascripts/oxalis/geometries/skeleton.ts +++ b/frontend/javascripts/oxalis/geometries/skeleton.ts @@ -12,14 +12,19 @@ import NodeShader, { } from "oxalis/geometries/materials/node_shader"; import Store from "oxalis/throttled_store"; import * as Utils from "libs/utils"; +import { type AdditionalCoordinate } from "types/api_flow_types"; const MAX_CAPACITY = 1000; +type BufferGeometryWithBufferAttributes = THREE.BufferGeometry & { + attributes: Record; +}; + type BufferHelper = typeof NodeBufferHelperType | typeof EdgeBufferHelperType; type Buffer = { capacity: number; nextIndex: number; - geometry: THREE.BufferGeometry; + geometry: BufferGeometryWithBufferAttributes; mesh: THREE.Object3D; }; type BufferPosition = { @@ -31,12 +36,21 @@ type BufferCollection = { idToBufferPosition: Map; freeList: Array; helper: BufferHelper; - material: THREE.Material; + material: THREE.RawShaderMaterial; }; + type BufferOperation = (position: BufferPosition) => Array; const NodeBufferHelperType = { setAttributes(geometry: THREE.BufferGeometry, capacity: number): void { geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 3), 3)); + + const additionalCoordLength = (Store.getState().flycam.additionalCoordinates ?? []).length; + for (const idx of _.range(0, additionalCoordLength)) { + geometry.setAttribute( + `additionalCoord_${idx}`, + new THREE.BufferAttribute(new Float32Array(capacity), 1), + ); + } geometry.setAttribute("radius", new THREE.BufferAttribute(new Float32Array(capacity), 1)); geometry.setAttribute("type", new THREE.BufferAttribute(new Float32Array(capacity), 1)); geometry.setAttribute("isCommented", new THREE.BufferAttribute(new Float32Array(capacity), 1)); @@ -44,19 +58,32 @@ const NodeBufferHelperType = { geometry.setAttribute("treeId", new THREE.BufferAttribute(new Float32Array(capacity), 1)); }, - buildMesh(geometry: THREE.BufferGeometry, material: THREE.Material): THREE.Object3D { + buildMesh(geometry: THREE.BufferGeometry, material: THREE.RawShaderMaterial): THREE.Object3D { return new THREE.Points(geometry, material); }, supportsPicking: true, }; + const EdgeBufferHelperType = { setAttributes(geometry: THREE.BufferGeometry, capacity: number): void { - geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(capacity * 6), 3)); + geometry.setAttribute( + "position", + new THREE.BufferAttribute(new Float32Array(capacity * 2 * 3), 3), + ); + + const additionalCoordLength = (Store.getState().flycam.additionalCoordinates ?? []).length; + for (const idx of _.range(0, additionalCoordLength)) { + geometry.setAttribute( + `additionalCoord_${idx}`, + new THREE.BufferAttribute(new Float32Array(capacity * 2), 1), + ); + } + geometry.setAttribute("treeId", new THREE.BufferAttribute(new Float32Array(capacity * 2), 1)); }, - buildMesh(geometry: THREE.BufferGeometry, material: THREE.Material): THREE.Object3D { + buildMesh(geometry: THREE.BufferGeometry, material: THREE.RawShaderMaterial): THREE.Object3D { return new THREE.LineSegments(geometry, material); }, @@ -87,7 +114,7 @@ class Skeleton { stopStoreListening: () => void; constructor( - skeletonTracingSelectorFn: (arg0: OxalisState) => Maybe, + skeletonTracingSelectorFn: (state: OxalisState) => Maybe, supportsPicking: boolean, ) { this.supportsPicking = supportsPicking; @@ -167,7 +194,7 @@ class Skeleton { initializeBufferCollection( initialCapacity: number, - material: THREE.Material, + material: THREE.RawShaderMaterial, helper: BufferHelper, ): BufferCollection { const initialBuffer = this.initializeBuffer( @@ -184,8 +211,12 @@ class Skeleton { }; } - initializeBuffer(capacity: number, material: THREE.Material, helper: BufferHelper): Buffer { - const geometry = new THREE.BufferGeometry(); + initializeBuffer( + capacity: number, + material: THREE.RawShaderMaterial, + helper: BufferHelper, + ): Buffer { + const geometry = new THREE.BufferGeometry() as BufferGeometryWithBufferAttributes; helper.setAttributes(geometry, capacity); const mesh = helper.buildMesh(geometry, material); this.rootGroup.add(mesh); @@ -320,10 +351,10 @@ class Skeleton { } case "updateNode": { - const { treeId, id, radius, position } = update.value; + const { treeId, id, radius, position, additionalCoordinates } = update.value; this.updateNodeRadius(treeId, id, radius); const tree = skeletonTracing.trees[treeId]; - this.updateNodePosition(tree, id, position); + this.updateNodePosition(tree, id, position, additionalCoordinates); break; } @@ -402,17 +433,14 @@ class Skeleton { activeNodeId = activeNodeId == null ? -1 : activeNodeId; let { activeTreeId } = skeletonTracing; activeTreeId = activeTreeId == null ? -1 : activeTreeId; - // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniforms' does not exist on type 'Materi... Remove this comment to see the full error message const nodeUniforms = this.nodes.material.uniforms; nodeUniforms.planeZoomFactor.value = getZoomValue(state.flycam); nodeUniforms.overrideParticleSize.value = particleSize; nodeUniforms.overrideNodeRadius.value = overrideNodeRadius; nodeUniforms.activeTreeId.value = activeTreeId; nodeUniforms.activeNodeId.value = activeNodeId; - // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniforms' does not exist on type 'Materi... Remove this comment to see the full error message const edgeUniforms = this.edges.material.uniforms; edgeUniforms.activeTreeId.value = activeTreeId; - // @ts-expect-error ts-migrate(2339) FIXME: Property 'linewidth' does not exist on type 'Mater... Remove this comment to see the full error message this.edges.material.linewidth = state.userConfiguration.particleSize / 4; this.prevTracing = skeletonTracing; } @@ -428,17 +456,13 @@ class Skeleton { startPicking(isTouch: boolean): THREE.Object3D { this.pickingNode.matrixAutoUpdate = false; this.pickingNode.matrix.copy(this.rootGroup.matrixWorld); - // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniforms' does not exist on type 'Materi... Remove this comment to see the full error message this.nodes.material.uniforms.isTouch.value = isTouch ? 1 : 0; - // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniforms' does not exist on type 'Materi... Remove this comment to see the full error message this.nodes.material.uniforms.isPicking.value = 1; return this.pickingNode; } stopPicking(): void { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniforms' does not exist on type 'Materi... Remove this comment to see the full error message this.nodes.material.uniforms.isTouch.value = 0; - // @ts-expect-error ts-migrate(2339) FIXME: Property 'uniforms' does not exist on type 'Materi... Remove this comment to see the full error message this.nodes.material.uniforms.isPicking.value = 0; } @@ -487,9 +511,16 @@ class Skeleton { id, this.nodes, ({ buffer, index }: BufferPosition): Array => { - const { attributes } = buffer.geometry; - // @ts-expect-error ts-migrate(2551) FIXME: Property 'set' does not exist on type 'BufferAttri... Remove this comment to see the full error message + const attributes = buffer.geometry.attributes; attributes.position.set(node.position, index * 3); + + if (node.additionalCoordinates) { + for (const idx of _.range(0, node.additionalCoordinates.length)) { + const attributeAdditionalCoordinates = + buffer.geometry.attributes[`additionalCoord_${idx}`]; + attributeAdditionalCoordinates.set([node.additionalCoordinates[idx].value], index); + } + } // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'any[] | ArrayLike... Remove this comment to see the full error message attributes.radius.array[index] = node.radius; // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'any[] | ArrayLike... Remove this comment to see the full error message @@ -500,7 +531,6 @@ class Skeleton { attributes.nodeId.array[index] = node.id; // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'any[] | ArrayLike... Remove this comment to see the full error message attributes.treeId.array[index] = treeId; - // @ts-expect-error ts-migrate(2322) FIXME: Type '(BufferAttribute | InterleavedBufferAttribut... Remove this comment to see the full error message return _.values(attributes); }, ); @@ -512,8 +542,7 @@ class Skeleton { deleteNode(treeId: number, nodeId: number) { const id = this.combineIds(nodeId, treeId); this.delete(id, this.nodes, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const attribute: THREE.BufferAttribute = buffer.geometry.attributes.type; + const attribute = buffer.geometry.attributes.type; // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'ArrayLike' only p... Remove this comment to see the full error message attribute.array[index] = NodeTypes.INVALID; return [attribute]; @@ -526,8 +555,7 @@ class Skeleton { updateNodeRadius(treeId: number, nodeId: number, radius: number) { const id = this.combineIds(nodeId, treeId); this.update(id, this.nodes, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const attribute: THREE.BufferAttribute = buffer.geometry.attributes.radius; + const attribute = buffer.geometry.attributes.radius; // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'ArrayLike' only p... Remove this comment to see the full error message attribute.array[index] = radius; return [attribute]; @@ -537,13 +565,26 @@ class Skeleton { /** * Updates a node's position and that of its edges in a WebGL buffer. */ - updateNodePosition(tree: Tree, nodeId: number, position: Vector3) { + updateNodePosition( + tree: Tree, + nodeId: number, + position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null | undefined, + ) { const { treeId } = tree; const bufferNodeId = this.combineIds(nodeId, treeId); this.update(bufferNodeId, this.nodes, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const attribute: THREE.BufferAttribute = buffer.geometry.attributes.position; + const attribute = buffer.geometry.attributes.position; attribute.set(position, index * 3); + + if (additionalCoordinates) { + for (const idx of _.range(0, additionalCoordinates.length)) { + const attributeAdditionalCoordinates = + buffer.geometry.attributes[`additionalCoord_${idx}`]; + attributeAdditionalCoordinates.set([additionalCoordinates[idx].value], index); + } + } + return [attribute]; }); @@ -553,8 +594,7 @@ class Skeleton { const indexOffset = isIngoingEdge ? 3 : 0; const bufferEdgeId = this.combineIds(treeId, edge.source, edge.target); this.update(bufferEdgeId, this.edges, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const positionAttribute: THREE.BufferAttribute = buffer.geometry.attributes.position; + const positionAttribute = buffer.geometry.attributes.position; positionAttribute.set(position, index * 6 + indexOffset); return [positionAttribute]; }); @@ -576,8 +616,7 @@ class Skeleton { updateNodeType(treeId: number, nodeId: number, type: number) { const id = this.combineIds(nodeId, treeId); this.update(id, this.nodes, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const attribute: THREE.BufferAttribute = buffer.geometry.attributes.type; + const attribute = buffer.geometry.attributes.type; // @ts-expect-error ts-migrate(2542) FIXME: Index signature in type 'ArrayLike' only p... Remove this comment to see the full error message attribute.array[index] = type; return [attribute]; @@ -587,8 +626,7 @@ class Skeleton { updateIsCommented(treeId: number, nodeId: number, isCommented: boolean) { const id = this.combineIds(nodeId, treeId); this.update(id, this.nodes, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const attribute: THREE.BufferAttribute = buffer.geometry.attributes.isCommented; + const attribute = buffer.geometry.attributes.isCommented; // @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'number'. attribute.array[index] = isCommented; return [attribute]; @@ -601,14 +639,26 @@ class Skeleton { createEdge(treeId: number, source: Node, target: Node) { const id = this.combineIds(treeId, source.id, target.id); this.create(id, this.edges, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const positionAttribute: THREE.BufferAttribute = buffer.geometry.attributes.position; - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const treeIdAttribute: THREE.BufferAttribute = buffer.geometry.attributes.treeId; + const { attributes } = buffer.geometry; + const positionAttribute = attributes.position; + const treeIdAttribute = attributes.treeId; + positionAttribute.set(source.position, index * 6); positionAttribute.set(target.position, index * 6 + 3); treeIdAttribute.set([treeId, treeId], index * 2); - return [positionAttribute, treeIdAttribute]; + + const changedAttributes = []; + if (source.additionalCoordinates && target.additionalCoordinates) { + for (const idx of _.range(0, source.additionalCoordinates.length)) { + const additionalCoordAttribute = attributes[`additionalCoord_${idx}`]; + + additionalCoordAttribute.set([source.additionalCoordinates[idx].value], 2 * index); + additionalCoordAttribute.set([target.additionalCoordinates[idx].value], 2 * index + 1); + changedAttributes.push(additionalCoordAttribute); + } + } + + return [positionAttribute, treeIdAttribute, ...changedAttributes]; }); } @@ -619,8 +669,7 @@ class Skeleton { deleteEdge(treeId: number, sourceId: number, targetId: number) { const id = this.combineIds(treeId, sourceId, targetId); this.delete(id, this.edges, ({ buffer, index }) => { - // @ts-expect-error ts-migrate(2322) FIXME: Type 'BufferAttribute | InterleavedBufferAttribute... Remove this comment to see the full error message - const attribute: THREE.BufferAttribute = buffer.geometry.attributes.position; + const attribute = buffer.geometry.attributes.position; attribute.set([0, 0, 0, 0, 0, 0], index * 6); return [attribute]; }); diff --git a/frontend/javascripts/oxalis/merger_mode.ts b/frontend/javascripts/oxalis/merger_mode.ts index 3ed3d019c06..e091a555348 100644 --- a/frontend/javascripts/oxalis/merger_mode.ts +++ b/frontend/javascripts/oxalis/merger_mode.ts @@ -11,6 +11,7 @@ import messages from "messages"; import { UnregisterHandler } from "oxalis/api/api_latest"; import { Action } from "oxalis/model/actions/actions"; import { CreateNodeAction } from "./model/actions/skeletontracing_actions"; +import { type AdditionalCoordinate } from "types/api_flow_types"; type MergerModeState = { treeIdToRepresentativeSegmentId: Record; @@ -114,8 +115,13 @@ async function createNodeOverwrite( return; } - const { position } = action; - const segmentId = await api.data.getDataValue(segmentationLayerName, position); + const { position, additionalCoordinates } = action; + const segmentId = await api.data.getDataValue( + segmentationLayerName, + position, + null, + additionalCoordinates, + ); // If there is no segment id, the node was set outside of all segments. // Drop the node creation action in that case. @@ -138,6 +144,7 @@ async function onCreateNode( nodeId: number, treeId: number, position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, updateMapping: boolean = true, ) { const { idMapping, segmentationLayerName, nodeSegmentMap } = mergerModeState; @@ -146,7 +153,12 @@ async function onCreateNode( return; } - const segmentId = await api.data.getDataValue(segmentationLayerName, position); + const segmentId = await api.data.getDataValue( + segmentationLayerName, + position, + null, + additionalCoordinates, + ); // 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 @@ -212,7 +224,7 @@ async function onUpdateNode(mergerModeState: MergerModeState, node: NodeWithTree } if (segmentId != null && segmentId > 0) { - await onCreateNode(mergerModeState, id, treeId, position, false); + await onCreateNode(mergerModeState, id, treeId, position, 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]; @@ -229,7 +241,7 @@ function updateState(mergerModeState: MergerModeState, skeletonTracing: Skeleton switch (action.name) { case "createNode": { const { treeId, id: nodeId, position } = action.value; - onCreateNode(mergerModeState, nodeId, treeId, position); + onCreateNode(mergerModeState, nodeId, treeId, position, action.value.additionalCoordinates); break; } diff --git a/frontend/javascripts/oxalis/model.ts b/frontend/javascripts/oxalis/model.ts index eb80727e193..f3675aa5bdb 100644 --- a/frontend/javascripts/oxalis/model.ts +++ b/frontend/javascripts/oxalis/model.ts @@ -166,12 +166,18 @@ export class OxalisModel { position: Vector3 | null | undefined, ): number { const state = Store.getState(); + const { additionalCoordinates } = state.flycam; + const zoomStep = getActiveMagIndexForLayer(state, layerName); if (position == null) return zoomStep; const cube = this.getCubeByLayerName(layerName); // Depending on the zoom value, which magnifications are loaded and other settings, // the currently rendered zoom step has to be determined. - const renderedZoomStep = cube.getNextCurrentlyUsableZoomStepForPosition(position, zoomStep); + const renderedZoomStep = cube.getNextCurrentlyUsableZoomStepForPosition( + position, + additionalCoordinates, + zoomStep, + ); return renderedZoomStep; } @@ -180,12 +186,14 @@ export class OxalisModel { position: Vector3, ): Promise { const state = Store.getState(); + const { additionalCoordinates } = state.flycam; const zoomStep = getActiveMagIndexForLayer(state, layerName); const cube = this.getCubeByLayerName(layerName); // Depending on the zoom value, the available magnifications and other settings, // the ultimately rendered zoom step has to be determined. const renderedZoomStep = await cube.getNextUltimatelyUsableZoomStepForPosition( position, + additionalCoordinates, zoomStep, ); return renderedZoomStep; @@ -216,9 +224,9 @@ export class OxalisModel { globalMousePosition, ); - // @ts-expect-error ts-migrate(7006) FIXME: Parameter 'pos' implicitly has an 'any' type. - const getIdForPos = (pos, usableZoomStep) => { - const id = cube.getDataValue(pos, null, usableZoomStep); + const getIdForPos = (pos: Vector3, usableZoomStep: number) => { + const additionalCoordinates = Store.getState().flycam.additionalCoordinates; + const id = cube.getDataValue(pos, additionalCoordinates, null, usableZoomStep); return { id: cube.mapId(id), unmappedId: id, diff --git a/frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts b/frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts index d9aa208f394..be886044623 100644 --- a/frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts +++ b/frontend/javascripts/oxalis/model/accessors/dataset_accessor.ts @@ -1,6 +1,7 @@ import _ from "lodash"; import memoizeOne from "memoize-one"; import type { + AdditionalAxis, APIAllowedMode, APIDataLayer, APIDataset, @@ -620,6 +621,38 @@ function _getLayerNameToIsDisabled(datasetConfiguration: DatasetConfiguration) { export const getLayerNameToIsDisabled = memoizeOne(_getLayerNameToIsDisabled); +function _getUnifiedAdditionalAxes( + mutableDataset: APIDataset, +): Record> { + /* + * Merge additional coordinates from all layers. + */ + const unifiedAdditionalAxes: Record> = {}; + for (const layer of mutableDataset.dataSource.dataLayers) { + const { additionalAxes } = layer; + + for (const additionalCoordinate of additionalAxes || []) { + const { name, bounds } = additionalCoordinate; + if (additionalCoordinate.name in unifiedAdditionalAxes) { + const existingBounds = unifiedAdditionalAxes[name].bounds; + unifiedAdditionalAxes[name].bounds = [ + Math.min(bounds[0], existingBounds[0]), + Math.max(bounds[1], existingBounds[1]), + ]; + } else { + unifiedAdditionalAxes[name] = { + name, + bounds, + }; + } + } + } + + return unifiedAdditionalAxes; +} + +export const getUnifiedAdditionalCoordinates = memoizeOne(_getUnifiedAdditionalAxes); + export function is2dDataset(dataset: APIDataset): boolean { // An empty dataset (e.g., depth == 0), should not be considered as 2D. // This avoids that the empty dummy dataset is rendered with a 2D layout diff --git a/frontend/javascripts/oxalis/model/actions/annotation_actions.ts b/frontend/javascripts/oxalis/model/actions/annotation_actions.ts index 3756d3fcdc0..199c58fc1bd 100644 --- a/frontend/javascripts/oxalis/model/actions/annotation_actions.ts +++ b/frontend/javascripts/oxalis/model/actions/annotation_actions.ts @@ -17,6 +17,7 @@ import type { Vector3 } from "oxalis/constants"; import _ from "lodash"; import { Dispatch } from "redux"; import Deferred from "libs/async/deferred"; +import { type AdditionalCoordinate } from "types/api_flow_types"; type InitializeAnnotationAction = ReturnType; type SetAnnotationNameAction = ReturnType; @@ -43,7 +44,6 @@ export type StartedLoadingIsosurfaceAction = ReturnType; export type UpdateMeshFileListAction = ReturnType; export type UpdateCurrentMeshFileAction = ReturnType; -export type ImportIsosurfaceFromSTLAction = ReturnType; export type RemoveIsosurfaceAction = ReturnType; export type AddAdHocIsosurfaceAction = ReturnType; export type AddPrecomputedIsosurfaceAction = ReturnType; @@ -74,7 +74,6 @@ export type AnnotationActionTypes = | FinishedLoadingIsosurfaceAction | UpdateMeshFileListAction | UpdateCurrentMeshFileAction - | ImportIsosurfaceFromSTLAction | RemoveIsosurfaceAction | AddAdHocIsosurfaceAction | AddPrecomputedIsosurfaceAction @@ -275,13 +274,6 @@ export const updateCurrentMeshFileAction = ( meshFileName, } as const); -export const importIsosurfaceFromSTLAction = (layerName: string, buffer: ArrayBuffer) => - ({ - type: "IMPORT_ISOSURFACE_FROM_STL", - layerName, - buffer, - } as const); - export const removeIsosurfaceAction = (layerName: string, segmentId: number) => ({ type: "REMOVE_ISOSURFACE", @@ -293,6 +285,7 @@ export const addAdHocIsosurfaceAction = ( layerName: string, segmentId: number, seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, mappingName: string | null | undefined, mappingType: MappingType | null | undefined, ) => @@ -301,6 +294,7 @@ export const addAdHocIsosurfaceAction = ( layerName, segmentId, seedPosition, + seedAdditionalCoordinates, mappingName, mappingType, } as const); @@ -309,6 +303,7 @@ export const addPrecomputedIsosurfaceAction = ( layerName: string, segmentId: number, seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, meshFileName: string, ) => ({ @@ -316,6 +311,7 @@ export const addPrecomputedIsosurfaceAction = ( layerName, segmentId, seedPosition, + seedAdditionalCoordinates, meshFileName, } as const); diff --git a/frontend/javascripts/oxalis/model/actions/flycam_actions.ts b/frontend/javascripts/oxalis/model/actions/flycam_actions.ts index 99e55d9ebf8..317ef7cf0a7 100644 --- a/frontend/javascripts/oxalis/model/actions/flycam_actions.ts +++ b/frontend/javascripts/oxalis/model/actions/flycam_actions.ts @@ -1,9 +1,12 @@ import type { Vector3, OrthoView } from "oxalis/constants"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + type ZoomInAction = ReturnType; type ZoomOutAction = ReturnType; type ZoomByDeltaAction = ReturnType; type SetZoomStepAction = ReturnType; type SetPositionAction = ReturnType; +type SetAdditionalCoordinatesAction = ReturnType; type SetRotationAction = ReturnType; type SetDirectionAction = ReturnType; type MoveFlycamOrthoAction = ReturnType; @@ -20,6 +23,7 @@ export type FlycamAction = | ZoomByDeltaAction | SetZoomStepAction | SetPositionAction + | SetAdditionalCoordinatesAction | SetRotationAction | SetDirectionAction | MoveFlycamAction @@ -45,6 +49,7 @@ export const FlycamActions = [ "ROLL_FLYCAM", "PITCH_FLYCAM", "ROTATE_FLYCAM", + "SET_ADDITIONAL_COORDINATES", ]; export const zoomInAction = () => ({ @@ -75,6 +80,12 @@ export const setPositionAction = (position: Vector3, dimensionToSkip?: number | dimensionToSkip, } as const); +export const setAdditionalCoordinatesAction = (values: AdditionalCoordinate[] | null) => + ({ + type: "SET_ADDITIONAL_COORDINATES", + values, + } as const); + export const setRotationAction = (rotation: Vector3) => ({ type: "SET_ROTATION", diff --git a/frontend/javascripts/oxalis/model/actions/proofread_actions.ts b/frontend/javascripts/oxalis/model/actions/proofread_actions.ts index 110de202fa3..ce644483ffa 100644 --- a/frontend/javascripts/oxalis/model/actions/proofread_actions.ts +++ b/frontend/javascripts/oxalis/model/actions/proofread_actions.ts @@ -1,4 +1,5 @@ import { Vector3 } from "oxalis/constants"; +import { type AdditionalCoordinate } from "types/api_flow_types"; export type ProofreadAtPositionAction = ReturnType; export type ClearProofreadingByProductsAction = ReturnType; @@ -10,10 +11,14 @@ export type MinCutAgglomerateWithPositionAction = ReturnType< export type ProofreadAction = ProofreadAtPositionAction | ClearProofreadingByProductsAction; -export const proofreadAtPosition = (position: Vector3) => +export const proofreadAtPosition = ( + position: Vector3, + additionalCoordinates?: AdditionalCoordinate[], +) => ({ type: "PROOFREAD_AT_POSITION", position, + additionalCoordinates, } as const); export const clearProofreadingByProducts = () => diff --git a/frontend/javascripts/oxalis/model/actions/segmentation_actions.ts b/frontend/javascripts/oxalis/model/actions/segmentation_actions.ts index f4b8984e605..4d6780cbec2 100644 --- a/frontend/javascripts/oxalis/model/actions/segmentation_actions.ts +++ b/frontend/javascripts/oxalis/model/actions/segmentation_actions.ts @@ -1,5 +1,7 @@ import type { Vector3 } from "oxalis/constants"; import type { MappingType } from "oxalis/store"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + export type AdHocIsosurfaceInfo = { mappingName: string | null | undefined; mappingType: MappingType | null | undefined; @@ -14,6 +16,7 @@ export type SegmentationAction = LoadAdHocMeshAction | LoadPrecomputedMeshAction export const loadAdHocMeshAction = ( segmentId: number, seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, extraInfo?: AdHocIsosurfaceInfo, layerName?: string, ) => @@ -21,6 +24,7 @@ export const loadAdHocMeshAction = ( type: "LOAD_AD_HOC_MESH_ACTION", segmentId, seedPosition, + seedAdditionalCoordinates, extraInfo, layerName, } as const); @@ -28,6 +32,7 @@ export const loadAdHocMeshAction = ( export const loadPrecomputedMeshAction = ( segmentId: number, seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, meshFileName: string, layerName?: string, ) => @@ -35,6 +40,7 @@ export const loadPrecomputedMeshAction = ( type: "LOAD_PRECOMPUTED_MESH_ACTION", segmentId, seedPosition, + seedAdditionalCoordinates, meshFileName, layerName, } as const); diff --git a/frontend/javascripts/oxalis/model/actions/skeletontracing_actions.tsx b/frontend/javascripts/oxalis/model/actions/skeletontracing_actions.tsx index d0ccbc518e8..04fe56ff2c9 100644 --- a/frontend/javascripts/oxalis/model/actions/skeletontracing_actions.tsx +++ b/frontend/javascripts/oxalis/model/actions/skeletontracing_actions.tsx @@ -14,6 +14,7 @@ import messages from "messages"; import renderIndependently from "libs/render_independently"; import { AllUserBoundingBoxActions } from "oxalis/model/actions/annotation_actions"; import { batchActions } from "redux-batched-actions"; +import { type AdditionalCoordinate } from "types/api_flow_types"; export type InitializeSkeletonTracingAction = ReturnType; export type CreateNodeAction = ReturnType; @@ -161,6 +162,7 @@ export const initializeSkeletonTracingAction = (tracing: ServerSkeletonTracing) export const createNodeAction = ( position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, rotation: Vector3, viewport: number, resolution: number, @@ -171,6 +173,7 @@ export const createNodeAction = ( ({ type: "CREATE_NODE", position, + additionalCoordinates, rotation, viewport, resolution, diff --git a/frontend/javascripts/oxalis/model/actions/volumetracing_actions.ts b/frontend/javascripts/oxalis/model/actions/volumetracing_actions.ts index 2acc1d7bab9..b7d0d630e00 100644 --- a/frontend/javascripts/oxalis/model/actions/volumetracing_actions.ts +++ b/frontend/javascripts/oxalis/model/actions/volumetracing_actions.ts @@ -1,5 +1,5 @@ import type { ServerEditableMapping, ServerVolumeTracing } from "types/api_flow_types"; -import type { Vector2, Vector3, Vector4, OrthoView, ContourMode } from "oxalis/constants"; +import type { Vector2, Vector3, OrthoView, ContourMode, BucketAddress } from "oxalis/constants"; import type { BucketDataArray } from "oxalis/model/bucket_data_handling/bucket"; import type { Segment, SegmentGroup, SegmentMap } from "oxalis/store"; import Deferred from "libs/async/deferred"; @@ -7,6 +7,7 @@ import type { Dispatch } from "redux"; import { AllUserBoundingBoxActions } from "oxalis/model/actions/annotation_actions"; import { QuickSelectGeometry } from "oxalis/geometries/helper_geometries"; import { batchActions } from "redux-batched-actions"; +import { type AdditionalCoordinate } from "types/api_flow_types"; export type InitializeVolumeTracingAction = ReturnType; export type InitializeEditableMappingAction = ReturnType; @@ -170,18 +171,28 @@ export const finishEditingAction = () => type: "FINISH_EDITING", } as const); -export const setActiveCellAction = (segmentId: number, somePosition?: Vector3) => +export const setActiveCellAction = ( + segmentId: number, + somePosition?: Vector3, + someAdditionalCoordinates?: AdditionalCoordinate[], +) => ({ type: "SET_ACTIVE_CELL", segmentId, somePosition, + someAdditionalCoordinates, } as const); -export const clickSegmentAction = (segmentId: number, somePosition: Vector3) => +export const clickSegmentAction = ( + segmentId: number, + somePosition: Vector3, + someAdditionalCoordinates: AdditionalCoordinate[] | undefined, +) => ({ type: "CLICK_SEGMENT", segmentId, somePosition, + someAdditionalCoordinates, } as const); export const setSegmentsAction = (segments: SegmentMap, layerName: string) => @@ -271,7 +282,7 @@ export const setContourTracingModeAction = (mode: ContourMode) => } as const); export const addBucketToUndoAction = ( - zoomedBucketAddress: Vector4, + zoomedBucketAddress: BucketAddress, bucketData: BucketDataArray, maybeUnmergedBucketLoadedPromise: MaybeUnmergedBucketLoadedPromise, pendingOperations: Array<(arg0: BucketDataArray) => void>, diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket.ts index 327d0b49d3d..91090d06101 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket.ts @@ -7,7 +7,7 @@ import type { MaybeUnmergedBucketLoadedPromise } from "oxalis/model/actions/volu import { addBucketToUndoAction } from "oxalis/model/actions/volumetracing_actions"; import { bucketPositionToGlobalAddress } from "oxalis/model/helpers/position_converter"; import { castForArrayType, mod } from "libs/utils"; -import type { BoundingBoxType, Vector3, Vector4 } from "oxalis/constants"; +import type { BoundingBoxType, BucketAddress, Vector3 } from "oxalis/constants"; import Constants from "oxalis/constants"; import type DataCube from "oxalis/model/bucket_data_handling/data_cube"; import ErrorHandling from "libs/error_handling"; @@ -15,6 +15,8 @@ import Store from "oxalis/store"; import TemporalBucketManager from "oxalis/model/bucket_data_handling/temporal_bucket_manager"; import window from "libs/window"; import { getActiveMagIndexForLayer } from "../accessors/flycam_accessor"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + export const enum BucketStateEnum { UNREQUESTED = "UNREQUESTED", REQUESTED = "REQUESTED", @@ -49,6 +51,12 @@ const warnMergeWithoutPendingOperations = _.throttle(() => { ); }, WARNING_THROTTLE_THRESHOLD); +export function assertNonNullBucket(bucket: Bucket): asserts bucket is DataBucket { + if (bucket.type === "null") { + throw new Error("Unexpected null bucket."); + } +} + export class NullBucket { type: "null" = "null"; @@ -138,7 +146,7 @@ export class DataBucket { accessed: boolean; data: BucketDataArray | null | undefined; temporalBucketManager: TemporalBucketManager; - zoomedAddress: Vector4; + zoomedAddress: BucketAddress; cube: DataCube; _fallbackBucket: Bucket | null | undefined; throttledTriggerLabeled: () => void; @@ -152,7 +160,7 @@ export class DataBucket { constructor( elementClass: ElementClass, - zoomedAddress: Vector4, + zoomedAddress: BucketAddress, temporalBucketManager: TemporalBucketManager, cube: DataCube, ) { @@ -270,15 +278,20 @@ export class DataBucket { return [this.zoomedAddress[0], this.zoomedAddress[1], this.zoomedAddress[2]]; } + getAdditionalCoordinates(): AdditionalCoordinate[] | undefined | null { + return this.zoomedAddress[4]; + } + is3DVoxelInsideBucket = (voxel: Vector3, zoomStep: number) => { // Checks whether a given 3D voxel is outside of the bucket it refers to (i.e., a coordinate is negative // or greater than 32). If this is the case, the bucket address of the neighbor which contains the position // is also returned along with the adjusted voxel coordinate in that neighboring bucket. - const neighbourBucketAddress: Vector4 = [ + const neighbourBucketAddress: BucketAddress = [ this.zoomedAddress[0], this.zoomedAddress[1], this.zoomedAddress[2], zoomStep, + this.getAdditionalCoordinates() || [], ]; let isVoxelOutside = false; const adjustedVoxel: Vector3 = [voxel[0], voxel[1], voxel[2]]; @@ -290,7 +303,7 @@ export class DataBucket { const offset = Math.ceil(Math.abs(voxel[dimensionIndex]) / Constants.BUCKET_WIDTH); // If the voxel coordinate is below 0, sign is negative and will lower the neighbor // bucket address - neighbourBucketAddress[dimensionIndex] += sign * offset; + (neighbourBucketAddress[dimensionIndex] as number) += sign * offset; } adjustedVoxel[dimensionIndex] = mod(adjustedVoxel[dimensionIndex], Constants.BUCKET_WIDTH); diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/flight_bucket_picker.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/flight_bucket_picker.ts index c44dfe32749..7a9063dc086 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/flight_bucket_picker.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/flight_bucket_picker.ts @@ -6,16 +6,16 @@ import { globalPositionToBucketPositionFloat, zoomedAddressToAnotherZoomStep, } from "oxalis/model/helpers/position_converter"; -import type { Vector3, Vector4 } from "oxalis/constants"; +import type { BucketAddress, Vector3, Vector4 } from "oxalis/constants"; import constants from "oxalis/constants"; import { map3, map4, mod } from "libs/utils"; const aggregatePerDimension = ( aggregateFn: (...args: number[]) => number, - buckets: Vector4[], -): Vector3 => map3((dim) => aggregateFn(...buckets.map((pos) => pos[dim])), [0, 1, 2]); + buckets: BucketAddress[], +): Vector3 => map3((dim) => aggregateFn(...buckets.map((pos) => pos[dim] as number)), [0, 1, 2]); -const getBBox = (buckets: Vector4[]) => ({ +const getBBox = (buckets: BucketAddress[]) => ({ cornerMin: aggregatePerDimension(Math.min, buckets), cornerMax: aggregatePerDimension(Math.max, buckets), }); @@ -116,7 +116,10 @@ export default function determineBucketsForFlight( ]; const planePointsGlobal = planePoints.map((vec) => transformAndApplyMatrix(vec)); const planeBuckets = planePointsGlobal.map((position: Vector3) => - globalPositionToBucketPosition(position, resolutions, logZoomStep), + // null is passed as additionalCoordinates, since the bucket picker doesn't care about the + // additional coordinates. It simply sticks to 3D and the caller is responsible for augmenting + // potential other coordinates. + globalPositionToBucketPosition(position, resolutions, logZoomStep, null), ); const traverseFallbackBBox = (boundingBoxBuckets: { diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/oblique_bucket_picker.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/oblique_bucket_picker.ts index bc115d4fefc..2a710b98c33 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/oblique_bucket_picker.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_picker_strategies/oblique_bucket_picker.ts @@ -155,7 +155,10 @@ function addNecessaryBucketsToPriorityQueueOblique( traversedBuckets = makeBucketsUnique(traversedBuckets); const traversedBucketsVec4 = traversedBuckets.map((addr): Vector4 => [...addr, logZoomStep]); - const centerAddress = globalPositionToBucketPosition(position, resolutions, logZoomStep); + // null is passed as additionalCoordinates, since the bucket picker doesn't care about the + // additional coordinates. It simply sticks to 3D and the caller is responsible for augmenting + // potential other coordinates. + const centerAddress = globalPositionToBucketPosition(position, resolutions, logZoomStep, null); for (const bucketAddress of traversedBucketsVec4) { const bucketVector3 = bucketAddress.slice(0, 3) as any as Vector3; diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_traversals.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_traversals.ts index 6410c9b693e..ed90de68a07 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_traversals.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/bucket_traversals.ts @@ -22,8 +22,8 @@ export default function traverse( const u = startPosition; const v = V3.sub(endPosition, startPosition); // The initialization phase begins by identifying the voxel in which the ray origin, → u, is found. - const uBucket = globalPositionToBucketPosition(startPosition, resolutions, zoomStep); - const lastBucket = globalPositionToBucketPosition(endPosition, resolutions, zoomStep); + const uBucket = globalPositionToBucketPosition(startPosition, resolutions, zoomStep, null); + const lastBucket = globalPositionToBucketPosition(endPosition, resolutions, zoomStep, null); // The integer variables X and Y are initialized to the starting voxel coordinates. let [X, Y, Z] = uBucket; const voxelSize = getBucketExtent(resolutions[zoomStep]); @@ -42,7 +42,7 @@ export default function traverse( ]; const intersectedBuckets: Vector3[] = [[X, Y, Z]]; - const behindLastBucket = (dim: number, pos: number) => { + const behindLastBucket = (dim: 0 | 1 | 2, pos: number) => { if (step[dim] < 0) { return pos < lastBucket[dim]; } else if (step[dim] > 0) { diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/cuckoo_table_vec5.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/cuckoo_table_vec5.ts index 40fb357adff..15518d1adfb 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/cuckoo_table_vec5.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/cuckoo_table_vec5.ts @@ -20,7 +20,7 @@ type Entry = [Key, Value]; requestedMagIdxAndLayerIdxAndBucketAddress ] From the above definition, the following limits follow: - - [x, y, z] is constraint to be smaller than ~4.29 billion + - x, y and z are constrained to be smaller than ~4.29 billion each - 32 different mags are supported per layer - 64 layers are supported - ~2 million different bucket can be addressed on the GPU. diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/data_cube.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/data_cube.ts index 23f63a30d81..32dfc98b292 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/data_cube.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/data_cube.ts @@ -1,7 +1,7 @@ import _ from "lodash"; import type { Bucket, BucketDataArray } from "oxalis/model/bucket_data_handling/bucket"; import { DataBucket, NULL_BUCKET, NullBucket } from "oxalis/model/bucket_data_handling/bucket"; -import type { ElementClass } from "types/api_flow_types"; +import type { AdditionalAxis, ElementClass } from "types/api_flow_types"; import type { ProgressCallback } from "libs/progress_callback"; import { V3 } from "libs/mjs"; import { VoxelNeighborQueue2D, VoxelNeighborQueue3D } from "oxalis/model/volumetracing/volumelayer"; @@ -20,9 +20,15 @@ import type { Mapping } from "oxalis/store"; import Store from "oxalis/store"; import TemporalBucketManager from "oxalis/model/bucket_data_handling/temporal_bucket_manager"; import Toast from "libs/toast"; -import type { Vector3, Vector4, BoundingBoxType, LabelMasksByBucketAndW } from "oxalis/constants"; +import type { + Vector3, + BoundingBoxType, + LabelMasksByBucketAndW, + BucketAddress, +} from "oxalis/constants"; import constants, { MappingStatusEnum } from "oxalis/constants"; import { ResolutionInfo } from "../helpers/resolution_info"; +import { type AdditionalCoordinate } from "types/api_flow_types"; const warnAboutTooManyAllocations = _.once(() => { const msg = @@ -59,8 +65,9 @@ class DataCube { BUCKET_COUNT_SOFT_LIMIT = constants.MAXIMUM_BUCKET_COUNT_PER_LAYER; buckets: Array; bucketIterator: number = 0; - cubes: Array; + private cubes: Record; boundingBox: BoundingBox; + additionalAxes: Record; // @ts-expect-error ts-migrate(2564) FIXME: Property 'pullQueue' has no initializer and is not... Remove this comment to see the full error message pullQueue: PullQueue; // @ts-expect-error ts-migrate(2564) FIXME: Property 'pushQueue' has no initializer and is not... Remove this comment to see the full error message @@ -90,6 +97,7 @@ class DataCube { // access-queue and is least recently used. It is then removed from the cube. constructor( layerBBox: BoundingBox, + additionalAxes: AdditionalAxis[], resolutionInfo: ResolutionInfo, elementClass: ElementClass, isSegmentation: boolean, @@ -99,24 +107,10 @@ class DataCube { this.isSegmentation = isSegmentation; this.resolutionInfo = resolutionInfo; this.layerName = layerName; + this.additionalAxes = _.keyBy(additionalAxes, "name"); - this.cubes = []; + this.cubes = {}; this.buckets = []; - // Initializing the cube-arrays with boundaries - const cubeBoundary = [ - Math.ceil(layerBBox.max[0] / constants.BUCKET_WIDTH), - Math.ceil(layerBBox.max[1] / constants.BUCKET_WIDTH), - Math.ceil(layerBBox.max[2] / constants.BUCKET_WIDTH), - ]; - - for (const [resolutionIndex, resolution] of resolutionInfo.getResolutionsWithIndices()) { - const zoomedCubeBoundary: Vector3 = [ - Math.ceil(cubeBoundary[0] / resolution[0]) + 1, - Math.ceil(cubeBoundary[1] / resolution[1]) + 1, - Math.ceil(cubeBoundary[2] / resolution[2]) + 1, - ]; - this.cubes[resolutionIndex] = new CubeEntry(zoomedCubeBoundary); - } const shouldBeRestrictedByTracingBoundingBox = () => { const { task } = Store.getState(); @@ -191,31 +185,73 @@ class DataCube { return mappedId != null ? mappedId : idToMap; } - isWithinBounds([x, y, z, zoomStep]: Vector4): boolean { - if (this.cubes[zoomStep] == null) { + private getCubeKey(zoomStep: number, allCoords: AdditionalCoordinate[] | undefined | null) { + const relevantCoords = (allCoords ?? []).filter( + (coord) => this.additionalAxes[coord.name] != null, + ); + return [zoomStep, ...relevantCoords.map((el) => el.value)].join("-"); + } + + isWithinBounds([x, y, z, zoomStep, coords]: BucketAddress): boolean { + const cube = this.getOrCreateCubeEntry(zoomStep, coords); + if (cube == null) { return false; } return this.boundingBox.containsBucket([x, y, z, zoomStep], this.resolutionInfo); } - getBucketIndex([x, y, z, zoomStep]: Vector4): number | null | undefined { + getBucketIndexAndCube([x, y, z, zoomStep, coords]: BucketAddress): [ + number | null | undefined, + CubeEntry | null, + ] { // Removed for performance reasons // ErrorHandling.assert(this.isWithinBounds([x, y, z, zoomStep])); - const cube = this.cubes[zoomStep]; + const cube = this.getOrCreateCubeEntry(zoomStep, coords); if (cube != null) { const { boundary } = cube; - return x * boundary[2] * boundary[1] + y * boundary[2] + z; + const index = x * boundary[2] * boundary[1] + y * boundary[2] + z; + return [index, cube]; } - return null; + return [null, null]; + } + + getOrCreateCubeEntry( + zoomStep: number, + coords: AdditionalCoordinate[] | null | undefined, + ): CubeEntry | null { + const cubeKey = this.getCubeKey(zoomStep, coords); + if (this.cubes[cubeKey] == null) { + const resolution = this.resolutionInfo.getResolutionByIndex(zoomStep); + if (resolution == null) { + return null; + } + + for (const coord of coords || []) { + if (coord.name in this.additionalAxes) { + const { bounds } = this.additionalAxes[coord.name]; + if (coord.value < bounds[0] || coord.value >= bounds[1]) { + return null; + } + } + } + + const zoomedCubeBoundary: Vector3 = [ + Math.ceil(this.boundingBox.max[0] / (constants.BUCKET_WIDTH * resolution[0])) + 1, + Math.ceil(this.boundingBox.max[1] / (constants.BUCKET_WIDTH * resolution[1])) + 1, + Math.ceil(this.boundingBox.max[2] / (constants.BUCKET_WIDTH * resolution[2])) + 1, + ]; + this.cubes[cubeKey] = new CubeEntry(zoomedCubeBoundary); + } + return this.cubes[cubeKey]; } // Either returns the existing bucket or creates a new one. Only returns // NULL_BUCKET if the bucket cannot possibly exist, e.g. because it is // outside the dataset's bounding box. - getOrCreateBucket(address: Vector4): Bucket { + getOrCreateBucket(address: BucketAddress): Bucket { if (!this.isWithinBounds(address)) { return this.getNullBucket(); } @@ -230,13 +266,12 @@ class DataCube { } // Returns the Bucket object if it exists, or NULL_BUCKET otherwise. - getBucket(address: Vector4, skipBoundsCheck: boolean = false): Bucket { + getBucket(address: BucketAddress, skipBoundsCheck: boolean = false): Bucket { if (!skipBoundsCheck && !this.isWithinBounds(address)) { return this.getNullBucket(); } - const bucketIndex = this.getBucketIndex(address); - const cube = this.cubes[address[3]]; + const [bucketIndex, cube] = this.getBucketIndexAndCube(address); if (bucketIndex != null && cube != null) { const bucket = cube.data.get(bucketIndex); @@ -249,11 +284,10 @@ class DataCube { return this.getNullBucket(); } - createBucket(address: Vector4): Bucket { + createBucket(address: BucketAddress): Bucket { const bucket = new DataBucket(this.elementClass, address, this.temporalBucketManager, this); this.addBucketToGarbageCollection(bucket); - const bucketIndex = this.getBucketIndex(address); - const cube = this.cubes[address[3]]; + const [bucketIndex, cube] = this.getBucketIndexAndCube(address); if (bucketIndex != null && cube != null) { cube.data.set(bucketIndex, bucket); @@ -341,8 +375,7 @@ class DataCube { collectBucket(bucket: DataBucket): void { const address = bucket.zoomedAddress; - const bucketIndex = this.getBucketIndex(address); - const cube = this.cubes[address[3]]; + const [bucketIndex, cube] = this.getBucketIndexAndCube(address); if (bucketIndex != null && cube != null) { bucket.destroy(); @@ -352,6 +385,7 @@ class DataCube { async _labelVoxelInAllResolutions_DEPRECATED( voxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, label: number, activeSegmentId?: number | null | undefined, ): Promise { @@ -362,7 +396,13 @@ class DataCube { for (const [resolutionIndex] of this.resolutionInfo.getResolutionsWithIndices()) { promises.push( - this._labelVoxelInResolution_DEPRECATED(voxel, label, resolutionIndex, activeSegmentId), + this._labelVoxelInResolution_DEPRECATED( + voxel, + additionalCoordinates, + label, + resolutionIndex, + activeSegmentId, + ), ); } @@ -372,6 +412,7 @@ class DataCube { async _labelVoxelInResolution_DEPRECATED( voxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, label: number, zoomStep: number, activeSegmentId: number | null | undefined, @@ -379,7 +420,7 @@ class DataCube { let voxelInCube = this.boundingBox.containsPoint(voxel); if (voxelInCube) { - const address = this.positionToZoomedAddress(voxel, zoomStep); + const address = this.positionToZoomedAddress(voxel, additionalCoordinates, zoomStep); const bucket = this.getOrCreateBucket(address); if (bucket instanceof DataBucket) { @@ -387,7 +428,7 @@ class DataCube { let shouldUpdateVoxel = true; if (activeSegmentId != null) { - const voxelValue = this.getMappedDataValue(voxel, zoomStep); + const voxelValue = this.getMappedDataValue(voxel, additionalCoordinates, zoomStep); shouldUpdateVoxel = activeSegmentId === voxelValue; } @@ -404,6 +445,7 @@ class DataCube { async floodFill( globalSeedVoxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, segmentIdNumber: number, dimensionIndices: DimensionMap, floodfillBoundingBox: BoundingBoxType, @@ -430,7 +472,11 @@ class DataCube { Dimensions.transDimWithIndices(voxel, dimensionIndices); const bucketsWithLabeledVoxelsMap: LabelMasksByBucketAndW = new Map(); - const seedBucketAddress = this.positionToZoomedAddress(globalSeedVoxel, zoomStep); + const seedBucketAddress = this.positionToZoomedAddress( + globalSeedVoxel, + additionalCoordinates, + zoomStep, + ); const seedBucket = this.getOrCreateBucket(seedBucketAddress); let coveredBBoxMin: Vector3 = [ Number.POSITIVE_INFINITY, @@ -665,7 +711,7 @@ class DataCube { } setBucketData( - zoomedAddress: Vector4, + zoomedAddress: BucketAddress, data: BucketDataArray, newPendingOperations: Array<(arg0: BucketDataArray) => void>, ) { @@ -684,18 +730,27 @@ class DataCube { async isZoomStepUltimatelyRenderableForVoxel( voxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, zoomStep: number = 0, ): Promise { // Make sure the respective bucket is loaded before checking whether the zoomStep // is currently renderable for this voxel. - await this.getLoadedBucket(this.positionToZoomedAddress(voxel, zoomStep)); - return this.isZoomStepCurrentlyRenderableForVoxel(voxel, zoomStep); + await this.getLoadedBucket( + this.positionToZoomedAddress(voxel, additionalCoordinates, zoomStep), + ); + return this.isZoomStepCurrentlyRenderableForVoxel(voxel, additionalCoordinates, zoomStep); } - isZoomStepCurrentlyRenderableForVoxel(voxel: Vector3, zoomStep: number = 0): boolean { + isZoomStepCurrentlyRenderableForVoxel( + voxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, + zoomStep: number = 0, + ): boolean { // When this method returns false, this means that the next resolution (if it exists) // needs to be examined for rendering. - const bucket = this.getBucket(this.positionToZoomedAddress(voxel, zoomStep)); + const bucket = this.getBucket( + this.positionToZoomedAddress(voxel, additionalCoordinates, zoomStep), + ); const { renderMissingDataBlack } = Store.getState().datasetConfiguration; if (!(bucket instanceof DataBucket)) { @@ -724,14 +779,18 @@ class DataCube { return false; } - getNextCurrentlyUsableZoomStepForPosition(position: Vector3, zoomStep: number): number { + getNextCurrentlyUsableZoomStepForPosition( + position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, + zoomStep: number, + ): number { const resolutions = this.resolutionInfo.getDenseResolutions(); let usableZoomStep = zoomStep; while ( position && usableZoomStep < resolutions.length - 1 && - !this.isZoomStepCurrentlyRenderableForVoxel(position, usableZoomStep) + !this.isZoomStepCurrentlyRenderableForVoxel(position, additionalCoordinates, usableZoomStep) ) { usableZoomStep++; } @@ -741,6 +800,7 @@ class DataCube { async getNextUltimatelyUsableZoomStepForPosition( position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, zoomStep: number, ): Promise { const resolutions = this.resolutionInfo.getDenseResolutions(); @@ -749,7 +809,11 @@ class DataCube { while ( position && usableZoomStep < resolutions.length - 1 && // eslint-disable-next-line no-await-in-loop - !(await this.isZoomStepUltimatelyRenderableForVoxel(position, usableZoomStep)) + !(await this.isZoomStepUltimatelyRenderableForVoxel( + position, + additionalCoordinates, + usableZoomStep, + )) ) { usableZoomStep++; } @@ -757,12 +821,19 @@ class DataCube { return usableZoomStep; } - getDataValue(voxel: Vector3, mapping: Mapping | null | undefined, zoomStep: number = 0): number { + getDataValue( + voxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, + mapping: Mapping | null | undefined, + zoomStep: number = 0, + ): number { if (!this.resolutionInfo.hasIndex(zoomStep)) { return 0; } - const bucket = this.getBucket(this.positionToZoomedAddress(voxel, zoomStep)); + const bucket = this.getBucket( + this.positionToZoomedAddress(voxel, additionalCoordinates, zoomStep), + ); const voxelIndex = this.getVoxelIndex(voxel, zoomStep); if (bucket.hasData()) { @@ -783,8 +854,17 @@ class DataCube { return 0; } - getMappedDataValue(voxel: Vector3, zoomStep: number = 0): number { - return this.getDataValue(voxel, this.isMappingEnabled() ? this.getMapping() : null, zoomStep); + getMappedDataValue( + voxel: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, + zoomStep: number = 0, + ): number { + return this.getDataValue( + voxel, + additionalCoordinates, + this.isMappingEnabled() ? this.getMapping() : null, + zoomStep, + ); } getVoxelIndexByVoxelOffset([x, y, z]: Vector3 | Float32Array): number { @@ -813,20 +893,21 @@ class DataCube { return this.getVoxelIndexByVoxelOffset(voxelOffset); } - positionToZoomedAddress(position: Vector3, zoomStep: number = 0): Vector4 { + positionToZoomedAddress( + position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, + zoomStep: number = 0, + ): BucketAddress { // return the bucket a given voxel lies in return globalPositionToBucketPosition( position, this.resolutionInfo.getDenseResolutions(), zoomStep, + additionalCoordinates, ); } - positionToBaseAddress(position: Vector3): Vector4 { - return this.positionToZoomedAddress(position, 0); - } - - async getLoadedBucket(bucketAddress: Vector4) { + async getLoadedBucket(bucketAddress: BucketAddress) { const bucket = this.getOrCreateBucket(bucketAddress); if (bucket.type !== "null") { diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/layer_rendering_manager.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/layer_rendering_manager.ts index 89102666e21..8ed99920766 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/layer_rendering_manager.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/layer_rendering_manager.ts @@ -21,7 +21,7 @@ import type PullQueue from "oxalis/model/bucket_data_handling/pullqueue"; import Store, { PlaneRects, SegmentMap } from "oxalis/store"; import TextureBucketManager from "oxalis/model/bucket_data_handling/texture_bucket_manager"; import UpdatableTexture from "libs/UpdatableTexture"; -import type { ViewMode, Vector3, Vector4 } from "oxalis/constants"; +import type { ViewMode, Vector3, Vector4, BucketAddress } from "oxalis/constants"; import shaderEditor from "oxalis/model/helpers/shader_editor"; import DiffableMap from "libs/diffable_map"; import { CuckooTable } from "./cuckoo_table"; @@ -30,6 +30,7 @@ import { cachedDiffSegmentLists } from "../sagas/volumetracing_saga"; import { getSegmentsForLayer } from "../accessors/volumetracing_accessor"; import { getViewportRects } from "../accessors/view_mode_accessor"; import { CuckooTableVec5 } from "./cuckoo_table_vec5"; +import { type AdditionalCoordinate } from "types/api_flow_types"; import app from "app"; const CUSTOM_COLORS_TEXTURE_WIDTH = 512; @@ -53,6 +54,7 @@ function consumeBucketsFromArrayBuffer( buffer: ArrayBuffer, cube: DataCube, capacity: number, + additionalCoordinates: AdditionalCoordinate[] | null, ): Array<{ priority: number; bucket: DataBucket; @@ -70,14 +72,14 @@ function consumeBucketsFromArrayBuffer( break; } - const bucketAddress = [ + const bucketAddress: BucketAddress = [ uint32Array[currentBufferIndex], uint32Array[currentBufferIndex + 1], uint32Array[currentBufferIndex + 2], uint32Array[currentBufferIndex + 3], + additionalCoordinates ?? [], ]; const priority = uint32Array[currentBufferIndex + 4]; - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message const bucket = cube.getOrCreateBucket(bucketAddress); if (bucket.type !== "null") { @@ -123,6 +125,7 @@ export default class LayerRenderingManager { needsRefresh: boolean = false; currentBucketPickerTick: number = 0; latestTaskExecutor: LatestTaskExecutor = new LatestTaskExecutor(); + additionalCoordinates: AdditionalCoordinate[] | null = null; cuckooTable: CuckooTable | undefined; storePropertyUnsubscribers: Array<() => void> = []; @@ -207,7 +210,8 @@ export default class LayerRenderingManager { const { viewMode } = state.temporaryConfiguration; const { sphericalCapRadius } = state.userConfiguration; const isVisible = isLayerVisible(dataset, this.name, datasetConfiguration, viewMode); - const rects = getViewportRects(Store.getState()); + const rects = getViewportRects(state); + const additionalCoordinates = state.flycam.additionalCoordinates; if ( !_.isEqual(this.lastZoomedMatrix, matrix) || @@ -215,6 +219,7 @@ export default class LayerRenderingManager { sphericalCapRadius !== this.lastSphericalCapRadius || isVisible !== this.lastIsVisible || rects !== this.lastRects || + !_.isEqual(additionalCoordinates, this.additionalCoordinates) || this.needsRefresh ) { this.lastZoomedMatrix = matrix; @@ -224,6 +229,7 @@ export default class LayerRenderingManager { this.lastRects = rects; this.needsRefresh = false; this.currentBucketPickerTick++; + this.additionalCoordinates = additionalCoordinates; this.pullQueue.clear(); let pickingPromise: Promise = Promise.resolve(dummyBuffer); @@ -249,6 +255,7 @@ export default class LayerRenderingManager { buffer, this.cube, this.textureBucketManager.maximumCapacity, + this.additionalCoordinates, ); const buckets = bucketsWithPriorities.map(({ bucket }) => bucket); this.textureBucketManager.setActiveBuckets(buckets); @@ -260,6 +267,7 @@ export default class LayerRenderingManager { bucket: bucket.zoomedAddress, priority, })); + this.pullQueue.addAll(missingBuckets); this.pullQueue.pull(); }, diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_arbitrary.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_arbitrary.ts index 5ed93e6dda5..4ce0f2f2e02 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_arbitrary.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_arbitrary.ts @@ -6,6 +6,7 @@ import type { PullQueueItem } from "oxalis/model/bucket_data_handling/pullqueue" import { globalPositionToBucketPosition } from "oxalis/model/helpers/position_converter"; import PolyhedronRasterizer from "oxalis/model/bucket_data_handling/polyhedron_rasterizer"; import { ResolutionInfo } from "../helpers/resolution_info"; +import { type AdditionalCoordinate } from "types/api_flow_types"; export class PrefetchStrategyArbitrary extends AbstractPrefetchStrategy { velocityRangeStart = 0; @@ -58,15 +59,14 @@ export class PrefetchStrategyArbitrary extends AbstractPrefetchStrategy { position: Vector3, resolutions: Array, resolutionInfo: ResolutionInfo, + additionalCoordinates: AdditionalCoordinate[] | null, ): Array { - // @ts-expect-error ts-migrate(7034) FIXME: Variable 'pullQueue' implicitly has type 'any[]' i... Remove this comment to see the full error message - const pullQueue = []; + const pullQueue: PullQueueItem[] = []; const zoomStep = resolutionInfo.getIndexOrClosestHigherIndex(activeZoomStep); if (zoomStep == null) { // The layer cannot be rendered at this zoom step, as necessary magnifications // are missing. Don't prefetch anything. - // @ts-expect-error ts-migrate(7005) FIXME: Variable 'pullQueue' implicitly has an 'any[]' typ... Remove this comment to see the full error message return pullQueue; } @@ -84,6 +84,7 @@ export class PrefetchStrategyArbitrary extends AbstractPrefetchStrategy { position, resolutions, zoomStep, + null, ); const positionBucket: Vector3 = [ positionBucketWithZoomStep[0], @@ -92,12 +93,11 @@ export class PrefetchStrategyArbitrary extends AbstractPrefetchStrategy { ]; const distanceToPosition = V3.length(V3.sub([bucketX, bucketY, bucketZ], positionBucket)); pullQueue.push({ - bucket: [bucketX, bucketY, bucketZ, zoomStep], + bucket: [bucketX, bucketY, bucketZ, zoomStep, additionalCoordinates ?? []], priority: 1 + distanceToPosition, }); } - // @ts-expect-error ts-migrate(2322) FIXME: Type '{ bucket: any[]; priority: any; }[]' is not ... Remove this comment to see the full error message return pullQueue; } } diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts index a4365526bf8..03c382e0aad 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/prefetch_strategy_plane.ts @@ -5,10 +5,12 @@ import { zoomedAddressToAnotherZoomStep } from "oxalis/model/helpers/position_co import type DataCube from "oxalis/model/bucket_data_handling/data_cube"; import type { DimensionIndices } from "oxalis/model/dimensions"; import Dimensions from "oxalis/model/dimensions"; -import type { OrthoView, OrthoViewMap, Vector3 } from "oxalis/constants"; +import type { OrthoView, OrthoViewMap, Vector3, Vector4 } from "oxalis/constants"; import constants, { OrthoViewValuesWithoutTDView } from "oxalis/constants"; import { getPriorityWeightForPrefetch } from "oxalis/model/bucket_data_handling/loading_strategy_logic"; import { ResolutionInfo } from "../helpers/resolution_info"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + const { MAX_ZOOM_STEP_DIFF_PREFETCH } = constants; export enum ContentTypes { @@ -87,6 +89,7 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { areas: OrthoViewMap, resolutions: Vector3[], resolutionInfo: ResolutionInfo, + additionalCoordinates: AdditionalCoordinate[] | null, ): Array { const zoomStep = resolutionInfo.getIndexOrClosestHigherIndex(currentZoomStep); @@ -108,6 +111,7 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { areas, resolutions, false, + additionalCoordinates, ); let queueItemsForFallbackZoomStep: Array = []; const fallbackZoomStep = Math.min(maxZoomStep, currentZoomStep + 1); @@ -123,6 +127,7 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { areas, resolutions, true, + additionalCoordinates, ); } @@ -139,6 +144,7 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { areas: OrthoViewMap, resolutions: Vector3[], isFallback: boolean, + additionalCoordinates: AdditionalCoordinate[] | null, ): Array { const pullQueue: Array = []; @@ -146,8 +152,8 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { return pullQueue; } - const centerBucket = cube.positionToZoomedAddress(position, zoomStep); - const centerBucket3 = [centerBucket[0], centerBucket[1], centerBucket[2]]; + const centerBucket = cube.positionToZoomedAddress(position, additionalCoordinates, zoomStep); + const centerBucket3: Vector3 = [centerBucket[0], centerBucket[1], centerBucket[2]]; const fallbackPriorityWeight = isFallback ? 50 : 0; for (const plane of OrthoViewValuesWithoutTDView) { @@ -158,18 +164,16 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { this.w = w; // areas holds bucket indices for zoomStep = 0, which we want to // convert to the desired zoomStep - const widthHeightVector = [0, 0, 0, 0]; + const widthHeightVector: Vector4 = [0, 0, 0, 0]; widthHeightVector[u] = areas[plane].right - areas[plane].left; widthHeightVector[v] = areas[plane].bottom - areas[plane].top; const scaledWidthHeightVector = zoomedAddressToAnotherZoomStep( - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message widthHeightVector, resolutions, zoomStep, ); const width = scaledWidthHeightVector[u]; const height = scaledWidthHeightVector[v]; - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message const bucketPositions = this.getBucketPositions(centerBucket3, width, height); const prefetchWeight = getPriorityWeightForPrefetch(); @@ -180,8 +184,9 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { Math.abs(bucket[2] - centerBucket3[2]) + prefetchWeight + fallbackPriorityWeight; + pullQueue.push({ - bucket: [bucket[0], bucket[1], bucket[2], zoomStep], + bucket: [bucket[0], bucket[1], bucket[2], zoomStep, additionalCoordinates ?? []], priority, }); @@ -196,7 +201,7 @@ export class PrefetchStrategy extends AbstractPrefetchStrategy { const preloadingPriority = (priority << (slide + 1)) + this.preloadingPriorityOffset; pullQueue.push({ - bucket: [bucket[0], bucket[1], bucket[2], zoomStep], + bucket: [bucket[0], bucket[1], bucket[2], zoomStep, additionalCoordinates ?? []], priority: preloadingPriority, }); } diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/pullqueue.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/pullqueue.ts index ff6b00f1ca2..c8dc978f805 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/pullqueue.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/pullqueue.ts @@ -1,14 +1,15 @@ import PriorityQueue from "js-priority-queue"; import { getLayerByName } from "oxalis/model/accessors/dataset_accessor"; import { requestWithFallback } from "oxalis/model/bucket_data_handling/wkstore_adapter"; -import type { Vector4 } from "oxalis/constants"; +import type { BucketAddress } from "oxalis/constants"; import type DataCube from "oxalis/model/bucket_data_handling/data_cube"; import type { DataStoreInfo } from "oxalis/store"; import Store from "oxalis/store"; import { asAbortable } from "libs/utils"; + export type PullQueueItem = { priority: number; - bucket: Vector4; + bucket: BucketAddress; }; export const PullQueueConstants = { // For buckets that should be loaded immediately and @@ -69,7 +70,7 @@ class PullQueue { this.abortController = new AbortController(); } - async pullBatch(batch: Array): Promise { + async pullBatch(batch: Array): Promise { // Loading a bunch of buckets this.batchCount++; const { dataset } = Store.getState(); @@ -124,7 +125,10 @@ class PullQueue { } } - handleBucket(bucketAddress: Vector4, bucketData: Uint8Array | null | undefined): void { + private handleBucket( + bucketAddress: BucketAddress, + bucketData: Uint8Array | null | undefined, + ): void { const bucket = this.cube.getBucket(bucketAddress); if (bucket.type === "data") { diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts index 964cd7853fb..a1c4c5b9bdf 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/texture_bucket_manager.ts @@ -140,6 +140,11 @@ export default class TextureBucketManager { bucket.zoomedAddress[3], this.layerIndex, ]); + + // If a bucket is evicted from the GPU, it should not be rendered, anymore. + // This is especially important when new buckets take a while to load. In that + // time window, old data should not be rendered. + app.vent.emit("rerender"); } // Takes an array of buckets and ensures that these diff --git a/frontend/javascripts/oxalis/model/bucket_data_handling/wkstore_adapter.ts b/frontend/javascripts/oxalis/model/bucket_data_handling/wkstore_adapter.ts index 8b3ce1aba55..a1cafe91af5 100644 --- a/frontend/javascripts/oxalis/model/bucket_data_handling/wkstore_adapter.ts +++ b/frontend/javascripts/oxalis/model/bucket_data_handling/wkstore_adapter.ts @@ -19,11 +19,12 @@ import Request from "libs/request"; import type { DataLayerType, VolumeTracing } from "oxalis/store"; import Store from "oxalis/store"; import WebworkerPool from "libs/webworker_pool"; -import type { Vector3, Vector4 } from "oxalis/constants"; +import type { BucketAddress, Vector3 } from "oxalis/constants"; import constants, { MappingStatusEnum } from "oxalis/constants"; import window from "libs/window"; import { getGlobalDataConnectionInfo } from "../data_connection_info"; import { ResolutionInfo } from "../helpers/resolution_info"; +import { AdditionalCoordinate } from "types/api_flow_types"; import _ from "lodash"; const decodeFourBit = createWorker(DecodeFourBitWorker); @@ -39,8 +40,10 @@ const compressionPool = new WebworkerPool( ); export const REQUEST_TIMEOUT = 60000; + export type SendBucketInfo = { position: Vector3; + additionalCoordinates: Array | null | undefined; mag: Vector3; cubeSize: number; }; @@ -53,7 +56,7 @@ type RequestBucketInfo = SendBucketInfo & { // Converts a zoomed address ([x, y, z, zoomStep] array) into a bucket JSON // object as expected by the server on bucket request const createRequestBucketInfo = ( - zoomedAddress: Vector4, + zoomedAddress: BucketAddress, resolutionInfo: ResolutionInfo, fourBit: boolean, applyAgglomerate: string | null | undefined, @@ -74,11 +77,12 @@ const createRequestBucketInfo = ( }); function createSendBucketInfo( - zoomedAddress: Vector4, + zoomedAddress: BucketAddress, resolutionInfo: ResolutionInfo, ): SendBucketInfo { return { position: bucketPositionToGlobalAddress(zoomedAddress, resolutionInfo), + additionalCoordinates: zoomedAddress[4], mag: resolutionInfo.getResolutionByIndexOrThrow(zoomedAddress[3]), cubeSize: constants.BUCKET_WIDTH, }; @@ -90,7 +94,7 @@ function getNullIndices(arr: Array): Array { export async function requestWithFallback( layerInfo: DataLayerType, - batch: Array, + batch: Array, ): Promise> { const state = Store.getState(); const datasetName = state.dataset.name; @@ -156,7 +160,7 @@ export async function requestWithFallback( export async function requestFromStore( dataUrl: string, layerInfo: DataLayerType, - batch: Array, + batch: Array, maybeVolumeTracing: VolumeTracing | null | undefined, isVolumeFallback: boolean = false, ): Promise> { @@ -230,7 +234,7 @@ export async function requestFromStore( function sliceBufferIntoPieces( layerInfo: DataLayerType, - batch: Array, + batch: Array, missingBuckets: Array, buffer: Uint8Array, ): Array { diff --git a/frontend/javascripts/oxalis/model/data_layer.ts b/frontend/javascripts/oxalis/model/data_layer.ts index 7e47f97ffb6..d5b3053c543 100644 --- a/frontend/javascripts/oxalis/model/data_layer.ts +++ b/frontend/javascripts/oxalis/model/data_layer.ts @@ -39,6 +39,7 @@ class DataLayer { this.cube = new DataCube( getLayerBoundingBox(dataset, this.name), + layerInfo.additionalAxes || [], getResolutionInfo(this.resolutions), layerInfo.elementClass, this.isSegmentation, diff --git a/frontend/javascripts/oxalis/model/helpers/generate_dummy_trees.ts b/frontend/javascripts/oxalis/model/helpers/generate_dummy_trees.ts index de201e31f6c..cc0619756a8 100644 --- a/frontend/javascripts/oxalis/model/helpers/generate_dummy_trees.ts +++ b/frontend/javascripts/oxalis/model/helpers/generate_dummy_trees.ts @@ -1,5 +1,6 @@ import _ from "lodash"; import type { ServerSkeletonTracingTree } from "types/api_flow_types"; // This is a quick'n'dirty code to generate a huge amount of mocked trees. + // Since the server cannot handle such big tracings at the moment, we'll // use this code to test the front-end performance. // By default, this code is not used, but can be used similar to: @@ -27,6 +28,7 @@ export default function generateDummyTrees( y: 3725 + ((currentTreeId - 1) * treeCount) / 10, z: 1545, }, + additionalCoordinates: [], rotation: { x: 0, y: 270, diff --git a/frontend/javascripts/oxalis/model/helpers/nml_helpers.ts b/frontend/javascripts/oxalis/model/helpers/nml_helpers.ts index f5c8bbfd4d6..94db3ccb8db 100644 --- a/frontend/javascripts/oxalis/model/helpers/nml_helpers.ts +++ b/frontend/javascripts/oxalis/model/helpers/nml_helpers.ts @@ -29,6 +29,7 @@ import { BoundingBoxType, TreeType, TreeTypeEnum, Vector3 } from "oxalis/constan import Constants from "oxalis/constants"; import { location } from "libs/window"; import { coalesce } from "libs/utils"; +import { type AdditionalCoordinate } from "types/api_flow_types"; // NML Defaults const DEFAULT_COLOR: Vector3 = [1, 0, 0]; @@ -88,9 +89,17 @@ function serializeTag( properties: Record, closed: boolean = true, ): string { - return `<${name} ${Object.keys(properties) - // @ts-expect-error ts-migrate(2533) FIXME: Object is possibly 'null' or 'undefined'. - .map((key) => `${key}="${properties[key] != null ? escape(properties[key].toString()) : ""}"`) + const maybeSpace = Object.keys(properties).length > 0 ? " " : ""; + return `<${name}${maybeSpace}${Object.keys(properties) + .map((key) => { + let valueStr = ""; + const value = properties[key]; + if (value != null) { + valueStr = escape(value.toString()); + } + + return `${key}="${valueStr}"`; + }) .join(" ")}${closed ? " /" : ""}>`; } @@ -216,6 +225,9 @@ function serializeParameters( skeletonTracing: SkeletonTracing, ): Array { const editPosition = getPosition(state.flycam).map(Math.round); + const editPositionAdditionalCoordinates = state.flycam.additionalCoordinates; + const { additionalAxes } = skeletonTracing; + const editRotation = getRotation(state.flycam); const userBBoxes = skeletonTracing.userBoundingBoxes; const taskBB = skeletonTracing.boundingBox; @@ -246,6 +258,7 @@ function serializeParameters( x: editPosition[0], y: editPosition[1], z: editPosition[2], + ...additionalCoordinatesToObject(editPositionAdditionalCoordinates || []), }), serializeTag("editRotation", { xRot: editRotation[0], @@ -257,6 +270,21 @@ function serializeParameters( }), ...userBBoxes.map((userBB) => serializeUserBoundingBox(userBB, "userBoundingBox")), serializeTaskBoundingBox(taskBB, "taskBoundingBox"), + + ...(additionalAxes.length > 0 + ? serializeTagWithChildren( + "additionalCoordinates", + {}, + additionalAxes.map((coord) => + serializeTag("additionalCoordinate", { + name: coord.name, + index: coord.index, + min: coord.bounds[0], + max: coord.bounds[1], + }), + ), + ) + : []), ]), ), "", @@ -291,12 +319,15 @@ function serializeTrees(trees: Array): Array { function serializeNodes(nodes: NodeMap): Array { return nodes.map((node) => { const position = node.position.map(Math.floor); + const maybeProperties = additionalCoordinatesToObject(node.additionalCoordinates || []); + return serializeTag("node", { id: node.id, radius: node.radius, x: Math.trunc(position[0]), y: Math.trunc(position[1]), z: Math.trunc(position[2]), + ...maybeProperties, rotX: node.rotation[0], rotY: node.rotation[1], rotZ: node.rotation[2], @@ -309,6 +340,19 @@ function serializeNodes(nodes: NodeMap): Array { }); } +function additionalCoordinatesToObject(additionalCoordinates: AdditionalCoordinate[]) { + return Object.fromEntries( + additionalCoordinates.map((coord) => [ + // Export additional coordinates like this: + // additionalCoordinate-t="10" + // Don't capitalize coord.name, because it it's not reversible for + // names that are already capitalized. + `additionalCoordinate-${coord.name}`, + coord.value, + ]), + ); +} + function serializeEdges(edges: EdgeCollection): Array { return edges.map((edge) => serializeTag("edge", { @@ -694,6 +738,14 @@ export function parseNml(nmlString: string): Promise<{ Math.trunc(_parseFloat(attr, "y")), Math.trunc(_parseFloat(attr, "z")), ] as Vector3, + // Parse additional coordinates, like additionalCoordinate-t="10" + additionalCoordinates: Object.keys(attr) + .map((key) => [key, key.split("additionalCoordinate-")[1]]) + .filter(([_key, name]) => name != null) + .map(([key, name]) => ({ + name, + value: _parseFloat(attr, key, 0), + })) as AdditionalCoordinate[], rotation: [ _parseFloat(attr, "rotX", DEFAULT_ROTATION[0]), _parseFloat(attr, "rotY", DEFAULT_ROTATION[1]), diff --git a/frontend/javascripts/oxalis/model/helpers/position_converter.ts b/frontend/javascripts/oxalis/model/helpers/position_converter.ts index 98ccfaa0a13..d7e0d4da324 100644 --- a/frontend/javascripts/oxalis/model/helpers/position_converter.ts +++ b/frontend/javascripts/oxalis/model/helpers/position_converter.ts @@ -1,12 +1,14 @@ -import type { Vector3, Vector4 } from "oxalis/constants"; +import type { Vector3, Vector4, BucketAddress } from "oxalis/constants"; import constants from "oxalis/constants"; +import { type AdditionalCoordinate } from "types/api_flow_types"; import { type ResolutionInfo } from "./resolution_info"; export function globalPositionToBucketPosition( [x, y, z]: Vector3, resolutions: Array, resolutionIndex: number, -): Vector4 { + additionalCoordinates: AdditionalCoordinate[] | null | undefined, +): BucketAddress { const resolution = resolutionIndex < resolutions.length ? resolutions[resolutionIndex] @@ -16,6 +18,7 @@ export function globalPositionToBucketPosition( Math.floor(y / (constants.BUCKET_WIDTH * resolution[1])), Math.floor(z / (constants.BUCKET_WIDTH * resolution[2])), resolutionIndex, + additionalCoordinates || [], ]; } export function scaleGlobalPositionWithResolution( @@ -65,9 +68,10 @@ export function upsampleResolution(resolutions: Array, resolutionIndex: ]; } export function bucketPositionToGlobalAddress( - [x, y, z, resolutionIndex]: Vector4, + bucketPosition: BucketAddress, resolutionInfo: ResolutionInfo, ): Vector3 { + const [x, y, z, resolutionIndex, _additionalCoordinates] = bucketPosition; const resolution = resolutionInfo.getResolutionByIndexOrThrow(resolutionIndex); return [ x * constants.BUCKET_WIDTH * resolution[0], @@ -85,15 +89,17 @@ export function getResolutionsFactors(resolutionA: Vector3, resolutionB: Vector3 export function zoomedPositionToZoomedAddress( [x, y, z]: Vector3, resolutionIndex: number, -): Vector4 { + additionalCoordinates: AdditionalCoordinate[] | null, +): BucketAddress { return [ Math.floor(x / constants.BUCKET_WIDTH), Math.floor(y / constants.BUCKET_WIDTH), Math.floor(z / constants.BUCKET_WIDTH), resolutionIndex, + additionalCoordinates || [], ]; } -export function zoomedAddressToZoomedPosition([x, y, z, _]: Vector4): Vector3 { +export function zoomedAddressToZoomedPosition([x, y, z, _]: BucketAddress): Vector3 { return [x * constants.BUCKET_WIDTH, y * constants.BUCKET_WIDTH, z * constants.BUCKET_WIDTH]; } // TODO: zoomedAddressToAnotherZoomStep usages should be converted to zoomedAddressToAnotherZoomStepWithInfo diff --git a/frontend/javascripts/oxalis/model/reducers/connectome_reducer.ts b/frontend/javascripts/oxalis/model/reducers/connectome_reducer.ts index fc78d0516c1..430a21eb1d9 100644 --- a/frontend/javascripts/oxalis/model/reducers/connectome_reducer.ts +++ b/frontend/javascripts/oxalis/model/reducers/connectome_reducer.ts @@ -84,6 +84,7 @@ function ConnectomeReducer(state: OxalisState, action: Action): OxalisState { activeIndex: -1, }, showSkeletons: true, + additionalAxes: [], }; return update(state, { localSegmentationData: { diff --git a/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts b/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts index ff05e7751ae..f139928df7d 100644 --- a/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts +++ b/frontend/javascripts/oxalis/model/reducers/flycam_reducer.ts @@ -12,6 +12,7 @@ import { } from "oxalis/model/accessors/flycam_accessor"; import Dimensions from "oxalis/model/dimensions"; import * as Utils from "libs/utils"; +import { getUnifiedAdditionalCoordinates } from "../accessors/dataset_accessor"; function cloneMatrix(m: Matrix4x4): Matrix4x4 { return [ @@ -237,6 +238,42 @@ function FlycamReducer(state: OxalisState, action: Action): OxalisState { }); } + case "SET_ADDITIONAL_COORDINATES": { + const unifiedAdditionalCoordinates = getUnifiedAdditionalCoordinates(state.dataset); + + // In case the specified additional coordinates don't cover all existing additional + // coordinates, add the missing coordinates back. + // This *should* not happen, but in case of a bug somewhere, it's better to guard + // against this here (for example, if a skeleton node doesn't have the necessary + // additional coordinates, the UI shouldn't forget about the other additional + // coordinates). + let { values } = action; + + const existingAdditionalCoordinates = state.flycam.additionalCoordinates; + values = Utils.values(unifiedAdditionalCoordinates).map(({ name, bounds }, index) => { + const fallbackValue = + (existingAdditionalCoordinates != null + ? existingAdditionalCoordinates[index]?.value + : null) ?? bounds[0]; + if (values) { + const specifiedValue = values.find((element) => element.name === name); + if (specifiedValue) { + return { + name, + value: Utils.clamp(bounds[0], specifiedValue.value, bounds[1]), + }; + } + } + return { name, value: fallbackValue }; + }); + + return update(state, { + flycam: { + additionalCoordinates: { $set: values }, + }, + }); + } + case "SET_ROTATION": { // This action should only be dispatched when *not* being in orthogonal mode, // because this would lead to incorrect buckets being selected for rendering. diff --git a/frontend/javascripts/oxalis/model/reducers/reducer_helpers.ts b/frontend/javascripts/oxalis/model/reducers/reducer_helpers.ts index f1288a883d7..83f7d35fdf7 100644 --- a/frontend/javascripts/oxalis/model/reducers/reducer_helpers.ts +++ b/frontend/javascripts/oxalis/model/reducers/reducer_helpers.ts @@ -1,7 +1,9 @@ import Maybe from "data.maybe"; import { updateKey } from "oxalis/model/helpers/deep_update"; import type { + AdditionalAxis, APIAnnotation, + ServerAdditionalAxis, ServerBoundingBox, UserBoundingBoxFromServer, } from "types/api_flow_types"; @@ -113,6 +115,16 @@ export function convertServerAnnotationToFrontendAnnotation(annotation: APIAnnot blockedByUser: null, }; } + +export function convertServerAdditionalAxesToFrontEnd( + additionalAxes: ServerAdditionalAxis[], +): AdditionalAxis[] { + return additionalAxes.map((coords) => ({ + ...coords, + bounds: [coords.bounds.x, coords.bounds.y], + })); +} + export function getNextTool(state: OxalisState): AnnotationTool | null { const disabledToolInfo = getDisabledInfoForTools(state); const tools = Object.keys(AnnotationToolEnum) as AnnotationTool[]; diff --git a/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts b/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts index 6bbd6de73a3..a3023c0d0cb 100644 --- a/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts +++ b/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts @@ -4,6 +4,7 @@ import update from "immutability-helper"; import type { Action } from "oxalis/model/actions/actions"; import type { OxalisState, SkeletonTracing, Tree } from "oxalis/store"; import { + convertServerAdditionalAxesToFrontEnd, convertServerBoundingBoxToFrontend, convertUserBoundingBoxesFromServerToFrontend, } from "oxalis/model/reducers/reducer_helpers"; @@ -106,6 +107,7 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState activeIndex: -1, }, showSkeletons: true, + additionalAxes: convertServerAdditionalAxesToFrontEnd(action.tracing.additionalAxes), }; return update(state, { tracing: { @@ -552,7 +554,15 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState switch (action.type) { case "CREATE_NODE": { - const { position, rotation, viewport, resolution, treeId, timestamp } = action; + const { + position, + rotation, + viewport, + resolution, + treeId, + timestamp, + additionalCoordinates, + } = action; return getOrCreateTree(state, skeletonTracing, treeId, timestamp, TreeTypeEnum.DEFAULT) .chain((tree) => createNode( @@ -560,6 +570,7 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState skeletonTracing, tree, position, + additionalCoordinates, rotation, viewport, resolution, diff --git a/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.ts b/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.ts index 46c2766489a..3141b0ec393 100644 --- a/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.ts +++ b/frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.ts @@ -41,6 +41,8 @@ import DiffableMap from "libs/diffable_map"; import EdgeCollection from "oxalis/model/edge_collection"; import * as Utils from "libs/utils"; import { V3 } from "libs/mjs"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + export function generateTreeName(state: OxalisState, timestamp: number, treeId: number) { let user = ""; @@ -115,6 +117,7 @@ export function createNode( skeletonTracing: SkeletonTracing, tree: Tree, positionFloat: Vector3, + additionalCoordinates: AdditionalCoordinate[] | null, rotation: Vector3, viewport: number, resolution: number, @@ -137,6 +140,7 @@ export function createNode( // Create the new node const node: Node = { position, + additionalCoordinates, radius, rotation, viewport, @@ -801,6 +805,7 @@ function serverNodeToMutableNode(n: ServerNode): MutableNode { return { id: n.id, position: Utils.point3ToVector3(n.position), + additionalCoordinates: n.additionalCoordinates, rotation: Utils.point3ToVector3(n.rotation), bitDepth: n.bitDepth, viewport: n.viewport, diff --git a/frontend/javascripts/oxalis/model/reducers/volumetracing_reducer.ts b/frontend/javascripts/oxalis/model/reducers/volumetracing_reducer.ts index f016af38b5e..129a35b69e5 100644 --- a/frontend/javascripts/oxalis/model/reducers/volumetracing_reducer.ts +++ b/frontend/javascripts/oxalis/model/reducers/volumetracing_reducer.ts @@ -3,6 +3,7 @@ import { ContourModeEnum } from "oxalis/constants"; import type { EditableMapping, OxalisState, + Segment, SegmentGroup, SegmentMap, VolumeTracing, @@ -14,6 +15,7 @@ import type { RemoveSegmentAction, } from "oxalis/model/actions/volumetracing_actions"; import { + convertServerAdditionalAxesToFrontEnd, convertServerBoundingBoxToFrontend, convertUserBoundingBoxesFromServerToFrontend, } from "oxalis/model/reducers/reducer_helpers"; @@ -177,6 +179,7 @@ function handleUpdateSegment(state: OxalisState, action: UpdateSegmentAction) { name: null, color: null, groupId: null, + someAdditionalCoordinates: [], ...oldSegment, ...segment, somePosition, @@ -205,8 +208,9 @@ export function serverVolumeToClientVolumeTracing(tracing: ServerVolumeTracing): somePosition: segment.anchorPosition ? Utils.point3ToVector3(segment.anchorPosition) : undefined, + someAdditionalCoordinates: segment.additionalCoordinates, color: segment.color != null ? Utils.colorObjectToRGBArray(segment.color) : null, - }, + } as Segment, ]), ), segmentGroups: tracing.segmentGroups || [], @@ -222,6 +226,7 @@ export function serverVolumeToClientVolumeTracing(tracing: ServerVolumeTracing): userBoundingBoxes, mappingName: tracing.mappingName, mappingIsEditable: tracing.mappingIsEditable, + additionalAxes: convertServerAdditionalAxesToFrontEnd(tracing.additionalAxes), }; return volumeTracing; } diff --git a/frontend/javascripts/oxalis/model/sagas/clip_histogram_saga.ts b/frontend/javascripts/oxalis/model/sagas/clip_histogram_saga.ts index a7791a7ff85..42f1f224049 100644 --- a/frontend/javascripts/oxalis/model/sagas/clip_histogram_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/clip_histogram_saga.ts @@ -22,6 +22,7 @@ async function getClippingValues( const { dataset } = state; const { elementClass } = getLayerByName(dataset, layerName); const [TypedArrayClass] = getConstructorForElementClass(elementClass); + const { additionalCoordinates } = state.flycam; // Find a viable resolution to compute the histogram on // Ideally, we want to avoid resolutions 1 and 2 to keep @@ -32,9 +33,24 @@ async function getClippingValues( let dataForAllViewPorts; try { const [cuboidXY, cuboidXZ, cuboidYZ] = await Promise.all([ - api.data.getViewportData(OrthoViews.PLANE_XY, layerName, desiredResolutionIndex), - api.data.getViewportData(OrthoViews.PLANE_XZ, layerName, desiredResolutionIndex), - api.data.getViewportData(OrthoViews.PLANE_YZ, layerName, desiredResolutionIndex), + api.data.getViewportData( + OrthoViews.PLANE_XY, + layerName, + desiredResolutionIndex, + additionalCoordinates, + ), + api.data.getViewportData( + OrthoViews.PLANE_XZ, + layerName, + desiredResolutionIndex, + additionalCoordinates, + ), + api.data.getViewportData( + OrthoViews.PLANE_YZ, + layerName, + desiredResolutionIndex, + additionalCoordinates, + ), ]); dataForAllViewPorts = new TypedArrayClass(cuboidXY.length + cuboidXZ.length + cuboidYZ.length); // If getViewportData returned a BigUint array, dataForAllViewPorts will be an BigUint array, too. diff --git a/frontend/javascripts/oxalis/model/sagas/isosurface_saga.ts b/frontend/javascripts/oxalis/model/sagas/isosurface_saga.ts index d0b153ebe73..4661931cc1b 100644 --- a/frontend/javascripts/oxalis/model/sagas/isosurface_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/isosurface_saga.ts @@ -24,7 +24,6 @@ import type { Action } from "oxalis/model/actions/actions"; import type { Vector3 } from "oxalis/constants"; import { MappingStatusEnum } from "oxalis/constants"; import { - ImportIsosurfaceFromSTLAction, UpdateIsosurfaceVisibilityAction, RemoveIsosurfaceAction, RefreshIsosurfaceAction, @@ -51,8 +50,6 @@ import { meshV3, getMeshfilesForDatasetLayer, } from "admin/admin_rest_api"; -import { getFlooredPosition } from "oxalis/model/accessors/flycam_accessor"; -import { setImportingMeshStateAction } from "oxalis/model/actions/ui_actions"; import { zoomedAddressToAnotherZoomStepWithInfo } from "oxalis/model/helpers/position_converter"; import DataLayer from "oxalis/model/data_layer"; import { Model } from "oxalis/singletons"; @@ -74,6 +71,7 @@ import processTaskWithPool from "libs/async/task_pool"; import { getBaseSegmentationName } from "oxalis/view/right-border-tabs/segments_tab/segments_view_helper"; import { RemoveSegmentAction, UpdateSegmentAction } from "../actions/volumetracing_actions"; import { ResolutionInfo } from "../helpers/resolution_info"; +import { type AdditionalCoordinate } from "types/api_flow_types"; import Zip from "libs/zipjs_wrapper"; export const NO_LOD_MESH_INDEX = -1; @@ -95,6 +93,8 @@ const MESH_CHUNK_THROTTLE_LIMIT = 50; * Ad Hoc Meshes * */ +// Maps from layerName and segmentId to a ThreeDMap that stores for each chunk +// (at x, y, z) position whether the mesh chunk was loaded. const adhocIsosurfacesMapByLayer: Record>> = {}; function marchingCubeSizeInMag1(): Vector3 { // @ts-ignore @@ -178,6 +178,7 @@ function* loadAdHocIsosurfaceFromAction(action: LoadAdHocMeshAction): Saga yield* call( loadAdHocIsosurface, action.seedPosition, + action.seedAdditionalCoordinates, action.segmentId, false, action.layerName, @@ -187,6 +188,7 @@ function* loadAdHocIsosurfaceFromAction(action: LoadAdHocMeshAction): Saga function* loadAdHocIsosurface( seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, segmentId: number, removeExistingIsosurface: boolean = false, layerName?: string | null | undefined, @@ -199,12 +201,20 @@ function* loadAdHocIsosurface( return; } + if (_.size(layer.cube.additionalAxes) > 0) { + // Also see https://github.com/scalableminds/webknossos/issues/7229 + Toast.warning( + "The current segmentation layer has more than 3 dimensions. Meshes are not properly supported in this case.", + ); + } + const isosurfaceExtraInfo = yield* call(getIsosurfaceExtraInfo, layer.name, maybeExtraInfo); yield* call( loadIsosurfaceForSegmentId, segmentId, seedPosition, + seedAdditionalCoordinates, isosurfaceExtraInfo, removeExistingIsosurface, layer, @@ -253,6 +263,7 @@ function* getInfoForIsosurfaceLoading( function* loadIsosurfaceForSegmentId( segmentId: number, seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, isosurfaceExtraInfo: AdHocIsosurfaceInfo, removeExistingIsosurface: boolean, layer: DataLayer, @@ -273,6 +284,7 @@ function* loadIsosurfaceForSegmentId( layer, segmentId, seedPosition, + seedAdditionalCoordinates, zoomStep, isosurfaceExtraInfo, resolutionInfo, @@ -291,6 +303,7 @@ function* loadIsosurfaceWithNeighbors( layer: DataLayer, segmentId: number, position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | undefined, zoomStep: number, isosurfaceExtraInfo: AdHocIsosurfaceInfo, resolutionInfo: ResolutionInfo, @@ -300,7 +313,16 @@ function* loadIsosurfaceWithNeighbors( const { mappingName, mappingType } = isosurfaceExtraInfo; const clippedPosition = clipPositionToCubeBoundary(position); let positionsToRequest = [clippedPosition]; - yield* put(addAdHocIsosurfaceAction(layer.name, segmentId, position, mappingName, mappingType)); + yield* put( + addAdHocIsosurfaceAction( + layer.name, + segmentId, + position, + additionalCoordinates, + mappingName, + mappingType, + ), + ); yield* put(startedLoadingIsosurfaceAction(layer.name, segmentId)); while (positionsToRequest.length > 0) { @@ -475,6 +497,7 @@ function* refreshIsosurface(action: RefreshIsosurfaceAction): Saga { loadPrecomputedMeshAction( isosurfaceInfo.segmentId, isosurfaceInfo.seedPosition, + isosurfaceInfo.seedAdditionalCoordinates, isosurfaceInfo.meshFileName, layerName, ), @@ -513,13 +536,24 @@ function* _refreshIsosurfaceWithMap( // The isosurface should only be removed once after re-fetching the isosurface first position. let shouldBeRemoved = true; + // Meshing for N-D segmentations is not yet supported. + // See https://github.com/scalableminds/webknossos/issues/7229 + const seedAdditionalCoordinates = undefined; for (const [, position] of isosurfacePositions) { // Reload the isosurface at the given position if it isn't already loaded there. // This is done to ensure that every voxel of the isosurface is reloaded. - yield* call(loadAdHocIsosurface, position, segmentId, shouldBeRemoved, layerName, { - mappingName, - mappingType, - }); + yield* call( + loadAdHocIsosurface, + position, + seedAdditionalCoordinates, + segmentId, + shouldBeRemoved, + layerName, + { + mappingName, + mappingType, + }, + ); shouldBeRemoved = false; } @@ -586,7 +620,7 @@ function* maybeFetchMeshFiles(action: MaybeFetchMeshFilesAction): Saga { } function* loadPrecomputedMesh(action: LoadPrecomputedMeshAction) { - const { segmentId, seedPosition, meshFileName, layerName } = action; + const { segmentId, seedPosition, seedAdditionalCoordinates, meshFileName, layerName } = action; const layer = yield* select((state) => layerName != null ? getSegmentationLayerByName(state.dataset, layerName) @@ -602,6 +636,7 @@ function* loadPrecomputedMesh(action: LoadPrecomputedMeshAction) { loadPrecomputedMeshForSegmentId, segmentId, seedPosition, + seedAdditionalCoordinates, meshFileName, layer, ), @@ -620,11 +655,20 @@ type ChunksMap = Record { const layerName = segmentationLayer.name; - yield* put(addPrecomputedIsosurfaceAction(layerName, id, seedPosition, meshFileName)); + yield* put( + addPrecomputedIsosurfaceAction( + layerName, + id, + seedPosition, + seedAdditionalCoordinates, + meshFileName, + ), + ); yield* put(startedLoadingIsosurfaceAction(layerName, id)); const dataset = yield* select((state) => state.dataset); @@ -1008,29 +1052,6 @@ function* downloadIsosurfaceCells(action: TriggerIsosurfacesDownloadAction): Sag yield* call(downloadIsosurfaceCellsAsZIP, action.segmentsArray); } -function* importIsosurfaceFromSTL(action: ImportIsosurfaceFromSTLAction): Saga { - const { layerName, buffer } = action; - const dataView = new DataView(buffer); - const segmentId = dataView.getUint32(stlIsosurfaceConstants.segmentIdIndex, true); - const geometry = yield* call(parseStlBuffer, buffer); - getSceneController().segmentMeshController.addIsosurfaceFromGeometry( - geometry, - segmentId, - null, - null, - NO_LOD_MESH_INDEX, - layerName, - ); - yield* put(setImportingMeshStateAction(false)); - // TODO: Ideally, persist the seed position in the STL file. As a workaround, - // we simply use the current position as a seed position. - const seedPosition = yield* select((state) => getFlooredPosition(state.flycam)); - // TODO: This code is not used currently and it will not be possible to share these - // isosurfaces via link. - // The mesh file the isosurface was computed from is not known. - yield* put(addPrecomputedIsosurfaceAction(layerName, segmentId, seedPosition, "unknown")); -} - function* handleRemoveSegment(action: RemoveSegmentAction) { // The dispatched action will make sure that the isosurface entry is removed from the // store **and** from the scene. Otherwise, the store will still contain a reference @@ -1078,7 +1099,6 @@ export default function* isosurfaceSaga(): Saga { yield* takeEvery(loadPrecomputedMeshActionChannel, loadPrecomputedMesh); yield* takeEvery("TRIGGER_ISOSURFACE_DOWNLOAD", downloadIsosurfaceCell); yield* takeEvery("TRIGGER_ISOSURFACES_DOWNLOAD", downloadIsosurfaceCells); - yield* takeEvery("IMPORT_ISOSURFACE_FROM_STL", importIsosurfaceFromSTL); yield* takeEvery("REMOVE_ISOSURFACE", removeIsosurface); yield* takeEvery("REMOVE_SEGMENT", handleRemoveSegment); yield* takeEvery("REFRESH_ISOSURFACES", refreshIsosurfaces); diff --git a/frontend/javascripts/oxalis/model/sagas/min_cut_saga.ts b/frontend/javascripts/oxalis/model/sagas/min_cut_saga.ts index ee5e5d7c79e..77ab0abf4ca 100644 --- a/frontend/javascripts/oxalis/model/sagas/min_cut_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/min_cut_saga.ts @@ -22,6 +22,8 @@ import { api } from "oxalis/singletons"; import window from "libs/window"; import { APISegmentationLayer } from "types/api_flow_types"; import { ResolutionInfo } from "../helpers/resolution_info"; +import { type AdditionalCoordinate } from "types/api_flow_types"; + // By default, a new bounding box is created around // the seed nodes with a padding. Within the bounding box // the min-cut is computed. @@ -420,11 +422,14 @@ function* tryMinCutAtMag( const seedA = V3.sub(globalSeedA, boundingBoxTarget.min); const seedB = V3.sub(globalSeedB, boundingBoxTarget.min); console.log(`Loading data... (for ${boundingBoxTarget.getVolume()} vx)`); + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); + const inputData = yield* call( [api.data, api.data.getDataForBoundingBox], volumeTracingLayer.name, boundingBoxMag1, resolutionIndex, + additionalCoordinates, ); // For the 3D volume flat arrays are constructed // which can be accessed with the helper methods @@ -508,7 +513,16 @@ function* tryMinCutAtMag( const { visitedField } = traverseResidualsField(boundingBoxTarget, seedA, ll, edgeBuffer); console.timeEnd(`traverseResidualsField (${targetMagString})`); console.time(`labelDeletedEdges (${targetMagString})`); - labelDeletedEdges(visitedField, boundingBoxTarget, size, originalEdgeBuffer, targetMag, l, ll); + labelDeletedEdges( + visitedField, + boundingBoxTarget, + size, + originalEdgeBuffer, + targetMag, + l, + ll, + additionalCoordinates, + ); console.timeEnd(`labelDeletedEdges (${targetMagString})`); console.timeEnd(`Total min-cut (${targetMagString})`); } @@ -774,6 +788,7 @@ function labelDeletedEdges( targetMag: Vector3, l: L, ll: LL, + additionalCoordinates: AdditionalCoordinate[] | null, ) { for (let z = 0; z < size[2]; z++) { for (let y = 0; y < size[1]; y++) { @@ -796,7 +811,11 @@ function labelDeletedEdges( for (let dz = 0; dz < targetMag[2]; dz++) { for (let dy = 0; dy < targetMag[1]; dy++) { for (let dx = 0; dx < targetMag[0]; dx++) { - api.data.labelVoxels([V3.add(position, [dx, dy, dz])], 0); + api.data.labelVoxels( + [V3.add(position, [dx, dy, dz])], + 0, + additionalCoordinates, + ); } } } diff --git a/frontend/javascripts/oxalis/model/sagas/prefetch_saga.ts b/frontend/javascripts/oxalis/model/sagas/prefetch_saga.ts index ce7112be262..0083c414694 100644 --- a/frontend/javascripts/oxalis/model/sagas/prefetch_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/prefetch_saga.ts @@ -104,6 +104,7 @@ export function* prefetchForPlaneMode( const resolutionInfo = getResolutionInfo(layer.resolutions); const activePlane = yield* select((state) => state.viewModeData.plane.activeViewport); const tracingTypes = yield* select(getTracingTypes); + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); const lastConnectionStats = getGlobalDataConnectionInfo().lastStats; const { lastPosition, lastDirection, lastZoomStep, lastBucketPickerTick } = previousProperties; const direction = getTraceDirection(position, lastPosition, lastDirection); @@ -135,6 +136,7 @@ export function* prefetchForPlaneMode( areas, resolutions, resolutionInfo, + additionalCoordinates, ); if (bucketDebuggingFlags.visualizePrefetchedBuckets) { @@ -177,6 +179,7 @@ export function* prefetchForArbitraryMode( const { lastMatrix, lastZoomStep, lastBucketPickerTick } = previousProperties; const { pullQueue, cube } = Model.dataLayers[layer.name]; const lastConnectionStats = getGlobalDataConnectionInfo().lastStats; + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); if ( currentBucketPickerTick !== lastBucketPickerTick && @@ -188,7 +191,14 @@ export function* prefetchForArbitraryMode( strategy.inVelocityRange(lastConnectionStats.avgDownloadSpeedInBytesPerS) && strategy.inRoundTripTimeRange(lastConnectionStats.avgRoundTripTime) ) { - const buckets = strategy.prefetch(matrix, zoomStep, position, resolutions, resolutionInfo); + const buckets = strategy.prefetch( + matrix, + zoomStep, + position, + resolutions, + resolutionInfo, + additionalCoordinates, + ); if (bucketDebuggingFlags.visualizePrefetchedBuckets) { for (const item of buckets) { @@ -199,7 +209,6 @@ export function* prefetchForArbitraryMode( } } } - pullQueue.addAll(buckets); break; } diff --git a/frontend/javascripts/oxalis/model/sagas/proofread_saga.ts b/frontend/javascripts/oxalis/model/sagas/proofread_saga.ts index 7cbde3ab033..74a2c76948f 100644 --- a/frontend/javascripts/oxalis/model/sagas/proofread_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/proofread_saga.ts @@ -37,7 +37,7 @@ import { mergeAgglomerate, UpdateAction, } from "oxalis/model/sagas/update_actions"; -import { Model, api } from "oxalis/singletons"; +import { Model, api, Store } from "oxalis/singletons"; import { getActiveSegmentationTracingLayer, getActiveSegmentationTracing, @@ -62,6 +62,7 @@ import { Tree, VolumeTracing } from "oxalis/store"; import { APISegmentationLayer } from "types/api_flow_types"; import { setBusyBlockingInfoAction } from "oxalis/model/actions/ui_actions"; import _ from "lodash"; +import { type AdditionalCoordinate } from "types/api_flow_types"; export default function* proofreadRootSaga(): Saga { yield* take("INITIALIZE_SKELETONTRACING"); @@ -96,7 +97,12 @@ function proofreadUsingMeshes(): boolean { let coarselyLoadedSegmentIds: number[] = []; -function* loadCoarseMesh(layerName: string, segmentId: number, position: Vector3): Saga { +function* loadCoarseMesh( + layerName: string, + segmentId: number, + position: Vector3, + additionalCoordinates: AdditionalCoordinate[] | undefined, +): Saga { if ((yield* select((state) => state.userConfiguration.autoRenderMeshInProofreading)) === false) return; const currentMeshFile = yield* select( @@ -110,7 +116,14 @@ function* loadCoarseMesh(layerName: string, segmentId: number, position: Vector3 ) { // If a mesh file is active which was computed without a mapping, use that instead of computing // meshes ad-hoc. - yield* put(loadPrecomputedMeshAction(segmentId, position, currentMeshFile.meshFileName)); + yield* put( + loadPrecomputedMeshAction( + segmentId, + position, + additionalCoordinates, + currentMeshFile.meshFileName, + ), + ); } else { const mappingInfo = yield* select((state) => getMappingInfo(state.temporaryConfiguration.activeMappingByLayer, layerName), @@ -120,7 +133,7 @@ function* loadCoarseMesh(layerName: string, segmentId: number, position: Vector3 // Load the whole agglomerate mesh in a coarse resolution for performance reasons const preferredQuality = proofreadCoarseResolutionIndex(); yield* put( - loadAdHocMeshAction(segmentId, position, { + loadAdHocMeshAction(segmentId, position, additionalCoordinates, { mappingName, mappingType, preferredQuality, @@ -152,7 +165,7 @@ function* checkForAgglomerateSkeletonModification( } function* proofreadAtPosition(action: ProofreadAtPositionAction): Saga { - const { position } = action; + const { position, additionalCoordinates } = action; const volumeTracingLayer = yield* select((state) => getActiveSegmentationTracingLayer(state)); if (volumeTracingLayer == null || volumeTracingLayer.tracingId == null) return; @@ -168,7 +181,7 @@ function* proofreadAtPosition(action: ProofreadAtPositionAction): Saga { if (!proofreadUsingMeshes()) return; /* Load a coarse ad hoc mesh of the agglomerate at the click position */ - yield* call(loadCoarseMesh, layerName, segmentId, position); + yield* call(loadCoarseMesh, layerName, segmentId, position, additionalCoordinates); } function* createEditableMapping(): Saga { @@ -644,8 +657,15 @@ function* prepareSplitOrMerge( const agglomerateFileMag = resolutionInfo.getLowestResolution(); const agglomerateFileZoomstep = resolutionInfo.getLowestResolutionIndex(); - const getDataValue = (position: Vector3) => - api.data.getDataValue(layerName, position, agglomerateFileZoomstep); + const getDataValue = (position: Vector3) => { + const { additionalCoordinates } = Store.getState().flycam; + return api.data.getDataValue( + layerName, + position, + agglomerateFileZoomstep, + additionalCoordinates, + ); + }; return { layerName, agglomerateFileMag, getDataValue }; } @@ -715,9 +735,25 @@ function* removeOldMeshesAndLoadUpdatedMeshes( yield* put(removeIsosurfaceAction(layerName, targetAgglomerateId)); } - yield* call(loadCoarseMesh, layerName, newSourceAgglomerateId, sourceNodePosition); + // Segmentations with more than 3 dimensions are currently not compatible + // with proofreading. Once such datasets appear, this parameter needs to be + // adapted. + const additionalCoordinates = undefined; + yield* call( + loadCoarseMesh, + layerName, + newSourceAgglomerateId, + sourceNodePosition, + additionalCoordinates, + ); if (newTargetAgglomerateId !== newSourceAgglomerateId) { - yield* call(loadCoarseMesh, layerName, newTargetAgglomerateId, targetNodePosition); + yield* call( + loadCoarseMesh, + layerName, + newTargetAgglomerateId, + targetNodePosition, + additionalCoordinates, + ); } } } diff --git a/frontend/javascripts/oxalis/model/sagas/quick_select_heuristic_saga.ts b/frontend/javascripts/oxalis/model/sagas/quick_select_heuristic_saga.ts index ac75868be86..45d7d23cc1d 100644 --- a/frontend/javascripts/oxalis/model/sagas/quick_select_heuristic_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/quick_select_heuristic_saga.ts @@ -186,6 +186,8 @@ export default function* performQuickSelect(action: ComputeQuickSelectForRectAct const { startPosition, endPosition, quickSelectGeometry } = action; const layerBBox = yield* select((state) => getLayerBoundingBox(state.dataset, colorLayer.name)); + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); + const boundingBoxMag1 = new BoundingBox({ min: V3.floor(V3.min(startPosition, endPosition)), max: V3.floor( @@ -210,6 +212,7 @@ export default function* performQuickSelect(action: ComputeQuickSelectForRectAct colorLayer.name, boundingBoxMag1, labeledZoomStep, + additionalCoordinates, ); const size = boundingBoxTarget.getSize(); const stride = [1, size[0], size[0] * size[1]]; diff --git a/frontend/javascripts/oxalis/model/sagas/quick_select_ml_saga.ts b/frontend/javascripts/oxalis/model/sagas/quick_select_ml_saga.ts index 500396be6b2..0e605562151 100644 --- a/frontend/javascripts/oxalis/model/sagas/quick_select_ml_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/quick_select_ml_saga.ts @@ -241,6 +241,15 @@ export function* prefetchEmbedding(action: MaybePrefetchEmbeddingAction) { } export default function* performQuickSelect(action: ComputeQuickSelectForRectAction): Saga { + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); + if (additionalCoordinates && additionalCoordinates.length > 0) { + Toast.warning( + `Quick select with AI might produce unexpected results for ${ + 3 + additionalCoordinates.length + }D datasets.`, + ); + } + const preparation = yield* call(prepareQuickSelect, action); if (preparation == null) { return; diff --git a/frontend/javascripts/oxalis/model/sagas/save_saga.ts b/frontend/javascripts/oxalis/model/sagas/save_saga.ts index c46a4e79289..f21309792e7 100644 --- a/frontend/javascripts/oxalis/model/sagas/save_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/save_saga.ts @@ -295,12 +295,13 @@ function* markBucketsAsNotDirty(saveQueue: Array, tracingId: str for (const saveEntry of saveQueue) { for (const updateAction of saveEntry.actions) { if (updateAction.name === "updateBucket") { - const { position, mag } = updateAction.value; + const { position, mag, additionalCoordinates } = updateAction.value; const resolutionIndex = segmentationResolutionInfo.getIndexByResolution(mag); const zoomedBucketAddress = globalPositionToBucketPosition( position, segmentationResolutionInfo.getDenseResolutions(), resolutionIndex, + additionalCoordinates, ); const bucket = segmentationLayer.cube.getOrCreateBucket(zoomedBucketAddress); diff --git a/frontend/javascripts/oxalis/model/sagas/skeletontracing_saga.ts b/frontend/javascripts/oxalis/model/sagas/skeletontracing_saga.ts index 39b24006c68..39edda5e8f2 100644 --- a/frontend/javascripts/oxalis/model/sagas/skeletontracing_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/skeletontracing_saga.ts @@ -48,7 +48,11 @@ import { getTreeNameForAgglomerateSkeleton, } from "oxalis/model/accessors/skeletontracing_accessor"; import { getPosition, getRotation } from "oxalis/model/accessors/flycam_accessor"; -import { setPositionAction, setRotationAction } from "oxalis/model/actions/flycam_actions"; +import { + setAdditionalCoordinatesAction, + setPositionAction, + setRotationAction, +} from "oxalis/model/actions/flycam_actions"; import { setVersionRestoreVisibilityAction } from "oxalis/model/actions/ui_actions"; import DiffableMap, { diffDiffableMaps } from "libs/diffable_map"; import EdgeCollection, { diffEdgeCollections } from "oxalis/model/edge_collection"; @@ -96,13 +100,15 @@ function* centerActiveNode(action: Action): Saga { getActiveNode(yield* select((state: OxalisState) => enforceSkeletonTracing(state.tracing))).map( (activeNode) => { - // @ts-expect-error ts-migrate(2339) FIXME: Property 'suppressAnimation' does not exist on typ... Remove this comment to see the full error message - if (action.suppressAnimation === true) { + if ("suppressAnimation" in action && action.suppressAnimation) { Store.dispatch(setPositionAction(activeNode.position)); Store.dispatch(setRotationAction(activeNode.rotation)); } else { api.tracing.centerPositionAnimated(activeNode.position, false, activeNode.rotation); } + if (activeNode.additionalCoordinates) { + Store.dispatch(setAdditionalCoordinatesAction(activeNode.additionalCoordinates)); + } }, ); } @@ -605,6 +611,7 @@ export function* diffSkeletonTracing( yield updateSkeletonTracing( skeletonTracing, V3.floor(getPosition(flycam)), + flycam.additionalCoordinates, getRotation(flycam), flycam.zoomStep, ); diff --git a/frontend/javascripts/oxalis/model/sagas/undo_saga.ts b/frontend/javascripts/oxalis/model/sagas/undo_saga.ts index 6c75e12f994..d7874166b3d 100644 --- a/frontend/javascripts/oxalis/model/sagas/undo_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/undo_saga.ts @@ -1,7 +1,7 @@ import createProgressCallback from "libs/progress_callback"; import Toast from "libs/toast"; import messages from "messages"; -import { AnnotationToolEnum, type Vector4 } from "oxalis/constants"; +import { AnnotationToolEnum, type BucketAddress } from "oxalis/constants"; import { enforceSkeletonTracing } from "oxalis/model/accessors/skeletontracing_accessor"; import { getUserBoundingBoxesFromState } from "oxalis/model/accessors/tracing_accessor"; import { @@ -53,7 +53,7 @@ const UndoRedoRelevantBoundingBoxActions = AllUserBoundingBoxActions.filter( (action) => action !== "SET_USER_BOUNDING_BOXES", ); type UndoBucket = { - zoomedBucketAddress: Vector4; + zoomedBucketAddress: BucketAddress; // The following arrays are Uint8Array due to the compression compressedData: Uint8Array; compressedBackendData?: Promise; @@ -552,7 +552,7 @@ function getBoundingBoxToUndoState( } function* compressBucketAndAddToList( - zoomedBucketAddress: Vector4, + zoomedBucketAddress: BucketAddress, bucketData: BucketDataArray, maybeUnmergedBucketLoadedPromise: MaybeUnmergedBucketLoadedPromise, pendingOperations: Array<(arg0: BucketDataArray) => void>, diff --git a/frontend/javascripts/oxalis/model/sagas/update_actions.ts b/frontend/javascripts/oxalis/model/sagas/update_actions.ts index f93dcfc83de..090da1cb3d9 100644 --- a/frontend/javascripts/oxalis/model/sagas/update_actions.ts +++ b/frontend/javascripts/oxalis/model/sagas/update_actions.ts @@ -9,6 +9,7 @@ import type { SegmentGroup, } from "oxalis/store"; import { convertUserBoundingBoxesFromFrontendToServer } from "oxalis/model/reducers/reducer_helpers"; +import { AdditionalCoordinate } from "types/api_flow_types"; export type NodeWithTreeId = { treeId: number; @@ -219,7 +220,8 @@ export function updateSkeletonTracing( tracing: { activeNodeId: number | null | undefined; }, - position: Vector3, + editPosition: Vector3, + editPositionAdditionalCoordinates: AdditionalCoordinate[] | null, rotation: Vector3, zoomLevel: number, ) { @@ -227,7 +229,8 @@ export function updateSkeletonTracing( name: "updateTracing", value: { activeNode: tracing.activeNodeId, - editPosition: position, + editPosition, + editPositionAdditionalCoordinates, editRotation: rotation, zoomLevel, }, @@ -250,6 +253,7 @@ export function moveTreeComponent( export function updateVolumeTracing( tracing: VolumeTracing, position: Vector3, + editPositionAdditionalCoordinates: AdditionalCoordinate[] | null, rotation: Vector3, zoomLevel: number, ) { @@ -258,6 +262,7 @@ export function updateVolumeTracing( value: { activeSegmentId: tracing.activeCellId, editPosition: position, + editPositionAdditionalCoordinates, editRotation: rotation, largestSegmentId: tracing.largestSegmentId, zoomLevel, @@ -295,6 +300,7 @@ export function createSegmentVolumeAction( export function updateSegmentVolumeAction( id: number, anchorPosition: Vector3 | null | undefined, + additionalCoordinates: AdditionalCoordinate[] | undefined, name: string | null | undefined, color: Vector3 | null, groupId: number | null | undefined, @@ -305,6 +311,7 @@ export function updateSegmentVolumeAction( value: { id, anchorPosition, + additionalCoordinates, name, color, groupId, diff --git a/frontend/javascripts/oxalis/model/sagas/volume/helpers.ts b/frontend/javascripts/oxalis/model/sagas/volume/helpers.ts index 3affebbb1b4..d451705d632 100644 --- a/frontend/javascripts/oxalis/model/sagas/volume/helpers.ts +++ b/frontend/javascripts/oxalis/model/sagas/volume/helpers.ts @@ -155,6 +155,7 @@ export function* labelWithVoxelBuffer2D( viewport: OrthoView, ): Saga { const allowUpdate = yield* select((state) => state.tracing.restrictions.allowUpdate); + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); if (!allowUpdate) return; if (voxelBuffer.isEmpty()) return; const volumeTracing = yield* select(enforceActiveVolumeTracing); @@ -186,7 +187,11 @@ export function* labelWithVoxelBuffer2D( for (const boundingBoxChunk of bucketBoundingBoxes) { const { min, max } = boundingBoxChunk; - const bucketZoomedAddress = zoomedPositionToZoomedAddress(min, labeledZoomStep); + const bucketZoomedAddress = zoomedPositionToZoomedAddress( + min, + labeledZoomStep, + additionalCoordinates, + ); if (currentLabeledVoxelMap.get(bucketZoomedAddress)) { throw new Error("When iterating over the buckets, we shouldn't visit the same bucket twice"); diff --git a/frontend/javascripts/oxalis/model/sagas/volume/volume_interpolation_saga.ts b/frontend/javascripts/oxalis/model/sagas/volume/volume_interpolation_saga.ts index bcd2397828c..cc0e0f59310 100644 --- a/frontend/javascripts/oxalis/model/sagas/volume/volume_interpolation_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/volume/volume_interpolation_saga.ts @@ -336,11 +336,13 @@ export default function* maybeInterpolateSegmentationLayer(): Saga { .rounded(); const relevantBoxCurrentMag = relevantBoxMag1.fromMag1ToMag(labeledResolution); + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); const inputData = yield* call( [api.data, api.data.getDataForBoundingBox], volumeTracingLayer.name, relevantBoxMag1, labeledZoomStep, + additionalCoordinates, ); const size = relevantBoxCurrentMag.getSize(); diff --git a/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.tsx b/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.tsx index 95ad14809a4..5e71fcfd38b 100644 --- a/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.tsx +++ b/frontend/javascripts/oxalis/model/sagas/volumetracing_saga.tsx @@ -216,12 +216,14 @@ export function* editVolumeLayerAsync(): Saga { ); continue; } + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); yield* put( updateSegmentAction( activeCellId, { somePosition: startEditingAction.position, + someAdditionalCoordinates: additionalCoordinates || undefined, }, volumeTracing.tracingId, ), @@ -320,6 +322,7 @@ export function* editVolumeLayerAsync(): Saga { activeCellId, { somePosition: lastPosition, + someAdditionalCoordinates: additionalCoordinates || undefined, }, volumeTracing.tracingId, ), @@ -385,7 +388,13 @@ export function* floodFill(): Saga { ); const resolutionInfo = yield* call(getResolutionInfo, segmentationLayer.resolutions); const labeledZoomStep = resolutionInfo.getClosestExistingIndex(requestedZoomStep); - const oldSegmentIdAtSeed = cube.getDataValue(seedPosition, null, labeledZoomStep); + const additionalCoordinates = yield* select((state) => state.flycam.additionalCoordinates); + const oldSegmentIdAtSeed = cube.getDataValue( + seedPosition, + additionalCoordinates, + null, + labeledZoomStep, + ); if (activeCellId === oldSegmentIdAtSeed) { Toast.warning("The clicked voxel's id is already equal to the active segment id."); @@ -411,6 +420,7 @@ export function* floodFill(): Saga { yield* call(progressCallback, false, "Performing floodfill..."); console.time("cube.floodFill"); const fillMode = yield* select((state) => state.userConfiguration.fillMode); + const { bucketsWithLabeledVoxelsMap: labelMasksByBucketAndW, wasBoundingBoxExceeded, @@ -418,6 +428,7 @@ export function* floodFill(): Saga { } = yield* call( { context: cube, fn: cube.floodFill }, seedPosition, + additionalCoordinates, activeCellId, dimensionIndices, boundingBoxForFloodFill, @@ -466,6 +477,7 @@ export function* floodFill(): Saga { volumeTracing.activeCellId, { somePosition: seedPosition, + someAdditionalCoordinates: additionalCoordinates || undefined, }, volumeTracing.tracingId, ), @@ -604,6 +616,7 @@ function* uncachedDiffSegmentLists( yield updateSegmentVolumeAction( segment.id, segment.somePosition, + segment.someAdditionalCoordinates, segment.name, segment.color, segment.groupId, @@ -622,6 +635,7 @@ export function* diffVolumeTracing( yield updateVolumeTracing( volumeTracing, V3.floor(getPosition(flycam)), + flycam.additionalCoordinates, getRotation(flycam), flycam.zoomStep, ); @@ -678,12 +692,13 @@ function* ensureSegmentExists( } if (action.type === "ADD_AD_HOC_ISOSURFACE" || action.type === "ADD_PRECOMPUTED_ISOSURFACE") { - const { seedPosition } = action; + const { seedPosition, seedAdditionalCoordinates } = action; yield* put( updateSegmentAction( segmentId, { somePosition: seedPosition, + someAdditionalCoordinates: seedAdditionalCoordinates, }, layerName, ), @@ -693,7 +708,7 @@ function* ensureSegmentExists( // This way the most up-to-date position of a cell is used to jump to when a // segment is selected in the segment list. Also, the position of the active // cell is used in the proofreading mode. - const { somePosition } = action; + const { somePosition, someAdditionalCoordinates } = action; if (somePosition == null) { // Not all SetActiveCell actions provide a position (e.g., when simply setting the ID) @@ -710,6 +725,7 @@ function* ensureSegmentExists( segmentId, { somePosition, + someAdditionalCoordinates: someAdditionalCoordinates, }, layerName, undefined, diff --git a/frontend/javascripts/oxalis/model/volumetracing/volume_annotation_sampling.ts b/frontend/javascripts/oxalis/model/volumetracing/volume_annotation_sampling.ts index 5c4cec475f3..af7fe655fdd 100644 --- a/frontend/javascripts/oxalis/model/volumetracing/volume_annotation_sampling.ts +++ b/frontend/javascripts/oxalis/model/volumetracing/volume_annotation_sampling.ts @@ -1,5 +1,5 @@ import _ from "lodash"; -import type { Vector3, LabeledVoxelsMap, Vector4 } from "oxalis/constants"; +import type { Vector3, LabeledVoxelsMap, BucketAddress } from "oxalis/constants"; import constants from "oxalis/constants"; import { map3 } from "libs/utils"; import type DataCube from "oxalis/model/bucket_data_handling/data_cube"; @@ -68,15 +68,16 @@ function upsampleVoxelMap( secondDimBucketOffset < numberOfBucketWithinSourceBucket[dimensionIndices[1]]; secondDimBucketOffset++ ) { - const currentGoalBucketAddress = [...goalBaseBucketAddress]; + const currentGoalBucketAddress: Vector3 = [...goalBaseBucketAddress]; currentGoalBucketAddress[dimensionIndices[0]] += firstDimBucketOffset; currentGoalBucketAddress[dimensionIndices[1]] += secondDimBucketOffset; // The inner bucket of whose the voxelMap will be created. let annotatedAtleastOneVoxel = false; - // @ts-expect-error ts-migrate(2345) FIXME: Argument of type '[...number[], number]' is not as... Remove this comment to see the full error message + const currentGoalBucket = dataCube.getOrCreateBucket([ ...currentGoalBucketAddress, targetZoomStep, + labeledBucket.getAdditionalCoordinates() || [], ]); if (currentGoalBucket.type === "null") { @@ -188,7 +189,12 @@ function downsampleVoxelMap( (value, index) => Math.floor(value * scaleToGoal[index]), labeledBucket.getAddress(), ); - const goalBucket = dataCube.getOrCreateBucket([...goalBucketAddress, targetZoomStep]); + + const goalBucket = dataCube.getOrCreateBucket([ + ...goalBucketAddress, + targetZoomStep, + labeledBucket.getAdditionalCoordinates() || [], + ]); if (goalBucket.type === "null") { warnAboutCouldNotCreate([...goalBucketAddress, targetZoomStep]); @@ -344,7 +350,7 @@ export function applyVoxelMap( if (sliceCount > 0 && newThirdDimValue % constants.BUCKET_WIDTH === 0) { // The current slice is in the next bucket in the third direction. - const nextBucketZoomedAddress: Vector4 = [...labeledBucketZoomedAddress]; + const nextBucketZoomedAddress: BucketAddress = [...labeledBucketZoomedAddress]; nextBucketZoomedAddress[thirdDimensionIndex]++; postprocessBucket(bucket); bucket = dataCube.getOrCreateBucket(nextBucketZoomedAddress); diff --git a/frontend/javascripts/oxalis/model_initialization.ts b/frontend/javascripts/oxalis/model_initialization.ts index f8aa13fec36..181b85f4988 100644 --- a/frontend/javascripts/oxalis/model_initialization.ts +++ b/frontend/javascripts/oxalis/model_initialization.ts @@ -29,6 +29,7 @@ import { getSegmentationLayers, getLayerByName, getSegmentationLayerByName, + getUnifiedAdditionalCoordinates, } from "oxalis/model/accessors/dataset_accessor"; import { getNullableSkeletonTracing } from "oxalis/model/accessors/skeletontracing_accessor"; import { getServerVolumeTracings } from "oxalis/model/accessors/volumetracing_accessor"; @@ -72,6 +73,7 @@ import { setPositionAction, setZoomStepAction, setRotationAction, + setAdditionalCoordinatesAction, } from "oxalis/model/actions/flycam_actions"; import { setTaskAction } from "oxalis/model/actions/task_actions"; import { setToolAction } from "oxalis/model/actions/ui_actions"; @@ -102,6 +104,7 @@ import { PricingPlanEnum, isFeatureAllowedByPricingPlan, } from "admin/organization/pricing_plan_utils"; +import { convertServerAdditionalAxesToFrontEnd } from "./model/reducers/reducer_helpers"; export const HANDLED_ERROR = "error_was_handled"; type DataLayerCollection = Record; @@ -403,6 +406,16 @@ function initializeDataset( } Store.dispatch(setDatasetAction(mutableDataset as APIDataset)); + initializeAdditionalCoordinates(mutableDataset); +} + +function initializeAdditionalCoordinates(mutableDataset: MutableAPIDataset) { + const unifiedAdditionalCoordinates = getUnifiedAdditionalCoordinates(mutableDataset); + const initialAdditionalCoordinates = Utils.values(unifiedAdditionalCoordinates).map( + ({ name, bounds }) => ({ name, value: Math.floor((bounds[1] - bounds[0]) / 2) }), + ); + + Store.dispatch(setAdditionalCoordinatesAction(initialAdditionalCoordinates)); } function initializeSettings( @@ -511,6 +524,7 @@ function setupLayerForVolumeTracing( // Remember the name of the original layer (e.g., used to request mappings) fallbackLayer: tracing.fallbackLayer, fallbackLayerInfo: fallbackLayer, + additionalAxes: convertServerAdditionalAxesToFrontEnd(tracing.additionalAxes), }; if (fallbackLayerIndex > -1) { newLayers[fallbackLayerIndex] = tracingLayer; @@ -548,6 +562,7 @@ function determineDefaultState( ): PartialUrlManagerState { const { position: urlStatePosition, + additionalCoordinates: urlStateAdditionalCoordinates, zoomStep: urlStateZoomStep, rotation: urlStateRotation, activeNode: urlStateActiveNode, @@ -560,6 +575,7 @@ function determineDefaultState( const { viewMode } = temporaryConfiguration; const defaultPosition = datasetConfiguration.position; let position = getDatasetCenter(dataset); + let additionalCoordinates = null; if (defaultPosition != null) { position = defaultPosition; @@ -569,12 +585,17 @@ function determineDefaultState( if (someTracing != null) { position = Utils.point3ToVector3(someTracing.editPosition); + additionalCoordinates = someTracing.editPositionAdditionalCoordinates; } if (urlStatePosition != null) { position = urlStatePosition; } + if (urlStateAdditionalCoordinates != null) { + additionalCoordinates = urlStateAdditionalCoordinates; + } + let zoomStep = datasetConfiguration.zoom; if (someTracing != null) { @@ -626,6 +647,7 @@ function determineDefaultState( rotation, activeNode, stateByLayer, + additionalCoordinates, ...rest, }; } @@ -652,6 +674,10 @@ export function applyState(state: PartialUrlManagerState, ignoreZoom: boolean = if (state.stateByLayer != null) { applyLayerState(state.stateByLayer); } + + if (state.additionalCoordinates != null) { + Store.dispatch(setAdditionalCoordinatesAction(state.additionalCoordinates)); + } } async function applyLayerState(stateByLayer: UrlStateByLayer) { @@ -737,12 +763,18 @@ async function applyLayerState(stateByLayer: UrlStateByLayer) { } for (const mesh of meshes) { - const { segmentId, seedPosition } = mesh; + const { segmentId, seedPosition, seedAdditionalCoordinates } = mesh; if (mesh.isPrecomputed) { const { meshFileName } = mesh; Store.dispatch( - loadPrecomputedMeshAction(segmentId, seedPosition, meshFileName, effectiveLayerName), + loadPrecomputedMeshAction( + segmentId, + seedPosition, + seedAdditionalCoordinates, + meshFileName, + effectiveLayerName, + ), ); } else { const { mappingName, mappingType } = mesh; @@ -750,6 +782,7 @@ async function applyLayerState(stateByLayer: UrlStateByLayer) { loadAdHocMeshAction( segmentId, seedPosition, + seedAdditionalCoordinates, { mappingName, mappingType, diff --git a/frontend/javascripts/oxalis/store.ts b/frontend/javascripts/oxalis/store.ts index bdedc106ed6..205cc8abe2e 100644 --- a/frontend/javascripts/oxalis/store.ts +++ b/frontend/javascripts/oxalis/store.ts @@ -24,6 +24,8 @@ import type { ServerEditableMapping, APIOrganization, APIUserCompact, + AdditionalCoordinate, + AdditionalAxis, } from "types/api_flow_types"; import type { Action } from "oxalis/model/actions/actions"; import type { @@ -82,6 +84,7 @@ export type Edge = Readonly; export type MutableNode = { id: number; position: Vector3; + additionalCoordinates: AdditionalCoordinate[] | null; rotation: Vector3; bitDepth: number; viewport: number; @@ -199,6 +202,7 @@ type TracingBase = { readonly tracingId: string; readonly boundingBox: BoundingBoxType | null | undefined; readonly userBoundingBoxes: Array; + readonly additionalAxes: AdditionalAxis[]; }; export type NavigationList = { readonly list: Array; @@ -219,6 +223,7 @@ export type Segment = { readonly id: number; readonly name: string | null | undefined; readonly somePosition: Vector3 | undefined; + readonly someAdditionalCoordinates: AdditionalCoordinate[] | undefined; readonly creationTime: number | null | undefined; readonly color: Vector3 | null; readonly groupId: number | null | undefined; @@ -445,6 +450,7 @@ export type SaveState = { export type Flycam = { readonly zoomStep: number; readonly currentMatrix: Matrix4x4; + readonly additionalCoordinates: AdditionalCoordinate[] | null; readonly spaceDirectionOrtho: [-1 | 1, -1 | 1, -1 | 1]; readonly direction: Vector3; }; @@ -522,6 +528,7 @@ type UiInformation = { type BaseIsosurfaceInformation = { readonly segmentId: number; readonly seedPosition: Vector3; + readonly seedAdditionalCoordinates?: AdditionalCoordinate[]; readonly isLoading: boolean; readonly isVisible: boolean; }; diff --git a/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx b/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx index 50b732634fc..5ae47d000b1 100644 --- a/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx +++ b/frontend/javascripts/oxalis/view/action-bar/dataset_position_view.tsx @@ -16,6 +16,7 @@ import type { ViewMode, Vector3 } from "oxalis/constants"; import constants from "oxalis/constants"; import message from "messages"; import { ShareButton } from "oxalis/view/action-bar/share_modal_view"; + type Props = { flycam: Flycam; viewMode: ViewMode; diff --git a/frontend/javascripts/oxalis/view/action-bar/download_modal_view.tsx b/frontend/javascripts/oxalis/view/action-bar/download_modal_view.tsx index 180360815a0..eaea5721e7a 100644 --- a/frontend/javascripts/oxalis/view/action-bar/download_modal_view.tsx +++ b/frontend/javascripts/oxalis/view/action-bar/download_modal_view.tsx @@ -2,7 +2,7 @@ import { Divider, Modal, Checkbox, Row, Col, Tabs, Typography, Button, Radio, Al import { CopyOutlined } from "@ant-design/icons"; import React, { useState } from "react"; import { makeComponentLazy, useFetch } from "libs/react_helpers"; -import type { APIDataLayer, APIDataset } from "types/api_flow_types"; +import type { AdditionalAxis, APIDataLayer, APIDataset } from "types/api_flow_types"; import Toast from "libs/toast"; import messages from "messages"; import { Model } from "oxalis/singletons"; @@ -61,6 +61,7 @@ type ExportLayerInfos = { layerName: string | null; tracingId: string | null; annotationId: string | null; + additionalAxes?: AdditionalAxis[] | null; }; enum ExportFormat { @@ -85,6 +86,7 @@ function getExportLayerInfos( layerName: layer.name, tracingId: null, annotationId: null, + additionalAxes: layer.additionalAxes, }; } @@ -102,6 +104,7 @@ function getExportLayerInfos( layerName: layer.fallbackLayerInfo?.name ?? null, tracingId: volumeTracing.tracingId, annotationId, + additionalAxes: layer.additionalAxes, }; } @@ -325,10 +328,14 @@ function _DownloadModalView({ tracing.annotationType, hasVolumeFallback, {}, - includeVolumeData, + includeVolumeData && !isVolumeNDimensional, ); onClose(); } else if (activeTabKey === "export" && startJob != null) { + if ((selectedLayerInfos.additionalAxes || []).length > 0) { + Toast.warning("Exporting an n-dimensional layer is currently not supported."); + return; + } await Model.ensureSavedState(); await startJob(async () => { const job = await startExportTiffJob( @@ -351,9 +358,9 @@ function _DownloadModalView({ }; const maybeShowWarning = () => { - if (activeTabKey === "download" && hasVolumeFallback) { - return ( - + const volumeFallbackWarning = + activeTabKey === "download" && hasVolumeFallback ? ( + - ); - } else if (activeTabKey === "python") { - return ( - + ) : null; + const ndVolumeWarning = isVolumeNDimensional ? ( + + + Downloading/exporting n-dimensional volume data is not yet supported. + + + ) : null; + const pythonTokenWarning = + activeTabKey === "python" ? ( + - ); - } - return null; + ) : null; + + return [volumeFallbackWarning, ndVolumeWarning, pythonTokenWarning]; }; const handleTabChange = (key: string) => { @@ -473,6 +492,9 @@ function _DownloadModalView({ activeTabKey === "export" && (!isExportable || isCurrentlyRunningExportJob || isMergerModeEnabled); + // Will be false if no volumes exist. + const isVolumeNDimensional = tracing.volumes.some((tracing) => tracing.additionalAxes.length > 0); + return ( Volume annotations as WKW @@ -577,6 +599,7 @@ function _DownloadModalView({ ) : null} + {maybeShowWarning()} + getUnifiedAdditionalCoordinates(state.dataset), + ); + const additionalCoordinates = useSelector( + (state: OxalisState) => state.flycam.additionalCoordinates, + ); + const dispatch = useDispatch(); + const changeAdditionalCoordinates = (values: AdditionalCoordinate[] | null) => { + if (values != null) { + dispatch(setAdditionalCoordinatesAction(values)); + } + }; + const changeAdditionalCoordinatesFromVector = (values: number[]) => { + if (additionalCoordinates != null) { + dispatch( + setAdditionalCoordinatesAction( + additionalCoordinates.map((el, index) => ({ + ...el, + value: values[index], + })), + ), + ); + } + }; + + if (additionalCoordinates == null || additionalCoordinates.length === 0) { + return null; + } + return ( + + {additionalCoordinates.map((coord, idx) => { + const { bounds } = additionalAxes[coord.name]; + return ( + { + const newCoords = additionalCoordinates.slice(); + newCoords[idx] = { + ...newCoords[idx], + value: newCoord, + }; + changeAdditionalCoordinates(newCoords); + }} + /> + ); + })} + + } + > + el.value)} + onChange={changeAdditionalCoordinatesFromVector} + style={{ marginLeft: 10, marginRight: 10 }} + addonBefore={additionalCoordinates.map((coord) => coord.name).join("")} + /> + + ); +} + class ActionBarView extends React.PureComponent { state: State = { isNewLayoutModalOpen: false, @@ -173,6 +247,7 @@ class ActionBarView extends React.PureComponent { )} {showVersionRestore ? VersionRestoreWarning : null} + {isArbitrarySupported && !is2d ? : null} {!isReadOnly && constants.MODES_PLANE.indexOf(viewMode) > -1 ? : null} {isViewMode ? this.renderStartTracingButton() : null} diff --git a/frontend/javascripts/oxalis/view/components/input_component.tsx b/frontend/javascripts/oxalis/view/components/input_component.tsx index 9a3722bedfc..753bfce37f1 100644 --- a/frontend/javascripts/oxalis/view/components/input_component.tsx +++ b/frontend/javascripts/oxalis/view/components/input_component.tsx @@ -1,22 +1,16 @@ -import { Input, InputProps, Tooltip } from "antd"; +import { Input, InputProps, InputRef, Tooltip } from "antd"; import * as React from "react"; import _ from "lodash"; -type InputComponentState = { - isFocused: boolean; - currentValue: React.InputHTMLAttributes["value"]; -}; // TODO Double check if we still need this once React v16 is released. - /* - * A lightweight wrapper around to deal with a "jumping cursor" bug. - * Modifying a input's value will always reset the cursor to the end even if - * you are editing the middle of a string. Saving the input's value in state - * remedies this. Rumors claim React v16 will fix this. - * Inspired by https://github.com/facebook/react/issues/955#issuecomment-281802381 - * @class + * A lightweight wrapper around which does: + * - automatically blur on Escape + * - maintain the cursor position / selection even when mutating the input value + * while it's focused (mainly necessary when mutating the value on arrow-keypresses) */ -class InputComponent extends React.PureComponent { +class InputComponent extends React.PureComponent { + inputRef = React.createRef(); static defaultProps: InputProps = { onChange: _.noop, placeholder: "", @@ -24,24 +18,35 @@ class InputComponent extends React.PureComponent) => { - this.setState({ - currentValue: e.target.value, - }); + componentDidUpdate( + _prevProps: InputProps, + _prevState: {}, + snapshot: [number | null, number | null], + ) { + // Restore the remembered selection when necessary + try { + // @ts-ignore + this.inputRef.current.input.selectionStart = snapshot[0]; + // @ts-ignore + this.inputRef.current.input.selectionEnd = snapshot[1]; + } catch {} + } + handleChange = (e: React.ChangeEvent) => { if (this.props.onChange) { this.props.onChange(e); } @@ -73,23 +78,27 @@ class InputComponent extends React.PureComponent document.activeElement ? (document.activeElement as HTMLElement).blur() : null; - blurOnEscape = (event: React.KeyboardEvent) => { + blurOnEscape = (event: React.KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); this.blurYourself(); } + if (this.props.onKeyDown) { + return this.props.onKeyDown(event); + } }; render() { const { title, style, ...inputProps } = this.props; const input = ( ); diff --git a/frontend/javascripts/oxalis/view/components/setting_input_views.tsx b/frontend/javascripts/oxalis/view/components/setting_input_views.tsx index 9c137a8482b..c7861824e14 100644 --- a/frontend/javascripts/oxalis/view/components/setting_input_views.tsx +++ b/frontend/javascripts/oxalis/view/components/setting_input_views.tsx @@ -29,12 +29,14 @@ type NumberSliderSettingProps = { min: number; step: number; disabled: boolean; + spans: Vector3; }; export class NumberSliderSetting extends React.PureComponent { static defaultProps = { min: 1, step: 1, disabled: false, + spans: [SETTING_LEFT_SPAN, SETTING_MIDDLE_SPAN, SETTING_VALUE_SPAN], }; _onChange = (_value: number | null) => { @@ -54,10 +56,10 @@ export class NumberSliderSetting extends React.PureComponent - + - + - + | null; export const ContextMenuContext = createContext(null); @@ -113,32 +114,12 @@ type OwnProps = { maybeMeshIntersectionPosition: Vector3 | null | undefined; clickedBoundingBoxId: number | null | undefined; globalPosition: Vector3 | null | undefined; + additionalCoordinates: AdditionalCoordinate[] | undefined; maybeViewport: OrthoView | null | undefined; hideContextMenu: () => void; }; -type DispatchProps = { - deleteEdge: (arg0: number, arg1: number) => void; - mergeTrees: (arg0: number, arg1: number) => void; - minCutAgglomerate: (arg0: number, arg1: number) => void; - deleteNode: (arg0: number, arg1: number) => void; - setActiveNode: (arg0: number) => void; - hideTree: (arg0: number) => void; - createTree: () => void; - addTreesAndGroups: (arg0: MutableTreeMap) => void; - hideBoundingBox: (arg0: number) => void; - setBoundingBoxColor: (arg0: number, arg1: Vector3) => void; - setBoundingBoxName: (arg0: number, arg1: string) => void; - addNewBoundingBox: (arg0: Vector3) => void; - deleteBoundingBox: (arg0: number) => void; - setActiveCell: (arg0: number, somePosition?: Vector3) => void; - createBranchPoint: (arg0: number, arg1: number) => void; - deleteBranchpointById: (arg0: number, arg1: number) => void; - performMinCut: (arg0: number, arg1: number | undefined) => void; - removeMesh: (arg0: string, arg1: number) => void; - hideMesh: (arg0: string, arg1: number) => void; - setPosition: (arg0: Vector3) => void; - refreshMesh: (arg0: string, arg1: number) => void; -}; + +type DispatchProps = ReturnType; type StateProps = { skeletonTracing: SkeletonTracing | null | undefined; datasetScale: Vector3; @@ -223,8 +204,12 @@ function measureAndShowFullTreeLength(treeId: number, treeName: string) { }); } -function positionToString(pos: Vector3): string { - return pos.map((value) => roundTo(value, 2)).join(", "); +function positionToString( + pos: Vector3, + optAdditionalCoordinates: AdditionalCoordinate[] | undefined | null, +): string { + const additionalCoordinates = (optAdditionalCoordinates || []).map((coord) => coord.value); + return [...pos, ...additionalCoordinates].map((value) => roundTo(value, 2)).join(", "); } function shortcutBuilder(shortcuts: Array): React.ReactNode { @@ -746,6 +731,7 @@ function getNoNodeContextMenuOptions(props: NoNodeContextMenuProps): ItemType[] volumeTracing, activeTool, globalPosition, + additionalCoordinates, maybeClickedMeshId, maybeMeshIntersectionPosition, viewport, @@ -786,7 +772,12 @@ function getNoNodeContextMenuOptions(props: NoNodeContextMenuProps): ItemType[] } Store.dispatch( - loadPrecomputedMeshAction(segmentId, globalPosition, currentMeshFile.meshFileName), + loadPrecomputedMeshAction( + segmentId, + globalPosition, + additionalCoordinates, + currentMeshFile.meshFileName, + ), ); }; @@ -802,7 +793,7 @@ function getNoNodeContextMenuOptions(props: NoNodeContextMenuProps): ItemType[] return; } - Store.dispatch(loadAdHocMeshAction(segmentId, globalPosition)); + Store.dispatch(loadAdHocMeshAction(segmentId, globalPosition, additionalCoordinates)); }; const isVolumeBasedToolActive = VolumeTools.includes(activeTool); @@ -936,7 +927,7 @@ function getNoNodeContextMenuOptions(props: NoNodeContextMenuProps): ItemType[] ? { key: "select-cell", onClick: () => { - setActiveCell(segmentIdAtPosition, globalPosition); + setActiveCell(segmentIdAtPosition, globalPosition, additionalCoordinates); }, label: ( <> @@ -1123,9 +1114,12 @@ function ContextMenuInner(propsWithInputRef: Props) { nodeContextMenuTree = tree; }); } + // TS doesnt understand the above initialization and assumes the values + // are always null. The following NOOP helps TS with the correct typing. + nodeContextMenuTree = nodeContextMenuTree as Tree | null; + nodeContextMenuNode = nodeContextMenuNode as MutableNode | null; const positionToMeasureDistanceTo = - // @ts-expect-error ts-migrate(2339) FIXME: Property 'position' does not exist on type 'never'... Remove this comment to see the full error message nodeContextMenuNode != null ? nodeContextMenuNode.position : globalPosition; const activeNode = activeNodeId != null && skeletonTracing != null @@ -1141,8 +1135,9 @@ function ContextMenuInner(propsWithInputRef: Props) { ] : null; const nodePositionAsString = - // @ts-expect-error ts-migrate(2339) FIXME: Property 'position' does not exist on type 'never'... Remove this comment to see the full error message - nodeContextMenuNode != null ? positionToString(nodeContextMenuNode.position) : ""; + nodeContextMenuNode != null + ? positionToString(nodeContextMenuNode.position, nodeContextMenuNode.additionalCoordinates) + : ""; const segmentIdAtPosition = globalPosition != null ? getSegmentIdForPosition(globalPosition) : 0; const infoRows = []; @@ -1150,7 +1145,6 @@ function ContextMenuInner(propsWithInputRef: Props) { infoRows.push( getInfoMenuItem( "nodeInfo", - // @ts-expect-error FIXME: Property 'treeId' does not exist on type 'never'... Remove this comment to see the full error message `Node with Id ${maybeClickedNodeId} in Tree ${nodeContextMenuTree.treeId}`, ), ); @@ -1168,7 +1162,8 @@ function ContextMenuInner(propsWithInputRef: Props) { ), ); } else if (globalPosition != null) { - const positionAsString = positionToString(globalPosition); + const positionAsString = positionToString(globalPosition, props.additionalCoordinates); + infoRows.push( getInfoMenuItem( "positionInfo", @@ -1323,8 +1318,12 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ dispatch(createTreeAction()); }, - setActiveCell(segmentId: number, somePosition?: Vector3) { - dispatch(setActiveCellAction(segmentId, somePosition)); + setActiveCell( + segmentId: number, + somePosition?: Vector3, + someAdditionalCoordinates?: AdditionalCoordinate[], + ) { + dispatch(setActiveCellAction(segmentId, somePosition, someAdditionalCoordinates)); }, addNewBoundingBox(center: Vector3) { diff --git a/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx b/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx index c73be088c7a..fa1e180bf0d 100644 --- a/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx +++ b/frontend/javascripts/oxalis/view/layouting/tracing_layout_view.tsx @@ -7,7 +7,7 @@ import { document, location } from "libs/window"; import _ from "lodash"; import messages from "messages"; import CrossOriginApi from "oxalis/api/cross_origin_api"; -import type { OrthoView, Vector3, ViewMode } from "oxalis/constants"; +import type { OrthoView, Vector3 } from "oxalis/constants"; import Constants from "oxalis/constants"; import type { ControllerStatus } from "oxalis/controller"; import OxalisController from "oxalis/controller"; @@ -53,18 +53,7 @@ type OwnProps = { initialMaybeCompoundType: APICompoundType | null; initialCommandType: TraceOrViewCommand; }; -type StateProps = { - viewMode: ViewMode; - isUpdateTracingAllowed: boolean; - showVersionRestore: boolean; - storedLayouts: Record; - isDatasetOnScratchVolume: boolean; - autoSaveLayouts: boolean; - datasetName: string; - is2d: boolean; - displayName: string; - organization: string; -}; +type StateProps = ReturnType; type DispatchProps = { setAutoSaveLayouts: (arg0: boolean) => void; }; @@ -350,6 +339,7 @@ class TracingLayoutView extends React.PureComponent { maybeClickedNodeId={this.state.clickedNodeId} clickedBoundingBoxId={this.state.clickedBoundingBoxId} globalPosition={this.state.contextMenuGlobalPosition} + additionalCoordinates={this.props.additionalCoordinates || undefined} contextMenuPosition={contextMenuPosition} maybeViewport={contextMenuViewport} maybeClickedMeshId={this.state.contextMenuMeshId} @@ -468,7 +458,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ }, }); -function mapStateToProps(state: OxalisState): StateProps { +function mapStateToProps(state: OxalisState) { return { viewMode: state.temporaryConfiguration.viewMode, autoSaveLayouts: state.userConfiguration.autoSaveLayouts, @@ -480,6 +470,7 @@ function mapStateToProps(state: OxalisState): StateProps { is2d: is2dDataset(state.dataset), displayName: state.tracing.name ? state.tracing.name : state.dataset.name, organization: state.dataset.owningOrganization, + additionalCoordinates: state.flycam.additionalCoordinates, }; } diff --git a/frontend/javascripts/oxalis/view/right-border-tabs/connectome_tab/connectome_view.tsx b/frontend/javascripts/oxalis/view/right-border-tabs/connectome_tab/connectome_view.tsx index c885f61b1b3..f3dc73d6890 100644 --- a/frontend/javascripts/oxalis/view/right-border-tabs/connectome_tab/connectome_view.tsx +++ b/frontend/javascripts/oxalis/view/right-border-tabs/connectome_tab/connectome_view.tsx @@ -166,6 +166,9 @@ const synapseNodeCreator = (synapseId: number, synapsePosition: Vector3): Mutabl timestamp: Date.now(), bitDepth: 8, interpolation: false, + // Don't assume any additionalCoordinates here, because no 4D connectomes are + // known yet. Also see https://github.com/scalableminds/webknossos/issues/7229. + additionalCoordinates: null, }); function* mapAndFilterTreeData( diff --git a/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segment_list_item.tsx b/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segment_list_item.tsx index c83a475d3c3..86c5a977a51 100644 --- a/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segment_list_item.tsx +++ b/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segment_list_item.tsx @@ -30,6 +30,7 @@ import { V4 } from "libs/mjs"; import { ChangeColorMenuItemContent } from "components/color_picker"; import { MenuItemType } from "antd/lib/menu/hooks/useItems"; import { withMappingActivationConfirmation } from "./segments_view_helper"; +import { type AdditionalCoordinate } from "types/api_flow_types"; function ColoredDotIconForSegment({ segmentColorHSLA }: { segmentColorHSLA: Vector4 }) { const hslaCss = hslaToCSS(segmentColorHSLA); @@ -48,7 +49,12 @@ function ColoredDotIconForSegment({ segmentColorHSLA }: { segmentColorHSLA: Vect const getLoadPrecomputedMeshMenuItem = ( segment: Segment, currentMeshFile: APIMeshFile | null | undefined, - loadPrecomputedMesh: (arg0: number, arg1: Vector3, arg2: string) => void, + loadPrecomputedMesh: ( + segmentId: number, + seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, + meshFileName: string, + ) => void, andCloseContextMenu: (_ignore?: any) => void, layerName: string | null | undefined, mappingInfo: ActiveMappingInfo, @@ -72,7 +78,12 @@ const getLoadPrecomputedMeshMenuItem = ( return; } andCloseContextMenu( - loadPrecomputedMesh(segment.id, segment.somePosition, currentMeshFile?.meshFileName), + loadPrecomputedMesh( + segment.id, + segment.somePosition, + segment.someAdditionalCoordinates, + currentMeshFile?.meshFileName, + ), ); }, mappingName, @@ -97,7 +108,11 @@ const getLoadPrecomputedMeshMenuItem = ( const getComputeMeshAdHocMenuItem = ( segment: Segment, - loadAdHocMesh: (arg0: number, arg1: Vector3) => void, + loadAdHocMesh: ( + segmentId: number, + seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, + ) => void, isSegmentationLayerVisible: boolean, andCloseContextMenu: (_ignore?: any) => void, ): MenuItemType => { @@ -115,7 +130,9 @@ const getComputeMeshAdHocMenuItem = ( return; } - andCloseContextMenu(loadAdHocMesh(segment.id, segment.somePosition)); + andCloseContextMenu( + loadAdHocMesh(segment.id, segment.somePosition, segment.someAdditionalCoordinates), + ); }, disabled, label: Compute Mesh (ad hoc), @@ -124,7 +141,11 @@ const getComputeMeshAdHocMenuItem = ( const getMakeSegmentActiveMenuItem = ( segment: Segment, - setActiveCell: (arg0: number, somePosition?: Vector3) => void, + setActiveCell: ( + arg0: number, + somePosition?: Vector3, + someAdditionalCoordinates?: AdditionalCoordinate[], + ) => void, activeCellId: number | null | undefined, andCloseContextMenu: (_ignore?: any) => void, ): MenuItemType => { @@ -134,7 +155,14 @@ const getMakeSegmentActiveMenuItem = ( : "Make this the active segment ID."; return { key: "setActiveCell", - onClick: () => andCloseContextMenu(setActiveCell(segment.id, segment.somePosition)), + onClick: () => + andCloseContextMenu( + setActiveCell( + segment.id, + segment.somePosition, + segment.someAdditionalCoordinates || undefined, + ), + ), disabled, label: Activate Segment ID, }; @@ -162,11 +190,25 @@ type Props = { removeSegment: (arg0: number, arg2: string) => void; onSelectSegment: (arg0: Segment) => void; visibleSegmentationLayer: APISegmentationLayer | null | undefined; - loadAdHocMesh: (arg0: number, arg1: Vector3) => void; - loadPrecomputedMesh: (arg0: number, arg1: Vector3, arg2: string) => void; - setActiveCell: (arg0: number, somePosition?: Vector3) => void; + loadAdHocMesh: ( + segmentId: number, + somePosition: Vector3, + someAdditionalCoordinates: AdditionalCoordinate[] | undefined, + ) => void; + loadPrecomputedMesh: ( + segmentId: number, + seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, + meshFileName: string, + ) => void; + setActiveCell: ( + arg0: number, + somePosition?: Vector3, + someAdditionalCoordinates?: AdditionalCoordinate[], + ) => void; isosurface: IsosurfaceInformation | null | undefined; setPosition: (arg0: Vector3) => void; + setAdditionalCoordinates: (additionalCoordinates: AdditionalCoordinate[] | undefined) => void; currentMeshFile: APIMeshFile | null | undefined; onRenameStart: () => void; onRenameEnd: () => void; @@ -181,6 +223,7 @@ function _MeshInfoItem(props: { handleSegmentDropdownMenuVisibility: (arg0: boolean, arg1: number) => void; visibleSegmentationLayer: APISegmentationLayer | null | undefined; setPosition: (arg0: Vector3) => void; + setAdditionalCoordinates: (additionalCoordinates: AdditionalCoordinate[] | undefined) => void; }) { const dispatch = useDispatch(); @@ -209,7 +252,8 @@ function _MeshInfoItem(props: { return null; } - const { seedPosition, isLoading, isPrecomputed, isVisible } = isosurface; + const { seedPosition, seedAdditionalCoordinates, isLoading, isPrecomputed, isVisible } = + isosurface; const className = isVisible ? "" : "deemphasized italic"; const downloadButton = ( @@ -286,6 +330,9 @@ function _MeshInfoItem(props: { className={className} onClick={() => { props.setPosition(seedPosition); + if (seedAdditionalCoordinates) { + props.setAdditionalCoordinates(seedAdditionalCoordinates); + } }} style={{ marginLeft: 8 }} > @@ -330,6 +377,7 @@ function _SegmentListItem({ setActiveCell, isosurface, setPosition, + setAdditionalCoordinates, loadPrecomputedMesh, currentMeshFile, onRenameStart, @@ -550,6 +598,7 @@ function _SegmentListItem({ handleSegmentDropdownMenuVisibility={handleSegmentDropdownMenuVisibility} visibleSegmentationLayer={visibleSegmentationLayer} setPosition={setPosition} + setAdditionalCoordinates={setAdditionalCoordinates} /> diff --git a/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segments_view.tsx b/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segments_view.tsx index a72bec155cc..8471d6b35a2 100644 --- a/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segments_view.tsx +++ b/frontend/javascripts/oxalis/view/right-border-tabs/segments_tab/segments_view.tsx @@ -59,7 +59,10 @@ import { updateCurrentMeshFileAction, updateIsosurfaceVisibilityAction, } from "oxalis/model/actions/annotation_actions"; -import { setPositionAction } from "oxalis/model/actions/flycam_actions"; +import { + setAdditionalCoordinatesAction, + setPositionAction, +} from "oxalis/model/actions/flycam_actions"; import { loadAdHocMeshAction, loadPrecomputedMeshAction, @@ -107,6 +110,7 @@ import { ItemType } from "antd/lib/menu/hooks/useItems"; import { pluralize } from "libs/utils"; import AdvancedSearchPopover from "../advanced_search_popover"; import ButtonComponent from "oxalis/view/components/button_component"; +import { type AdditionalCoordinate } from "types/api_flow_types"; import { DataNode } from "antd/lib/tree"; const { confirm } = Modal; @@ -195,16 +199,31 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ dispatch(updateTemporarySettingAction("hoveredSegmentId", segmentId || null)); }, - loadAdHocMesh(segmentId: number, seedPosition: Vector3) { - dispatch(loadAdHocMeshAction(segmentId, seedPosition)); + loadAdHocMesh( + segmentId: number, + seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, + ) { + dispatch(loadAdHocMeshAction(segmentId, seedPosition, seedAdditionalCoordinates)); }, - loadPrecomputedMesh(segmentId: number, seedPosition: Vector3, meshFileName: string) { - dispatch(loadPrecomputedMeshAction(segmentId, seedPosition, meshFileName)); + loadPrecomputedMesh( + segmentId: number, + seedPosition: Vector3, + seedAdditionalCoordinates: AdditionalCoordinate[] | undefined, + meshFileName: string, + ) { + dispatch( + loadPrecomputedMeshAction(segmentId, seedPosition, seedAdditionalCoordinates, meshFileName), + ); }, - setActiveCell(segmentId: number, somePosition?: Vector3) { - dispatch(setActiveCellAction(segmentId, somePosition)); + setActiveCell( + segmentId: number, + somePosition?: Vector3, + someAdditionalCoordinates?: AdditionalCoordinate[], + ) { + dispatch(setActiveCellAction(segmentId, somePosition, someAdditionalCoordinates)); }, setCurrentMeshFile(layerName: string, fileName: string) { @@ -215,6 +234,10 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ dispatch(setPositionAction(position)); }, + setAdditionalCoordinates(additionalCoordinates: AdditionalCoordinate[] | undefined) { + dispatch(setAdditionalCoordinatesAction(additionalCoordinates || null)); + }, + updateSegments( segmentIds: number[], segmentShape: Partial, @@ -651,6 +674,9 @@ class SegmentsView extends React.Component { return; } this.props.setPosition(segment.somePosition); + if (segment.someAdditionalCoordinates) { + this.props.setAdditionalCoordinates(segment.someAdditionalCoordinates); + } }; closeSegmentOrGroupDropdown = () => { @@ -1255,7 +1281,7 @@ class SegmentsView extends React.Component { handleLoadMeshesAdHoc = (groupId: number | null) => { this.handlePerSegment(groupId, (segment) => { if (segment.somePosition == null) return; - this.props.loadAdHocMesh(segment.id, segment.somePosition); + this.props.loadAdHocMesh(segment.id, segment.somePosition, segment.someAdditionalCoordinates); }); }; @@ -1320,6 +1346,7 @@ class SegmentsView extends React.Component { this.props.loadPrecomputedMesh( segment.id, segment.somePosition, + segment.someAdditionalCoordinates, this.props.currentMeshFile.meshFileName, ); }); @@ -1562,6 +1589,7 @@ class SegmentsView extends React.Component { loadPrecomputedMesh={this.props.loadPrecomputedMesh} setActiveCell={this.props.setActiveCell} setPosition={this.props.setPosition} + setAdditionalCoordinates={this.props.setAdditionalCoordinates} currentMeshFile={this.props.currentMeshFile} onRenameStart={this.onRenameStart} onRenameEnd={this.onRenameEnd} diff --git a/frontend/javascripts/oxalis/view/statusbar.tsx b/frontend/javascripts/oxalis/view/statusbar.tsx index 4297f80608a..39ddc612585 100644 --- a/frontend/javascripts/oxalis/view/statusbar.tsx +++ b/frontend/javascripts/oxalis/view/statusbar.tsx @@ -37,6 +37,7 @@ import { import { getGlobalDataConnectionInfo } from "oxalis/model/data_connection_info"; import { useInterval } from "libs/react_helpers"; import _ from "lodash"; +import { AdditionalCoordinate } from "types/api_flow_types"; const lineColor = "rgba(255, 255, 255, 0.67)"; const moreIconStyle = { @@ -48,8 +49,12 @@ const moreLinkStyle = { marginRight: "auto", }; -function getPosString(pos: Vector3) { - return V3.floor(pos).join(","); +function getPosString( + pos: Vector3, + optAdditionalCoordinates: AdditionalCoordinate[] | null | undefined, +) { + const additionalCoordinates = (optAdditionalCoordinates || []).map((coord) => coord.value); + return V3.floor(pos).concat(additionalCoordinates).join(","); } function ZoomShortcut() { @@ -490,6 +495,9 @@ function SegmentAndMousePosition() { const mousePosition = useSelector( (state: OxalisState) => state.temporaryConfiguration.mousePosition, ); + const additionalCoordinates = useSelector( + (state: OxalisState) => state.flycam.additionalCoordinates, + ); const isPlaneMode = useSelector((state: OxalisState) => getIsPlaneMode(state)); const globalMousePosition = useSelector((state: OxalisState) => { const { activeViewport } = state.viewModeData.plane; @@ -509,7 +517,9 @@ function SegmentAndMousePosition() { {isPlaneMode && hasVisibleSegmentation ? getCellInfo(globalMousePosition) : null} {isPlaneMode ? ( - Pos [{globalMousePosition ? getPosString(globalMousePosition) : "-,-,-"}] + Pos [ + {globalMousePosition ? getPosString(globalMousePosition, additionalCoordinates) : "-,-,-"} + ] ) : null} diff --git a/frontend/javascripts/test/api/api_skeleton_latest.spec.ts b/frontend/javascripts/test/api/api_skeleton_latest.spec.ts index 1dab1019ff5..696f867472c 100644 --- a/frontend/javascripts/test/api/api_skeleton_latest.spec.ts +++ b/frontend/javascripts/test/api/api_skeleton_latest.spec.ts @@ -115,7 +115,7 @@ test("Data Api: getDataValue should get the data value for a layer, position and const cube = model.getCubeByLayerName("segmentation"); const position = [100, 100, 100]; const zoomStep = 0; - const bucketAddress = cube.positionToZoomedAddress(position, zoomStep); + const bucketAddress = cube.positionToZoomedAddress(position, null, zoomStep); const bucket = cube.getOrCreateBucket(bucketAddress); sinon.stub(cube.pullQueue, "pull").returns([Promise.resolve(true)]); sinon.stub(cube, "getDataValue").returns(1337); diff --git a/frontend/javascripts/test/api/api_v2.spec.ts b/frontend/javascripts/test/api/api_v2.spec.ts index dcc7ca99016..7dc277b5533 100644 --- a/frontend/javascripts/test/api/api_v2.spec.ts +++ b/frontend/javascripts/test/api/api_v2.spec.ts @@ -93,7 +93,7 @@ test("getDataValue should get the data value for a layer, position and zoomstep" const cube = model.getCubeByLayerName("segmentation"); const position = [100, 100, 100]; const zoomStep = 0; - const bucketAddress = cube.positionToZoomedAddress(position, zoomStep); + const bucketAddress = cube.positionToZoomedAddress(position, null, zoomStep); const bucket = cube.getOrCreateBucket(bucketAddress); sinon.stub(cube.pullQueue, "pull").returns([Promise.resolve(true)]); sinon.stub(cube, "getDataValue").returns(1337); diff --git a/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts b/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts index fbd9c578530..6b81102d98d 100644 --- a/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts +++ b/frontend/javascripts/test/backend-snapshot-tests/annotations.e2e.ts @@ -190,8 +190,8 @@ test.serial("Send update actions and compare resulting tracing", async (t) => { const saveQueue = addVersionNumbers( createSaveQueueFromUpdateActions( [ - [UpdateActions.updateSkeletonTracing(initialSkeleton, [1, 2, 3], [0, 1, 2], 1)], - [UpdateActions.updateSkeletonTracing(initialSkeleton, [2, 3, 4], [1, 2, 3], 2)], + [UpdateActions.updateSkeletonTracing(initialSkeleton, [1, 2, 3], null, [0, 1, 2], 1)], + [UpdateActions.updateSkeletonTracing(initialSkeleton, [2, 3, 4], null, [1, 2, 3], 2)], ], 123456789, ), diff --git a/frontend/javascripts/test/controller/url_manager.spec.ts b/frontend/javascripts/test/controller/url_manager.spec.ts index d8d0e74ed95..fa43226dd66 100644 --- a/frontend/javascripts/test/controller/url_manager.spec.ts +++ b/frontend/javascripts/test/controller/url_manager.spec.ts @@ -125,6 +125,7 @@ test("UrlManager should parse url hash with comment links", (t) => { test("UrlManager should parse json url hash", (t) => { const state = { position: [555, 278, 482], + additionalCoordinates: [], mode: "flight", zoomStep: 2.0, rotation: [40.45, 13.65, 0.8], @@ -137,6 +138,7 @@ test("UrlManager should parse json url hash", (t) => { test("UrlManager should parse incomplete json url hash", (t) => { const state = { position: [555, 278, 482], + additionalCoordinates: [], mode: "flight", zoomStep: 2.0, rotation: [40.45, 13.65, 0.8], @@ -156,6 +158,7 @@ test("UrlManager should build json url hash and parse it again", (t) => { const mode = Constants.MODE_ARBITRARY; const urlState = { position: [0, 0, 0], + additionalCoordinates: [], mode, zoomStep: 1.3, rotation: [0, 0, 180], diff --git a/frontend/javascripts/test/fixtures/skeletontracing_server_objects.ts b/frontend/javascripts/test/fixtures/skeletontracing_server_objects.ts index a670d92ace6..8c7f37df098 100644 --- a/frontend/javascripts/test/fixtures/skeletontracing_server_objects.ts +++ b/frontend/javascripts/test/fixtures/skeletontracing_server_objects.ts @@ -14,6 +14,7 @@ export const tracing: ServerSkeletonTracing = { y: 22, z: 0, }, + additionalCoordinates: [], rotation: { x: 0, y: 0, @@ -60,6 +61,7 @@ export const tracing: ServerSkeletonTracing = { y: 32, z: 0, }, + additionalCoordinates: [], rotation: { x: 0, y: 0, @@ -79,6 +81,7 @@ export const tracing: ServerSkeletonTracing = { y: 106, z: 0, }, + additionalCoordinates: [], rotation: { x: 0, y: 0, @@ -141,11 +144,13 @@ export const tracing: ServerSkeletonTracing = { y: 32, z: 0, }, + editPositionAdditionalCoordinates: null, editRotation: { x: 79.99999570976581, y: 73.99999869555745, z: 4.908922051072295e-7, }, + additionalAxes: [], zoomLevel: 2, version: 7, }; diff --git a/frontend/javascripts/test/fixtures/tasktracing_server_objects.ts b/frontend/javascripts/test/fixtures/tasktracing_server_objects.ts index ccd86022509..a6cf3dad2c1 100644 --- a/frontend/javascripts/test/fixtures/tasktracing_server_objects.ts +++ b/frontend/javascripts/test/fixtures/tasktracing_server_objects.ts @@ -1,4 +1,5 @@ import type { ServerSkeletonTracing, APIAnnotation } from "types/api_flow_types"; + export const tracing: ServerSkeletonTracing = { typ: "Skeleton", trees: [ @@ -11,6 +12,7 @@ export const tracing: ServerSkeletonTracing = { y: 0, z: 0, }, + additionalCoordinates: [], rotation: { x: 0, y: 0, @@ -48,11 +50,13 @@ export const tracing: ServerSkeletonTracing = { y: 0, z: 0, }, + editPositionAdditionalCoordinates: null, editRotation: { x: 0, y: 0, z: 0, }, + additionalAxes: [], zoomLevel: 2, version: 0, id: "e90133de-b2db-4912-8261-8b6f84f7edab", diff --git a/frontend/javascripts/test/fixtures/volumetracing_server_objects.ts b/frontend/javascripts/test/fixtures/volumetracing_server_objects.ts index ad82c619fe8..15b02a94425 100644 --- a/frontend/javascripts/test/fixtures/volumetracing_server_objects.ts +++ b/frontend/javascripts/test/fixtures/volumetracing_server_objects.ts @@ -21,11 +21,13 @@ export const tracing: ServerVolumeTracing = { y: 4282, z: 2496, }, + editPositionAdditionalCoordinates: null, editRotation: { x: 0, y: 0, z: 0, }, + additionalAxes: [], elementClass: "uint16", id: "segmentation", largestSegmentId: 21890, diff --git a/frontend/javascripts/test/geometries/skeleton.spec.ts b/frontend/javascripts/test/geometries/skeleton.spec.ts index 21896a3d1ef..b903832e985 100644 --- a/frontend/javascripts/test/geometries/skeleton.spec.ts +++ b/frontend/javascripts/test/geometries/skeleton.spec.ts @@ -1,5 +1,7 @@ // Integration tests for skeleton.js import "test/mocks/lz4"; +// Ensure singletons are set up +import "test/helpers/apiHelpers"; import _ from "lodash"; import { getSkeletonTracing } from "oxalis/model/accessors/skeletontracing_accessor"; import * as Utils from "libs/utils"; @@ -44,7 +46,7 @@ test.before((t) => { Store.dispatch(createTreeAction()); } - Store.dispatch(createNodeAction([i, i, i], rotation, viewport, resolution)); + Store.dispatch(createNodeAction([i, i, i], null, rotation, viewport, resolution)); } getSkeletonTracing(Store.getState().tracing).map((skeletonTracing) => { @@ -128,7 +130,7 @@ test.serial("Skeleton should initialize correctly using the store's state", (t) }); test.serial("Skeleton should increase its buffers once the max capacity is reached", async (t) => { const skeleton = skeletonCreator(); - Store.dispatch(createNodeAction([2001, 2001, 2001], [0.5, 0.5, 0.5], 0, 0)); + Store.dispatch(createNodeAction([2001, 2001, 2001], null, [0.5, 0.5, 0.5], 0, 0)); await Utils.sleep(100); t.is(skeleton.nodes.buffers.length, 2); t.is(skeleton.edges.buffers.length, 2); diff --git a/frontend/javascripts/test/libs/nml.spec.ts b/frontend/javascripts/test/libs/nml.spec.ts index 716535d461f..54129b933b8 100644 --- a/frontend/javascripts/test/libs/nml.spec.ts +++ b/frontend/javascripts/test/libs/nml.spec.ts @@ -32,6 +32,7 @@ const createDummyNode = (id: number): Node => ({ bitDepth: 8, id, position: [id, id, id], + additionalCoordinates: [], radius: id, resolution: 10, rotation: [id, id, id], @@ -161,6 +162,7 @@ const initialSkeletonTracing: SkeletonTracing = { activeIndex: -1, }, showSkeletons: true, + additionalAxes: [], }; const initialState: OxalisState = _.extend({}, defaultState, { @@ -291,6 +293,40 @@ test("NML serializing and parsing should yield the same state even when using mu t.deepEqual(skeletonTracing.trees, trees); t.deepEqual(skeletonTracing.treeGroups, treeGroups); }); +test("NML serializing and parsing should yield the same state even when additional coordinates exist", async (t) => { + const existingNodeMap = initialState.tracing.skeleton?.trees[1].nodes; + if (existingNodeMap == null) { + throw new Error("Unexpected null value."); + } + const existingNode = existingNodeMap.get(1); + const newNodeMap = existingNodeMap.set(1, { + ...existingNode, + additionalCoordinates: [{ name: "t", value: 123 }], + }); + const state = update(initialState, { + tracing: { + skeleton: { + trees: { + "1": { + nodes: { + $set: newNodeMap, + }, + }, + }, + }, + }, + }); + const serializedNml = serializeToNml( + state, + state.tracing, + enforceSkeletonTracing(state.tracing), + BUILD_INFO, + ); + const { trees, treeGroups } = await parseNml(serializedNml); + const skeletonTracing = enforceSkeletonTracing(state.tracing); + t.deepEqual(skeletonTracing.trees, trees); + t.deepEqual(skeletonTracing.treeGroups, treeGroups); +}); test("NML Serializer should only serialize visible trees", async (t) => { const state = update(initialState, { tracing: { @@ -355,6 +391,50 @@ test("NML serializer should produce correct NMLs", (t) => { id: "nml", }); }); +test("NML serializer should produce correct NMLs with additional coordinates", (t) => { + let adaptedState = update(initialState, { + tracing: { + skeleton: { + additionalAxes: { + $set: [{ name: "t", bounds: [0, 100], index: 0 }], + }, + }, + }, + }); + + const existingNodeMap = adaptedState.tracing.skeleton?.trees[1].nodes; + if (existingNodeMap == null) { + throw new Error("Unexpected null value."); + } + const existingNode = existingNodeMap.get(1); + const newNodeMap = existingNodeMap.set(1, { + ...existingNode, + additionalCoordinates: [{ name: "t", value: 123 }], + }); + adaptedState = update(adaptedState, { + tracing: { + skeleton: { + trees: { + "1": { + nodes: { + $set: newNodeMap, + }, + }, + }, + }, + }, + }); + + const serializedNml = serializeToNml( + adaptedState, + adaptedState.tracing, + enforceSkeletonTracing(adaptedState.tracing), + BUILD_INFO, + ); + t.snapshot(serializedNml, { + id: "nml-with-additional-coordinates", + }); +}); test("NML serializer should escape special characters and multilines", (t) => { const state = update(initialState, { tracing: { diff --git a/frontend/javascripts/test/model/binary/cube.spec.ts b/frontend/javascripts/test/model/binary/cube.spec.ts index 381ba215afa..840cbc307e4 100644 --- a/frontend/javascripts/test/model/binary/cube.spec.ts +++ b/frontend/javascripts/test/model/binary/cube.spec.ts @@ -10,8 +10,9 @@ import runAsync from "test/helpers/run-async"; import sinon from "sinon"; import { ResolutionInfo } from "oxalis/model/helpers/resolution_info"; import type { Vector3, Vector4 } from "oxalis/constants"; -import { DataBucket } from "oxalis/model/bucket_data_handling/bucket"; +import { assertNonNullBucket, DataBucket } from "oxalis/model/bucket_data_handling/bucket"; import BoundingBox from "oxalis/model/bucket_data_handling/bounding_box"; +import DataCubeType from "oxalis/model/bucket_data_handling/data_cube"; const StoreMock = { getState: () => ({ @@ -47,11 +48,13 @@ mockRequire("libs/toast", { error: _.noop, }); // Avoid node caching and make sure all mockRequires are applied -const DataCube = mockRequire.reRequire("oxalis/model/bucket_data_handling/data_cube").default; +const DataCube: typeof DataCubeType = mockRequire.reRequire( + "oxalis/model/bucket_data_handling/data_cube", +).default; // Ava's recommendation for Flow types // https://github.com/avajs/ava/blob/master/docs/recipes/flow.md#typing-tcontext const test: TestInterface<{ - cube: typeof DataCube; + cube: DataCubeType; pullQueue: Record; pushQueue: Record; }> = anyTest as any; @@ -69,9 +72,11 @@ test.beforeEach((t) => { const resolutionInfo = new ResolutionInfo(mockedLayer.resolutions); const cube = new DataCube( new BoundingBox({ min: [0, 0, 0], max: [100, 100, 100] }), + [], resolutionInfo, "uint32", false, + "layerName", ); class PullQueueMock { @@ -108,7 +113,7 @@ test.beforeEach((t) => { insert: sinon.stub(), push: sinon.stub(), }; - cube.initializeWithQueues(pullQueue, pushQueue); + cube.initializeWithQueues(pullQueue as any, pushQueue as any); t.context = { cube, pullQueue, @@ -117,34 +122,34 @@ test.beforeEach((t) => { }); test("GetBucket should return a NullBucket on getBucket()", (t) => { const { cube } = t.context; - const bucket = cube.getBucket([0, 0, 0, 0]); + const bucket = cube.getBucket([0, 0, 0, 0, []]); t.is(bucket.type, "null"); t.is(cube.buckets.length, 0); }); test("GetBucket should create a new bucket on getOrCreateBucket()", (t) => { const { cube } = t.context; t.is(cube.buckets.length, 0); - const bucket = cube.getOrCreateBucket([0, 0, 0, 0]); + const bucket = cube.getOrCreateBucket([0, 0, 0, 0, []]); t.is(bucket.type, "data"); t.is(cube.buckets.length, 1); }); test("GetBucket should only create one bucket on getOrCreateBucket()", (t) => { const { cube } = t.context; - const bucket1 = cube.getOrCreateBucket([0, 0, 0, 0]); - const bucket2 = cube.getOrCreateBucket([0, 0, 0, 0]); + const bucket1 = cube.getOrCreateBucket([0, 0, 0, 0, []]); + const bucket2 = cube.getOrCreateBucket([0, 0, 0, 0, []]); t.is(bucket1, bucket2); t.is(cube.buckets.length, 1); }); test("Voxel Labeling should request buckets when temporal buckets are created", (t) => { const { cube, pullQueue } = t.context; - cube._labelVoxelInResolution_DEPRECATED([1, 1, 1], 42, 0); + cube._labelVoxelInResolution_DEPRECATED([1, 1, 1], null, 42, 0, null); t.plan(1); return runAsync([ () => { t.deepEqual(pullQueue.processedQueue[0], { - bucket: [0, 0, 0, 0], + bucket: [0, 0, 0, 0, []], priority: -1, }); }, @@ -152,9 +157,9 @@ test("Voxel Labeling should request buckets when temporal buckets are created", }); test("Voxel Labeling should push buckets after they were pulled", async (t) => { const { cube, pushQueue } = t.context; - await cube._labelVoxelInResolution_DEPRECATED([1, 1, 1], 42, 0); + await cube._labelVoxelInResolution_DEPRECATED([1, 1, 1], null, 42, 0, null); t.plan(1); - const bucket = cube.getBucket([0, 0, 0, 0]); + const bucket = cube.getBucket([0, 0, 0, 0, []]); return runAsync([ () => { t.true(pushQueue.insert.calledWith(bucket)); @@ -163,10 +168,11 @@ test("Voxel Labeling should push buckets after they were pulled", async (t) => { }); test("Voxel Labeling should push buckets immediately if they are pulled already", async (t) => { const { cube, pushQueue } = t.context; - const bucket = cube.getOrCreateBucket([0, 0, 0, 0]); + const bucket = cube.getOrCreateBucket([0, 0, 0, 0, []]); + assertNonNullBucket(bucket); bucket.markAsPulled(); bucket.receiveData(new Uint8Array(4 * 32 ** 3)); - await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], 42, 0); + await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], null, 42, 0, null); t.plan(1); return runAsync([ () => { @@ -177,32 +183,32 @@ test("Voxel Labeling should push buckets immediately if they are pulled already" test("Voxel Labeling should only instantiate one bucket when labelling the same bucket twice", async (t) => { const { cube } = t.context; // Creates bucket - await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], 42, 0); + await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], null, 42, 0, null); // Uses existing bucket - await cube._labelVoxelInResolution_DEPRECATED([1, 0, 0], 43, 0); - const data = cube.getBucket([0, 0, 0, 0]).getData(); + await cube._labelVoxelInResolution_DEPRECATED([1, 0, 0], null, 43, 0, null); + const data = cube.getBucket([0, 0, 0, 0, []]).getData(); t.is(data[0], 42); t.is(data[1], 43); }); test("getDataValue() should return the raw value without a mapping", async (t) => { const { cube } = t.context; const value = 1 * (1 << 16) + 2 * (1 << 8) + 3; - await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], value, 0); - t.is(cube.getDataValue([0, 0, 0]), value); + await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], null, value, 0, null); + t.is(cube.getDataValue([0, 0, 0], null, null), value); }); test("getDataValue() should return the mapping value if available", async (t) => { const { cube } = t.context; - await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], 42, 0); - await cube._labelVoxelInResolution_DEPRECATED([1, 1, 1], 43, 0); + await cube._labelVoxelInResolution_DEPRECATED([0, 0, 0], null, 42, 0, null); + await cube._labelVoxelInResolution_DEPRECATED([1, 1, 1], null, 43, 0, null); const mapping = []; mapping[42] = 1; - t.is(cube.getDataValue([0, 0, 0], mapping), 1); - t.is(cube.getDataValue([1, 1, 1], mapping), 43); + t.is(cube.getDataValue([0, 0, 0], null, mapping), 1); + t.is(cube.getDataValue([1, 1, 1], null, mapping), 43); }); test("Garbage Collection should only keep 3 buckets when possible", (t) => { const { cube } = t.context; cube.BUCKET_COUNT_SOFT_LIMIT = 3; - cube.getOrCreateBucket([0, 0, 0, 0]); + cube.getOrCreateBucket([0, 0, 0, 0, []]); cube.getOrCreateBucket([1, 1, 1, 0]); cube.getOrCreateBucket([2, 2, 2, 0]); cube.getOrCreateBucket([3, 3, 3, 0]); @@ -211,7 +217,8 @@ test("Garbage Collection should only keep 3 buckets when possible", (t) => { test("Garbage Collection should not collect buckets with shouldCollect() == false", (t) => { const { cube } = t.context; cube.BUCKET_COUNT_SOFT_LIMIT = 3; - const b1 = cube.getOrCreateBucket([0, 0, 0, 0]); + const b1 = cube.getOrCreateBucket([0, 0, 0, 0, []]); + assertNonNullBucket(b1); b1.markAsPulled(); cube.getOrCreateBucket([1, 1, 1, 0]); cube.getOrCreateBucket([2, 2, 2, 0]); @@ -219,7 +226,7 @@ test("Garbage Collection should not collect buckets with shouldCollect() == fals t.is(b1.shouldCollect(), false); const addresses = cube.buckets.map((b: DataBucket) => b.zoomedAddress); t.deepEqual(addresses, [ - [0, 0, 0, 0], + [0, 0, 0, 0, []], [3, 3, 3, 0], [2, 2, 2, 0], ]); @@ -227,16 +234,19 @@ test("Garbage Collection should not collect buckets with shouldCollect() == fals test("Garbage Collection should grow beyond soft limit if necessary", (t) => { const { cube } = t.context; cube.BUCKET_COUNT_SOFT_LIMIT = 3; - const b1 = cube.getOrCreateBucket([0, 0, 0, 0]); + const b1 = cube.getOrCreateBucket([0, 0, 0, 0, []]); const b2 = cube.getOrCreateBucket([1, 1, 1, 0]); const b3 = cube.getOrCreateBucket([2, 2, 2, 0]); // No bucket may be collected. - [b1, b2, b3].map((b) => b.markAsPulled()); + [b1, b2, b3].map((b) => { + assertNonNullBucket(b); + b.markAsPulled(); + }); // Allocate a 4th one which should still be possible (will exceed BUCKET_COUNT_SOFT_LIMIT) cube.getOrCreateBucket([3, 3, 3, 0]); const addresses = cube.buckets.map((b: DataBucket) => b.zoomedAddress); t.deepEqual(addresses, [ - [0, 0, 0, 0], + [0, 0, 0, 0, []], [1, 1, 1, 0], [2, 2, 2, 0], [3, 3, 3, 0], diff --git a/frontend/javascripts/test/model/binary/layers/wkstore_adapter.spec.ts b/frontend/javascripts/test/model/binary/layers/wkstore_adapter.spec.ts index badbdfbe361..9a9990d5867 100644 --- a/frontend/javascripts/test/model/binary/layers/wkstore_adapter.spec.ts +++ b/frontend/javascripts/test/model/binary/layers/wkstore_adapter.spec.ts @@ -161,12 +161,14 @@ function createExpectedOptions(fourBit: boolean = false) { data: [ { position: [0, 0, 0], + additionalCoordinates: undefined, mag: [1, 1, 1], cubeSize: 32, fourBit, }, { position: [64, 64, 64], + additionalCoordinates: undefined, mag: [2, 2, 2], cubeSize: 32, fourBit, @@ -247,6 +249,7 @@ test.serial("sendToStore: Request Handling should send the correct request param name: "updateBucket", value: { position: [0, 0, 0], + additionalCoordinates: undefined, mag: [1, 1, 1], cubeSize: 32, base64Data: byteArraysToLz4Base64([data])[0], @@ -256,6 +259,7 @@ test.serial("sendToStore: Request Handling should send the correct request param name: "updateBucket", value: { position: [64, 64, 64], + additionalCoordinates: undefined, mag: [2, 2, 2], cubeSize: 32, base64Data: byteArraysToLz4Base64([data])[0], diff --git a/frontend/javascripts/test/model/volumetracing/volume_annotation_sampling.spec.ts b/frontend/javascripts/test/model/volumetracing/volume_annotation_sampling.spec.ts index 81233fbe038..9130c8c4917 100644 --- a/frontend/javascripts/test/model/volumetracing/volume_annotation_sampling.spec.ts +++ b/frontend/javascripts/test/model/volumetracing/volume_annotation_sampling.spec.ts @@ -10,6 +10,8 @@ import mockRequire from "mock-require"; import sinon from "sinon"; import { ResolutionInfo } from "oxalis/model/helpers/resolution_info"; import BoundingBox from "oxalis/model/bucket_data_handling/bounding_box"; +import DataCubeType from "oxalis/model/bucket_data_handling/data_cube"; +import { assertNonNullBucket } from "oxalis/model/bucket_data_handling/bucket"; const StoreMock = { getState: () => ({ @@ -31,14 +33,16 @@ mockRequire("oxalis/model/sagas/root_saga", function* () { }); type LabeledVoxelsMapAsArray = Array<[Vector4, Uint8Array]>; // Avoid node caching and make sure all mockRequires are applied -const DataCube = mockRequire.reRequire("oxalis/model/bucket_data_handling/data_cube").default; +const DataCube: typeof DataCubeType = mockRequire.reRequire( + "oxalis/model/bucket_data_handling/data_cube", +).default; const { default: sampleVoxelMapToResolution, applyVoxelMap } = mockRequire.reRequire( "oxalis/model/volumetracing/volume_annotation_sampling", ); // Ava's recommendation for Flow types // https://github.com/avajs/ava/blob/master/docs/recipes/flow.md#typing-tcontext const test: TestInterface<{ - cube: typeof DataCube; + cube: DataCubeType; }> = anyTest as any; test.beforeEach((t) => { const mockedLayer = { @@ -54,9 +58,11 @@ test.beforeEach((t) => { const resolutionInfo = new ResolutionInfo(mockedLayer.resolutions); const cube = new DataCube( new BoundingBox({ min: [0, 0, 0], max: [1024, 1024, 1024] }), + [], resolutionInfo, "uint32", false, + "layerName", ); const pullQueue = { add: sinon.stub(), @@ -66,6 +72,7 @@ test.beforeEach((t) => { insert: sinon.stub(), push: sinon.stub(), }; + // @ts-expect-error cube.initializeWithQueues(pullQueue, pushQueue); t.context = { cube, @@ -115,6 +122,7 @@ test("Upsampling an annotation should work in the top left part of a bucket", (t [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, goalVoxelMap)); const bucket = cube.getOrCreateBucket([0, 0, 0, 1]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map([[bucket.zoomedAddress, sourceVoxelMap]]); const upsampledVoxelMapPerBucket = sampleVoxelMapToResolution( labeledVoxelsMap, @@ -131,7 +139,7 @@ test("Upsampling an annotation should work in the top left part of a bucket", (t const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [0, 0, 0, 0], + [0, 0, 0, 0, []], "The bucket of the upsampled map should be correct.", ); @@ -176,6 +184,7 @@ test("Upsampling an annotation should work in the top right part of a bucket", ( [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, goalVoxelMap)); const bucket = cube.getOrCreateBucket([0, 0, 0, 1]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map([[bucket.zoomedAddress, sourceVoxelMap]]); const upsampledVoxelMapPerBucket = sampleVoxelMapToResolution( labeledVoxelsMap, @@ -192,7 +201,7 @@ test("Upsampling an annotation should work in the top right part of a bucket", ( const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [1, 0, 0, 0], + [1, 0, 0, 0, []], "The bucket of the upsampled map should be correct.", ); @@ -237,6 +246,7 @@ test("Upsampling an annotation should work in the bottom left part of a bucket", [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, goalVoxelMap)); const bucket = cube.getOrCreateBucket([0, 0, 0, 1]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map([[bucket.zoomedAddress, sourceVoxelMap]]); const upsampledVoxelMapPerBucket = sampleVoxelMapToResolution( labeledVoxelsMap, @@ -253,7 +263,7 @@ test("Upsampling an annotation should work in the bottom left part of a bucket", const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [0, 1, 0, 0], + [0, 1, 0, 0, []], "The bucket of the upsampled map should be correct.", ); @@ -298,6 +308,7 @@ test("Upsampling an annotation should work in the bottom right part of a bucket" [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, goalVoxelMap)); const bucket = cube.getOrCreateBucket([0, 0, 0, 1]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map([[bucket.zoomedAddress, sourceVoxelMap]]); const upsampledVoxelMapPerBucket = sampleVoxelMapToResolution( labeledVoxelsMap, @@ -314,7 +325,7 @@ test("Upsampling an annotation should work in the bottom right part of a bucket" const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [1, 1, 0, 0], + [1, 1, 0, 0, []], "The bucket of the upsampled map should be correct.", ); @@ -359,6 +370,7 @@ test("Upsampling an annotation where the annotation slice is in the lower part o [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, goalVoxelMap)); const bucket = cube.getOrCreateBucket([0, 0, 0, 1]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map([[bucket.zoomedAddress, sourceVoxelMap]]); const upsampledVoxelMapPerBucket = sampleVoxelMapToResolution( labeledVoxelsMap, @@ -375,7 +387,7 @@ test("Upsampling an annotation where the annotation slice is in the lower part o const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [0, 0, 1, 0], + [0, 0, 1, 0, []], "The bucket of the upsampled map should be correct.", ); @@ -408,6 +420,7 @@ test("Upsampling an annotation should work across more than one resolution", (t) } const bucket = cube.getOrCreateBucket([0, 0, 0, 2]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map([[bucket.zoomedAddress, sourceVoxelMap]]); const upsampledVoxelMapPerBucket = sampleVoxelMapToResolution( labeledVoxelsMap, @@ -424,7 +437,7 @@ test("Upsampling an annotation should work across more than one resolution", (t) const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [1, 1, 0, 0], + [1, 1, 0, 0, []], "The bucket of the upsampled map should be correct.", ); @@ -441,12 +454,14 @@ test("Upsampling an annotation should work across more than one resolution", (t) test("Downsampling annotation of neighbour buckets should result in one downsampled voxelMap", (t) => { const { cube } = t.context; const labeledVoxelsMap = new Map(); - [ - [0, 0, 0], - [1, 0, 0], - [0, 1, 0], - [1, 1, 0], - ].forEach((zoomedAddress) => { + ( + [ + [0, 0, 0], + [1, 0, 0], + [0, 1, 0], + [1, 1, 0], + ] as Vector3[] + ).forEach((zoomedAddress) => { const voxelMap = getEmptyVoxelMap(); [ [10, 10], @@ -469,6 +484,7 @@ test("Downsampling annotation of neighbour buckets should result in one downsamp [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, voxelMap)); const bucket = cube.getOrCreateBucket([...zoomedAddress, 0]); + assertNonNullBucket(bucket); labeledVoxelsMap.set(bucket.zoomedAddress, voxelMap); }); const goalVoxelMap = getEmptyVoxelMap(); @@ -502,7 +518,7 @@ test("Downsampling annotation of neighbour buckets should result in one downsamp const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [0, 0, 0, 1], + [0, 0, 0, 1, []], "The bucket of the downsampled map should be correct.", ); @@ -523,12 +539,14 @@ test("Downsampling annotation of neighbour buckets should result in one downsamp test("Downsampling annotation should work across more than one resolution", (t) => { const { cube } = t.context; const labeledVoxelsMap = new Map(); - [ - [0, 0, 0], - [1, 0, 0], - [0, 1, 0], - [1, 1, 0], - ].forEach((zoomedAddress) => { + ( + [ + [0, 0, 0], + [1, 0, 0], + [0, 1, 0], + [1, 1, 0], + ] as Vector3[] + ).forEach((zoomedAddress) => { const voxelMap = getEmptyVoxelMap(); [ [10, 10], @@ -551,6 +569,7 @@ test("Downsampling annotation should work across more than one resolution", (t) [13, 13], ].forEach(([firstDim, secondDim]) => labelVoxelInVoxelMap(firstDim, secondDim, voxelMap)); const bucket = cube.getOrCreateBucket([...zoomedAddress, 0]); + assertNonNullBucket(bucket); labeledVoxelsMap.set(bucket.zoomedAddress, voxelMap); }); const goalVoxelMap = getEmptyVoxelMap(); @@ -584,7 +603,7 @@ test("Downsampling annotation should work across more than one resolution", (t) const upsampledVoxelMap = upsampledVoxelMapAsArray[0][1]; t.deepEqual( bucketZoomedAddress, - [0, 0, 0, 2], + [0, 0, 0, 2, []], "The bucket of the downsampled map should be correct.", ); @@ -605,6 +624,7 @@ test("Downsampling annotation should work across more than one resolution", (t) test("A labeledVoxelMap should be applied correctly", (t) => { const { cube } = t.context; const bucket = cube.getOrCreateBucket([0, 0, 0, 0]); + assertNonNullBucket(bucket); const labeledVoxelsMap = new Map(); const voxelMap = getEmptyVoxelMap(); const voxelsToLabel = [ diff --git a/frontend/javascripts/test/reducers/flycam_reducer.spec.ts b/frontend/javascripts/test/reducers/flycam_reducer.spec.ts index 08b7270be38..a05c23d36e8 100644 --- a/frontend/javascripts/test/reducers/flycam_reducer.spec.ts +++ b/frontend/javascripts/test/reducers/flycam_reducer.spec.ts @@ -1,6 +1,7 @@ // @ts-nocheck import { M4x4, V3 } from "libs/mjs"; import { OrthoViews } from "oxalis/constants"; +import update from "immutability-helper"; import { getPosition, getRotation, @@ -25,6 +26,7 @@ const initialState = { dataset: { dataSource: { scale: [1, 1, 2], + dataLayers: [{ name: "color", type: "color", additionalCoordinates: [] }], }, }, userConfiguration: { @@ -33,6 +35,7 @@ const initialState = { }, flycam: { zoomStep: 2, + additionalCoordinates: [], currentMatrix: M4x4.identity, spaceDirectionOrtho: [1, 1, 1], }, @@ -152,3 +155,73 @@ test("Flycam should move by plane in ortho mode with dynamicSpaceDirection", (t) ); equalWithEpsilon(t, getPosition(newState.flycam), [0, 0, -2]); }); +test("Flycam should not change additional coordinates value when layers don't have any.", (t) => { + const newState = FlycamReducer( + initialState, + FlycamActions.setAdditionalCoordinatesAction([{ name: "t", value: 123 }]), + ); + t.deepEqual(initialState, newState); +}); + +test("Flycam should get correct subset of additional coordinates value when subset is set.", (t) => { + const adaptedState = update(initialState, { + // flycam + dataset: { + dataSource: { + dataLayers: { + $set: [ + { + name: "color1", + type: "color", + additionalAxes: [{ name: "t", bounds: [0, 10] }], + }, + { + name: "color2", + type: "color", + additionalAxes: [{ name: "u", bounds: [10, 20] }], + }, + ], + }, + }, + }, + }); + const newState = FlycamReducer( + adaptedState, + // t is passed, but u is missing. Instead, a superfluous + // q is passed. + FlycamActions.setAdditionalCoordinatesAction([ + { name: "t", value: 7 }, + { name: "q", value: 0 }, + ]), + ); + t.deepEqual(newState.flycam.additionalCoordinates, [ + { name: "t", value: 7 }, + { name: "u", value: 10 }, + ]); +}); + +test("Flycam should get valid additional coordinate value even when invalid value is passed.", (t) => { + const adaptedState = update(initialState, { + // flycam + dataset: { + dataSource: { + dataLayers: { + $set: [ + { + name: "color1", + type: "color", + additionalAxes: [{ name: "t", bounds: [0, 10] }], + }, + ], + }, + }, + }, + }); + const newState = FlycamReducer( + adaptedState, + // t is passed, but u is missing. Instead, a superfluous + // q is passed. + FlycamActions.setAdditionalCoordinatesAction([{ name: "t", value: 70 }]), + ); + t.deepEqual(newState.flycam.additionalCoordinates, [{ name: "t", value: 10 }]); +}); diff --git a/frontend/javascripts/test/reducers/skeletontracing_reducer.spec.ts b/frontend/javascripts/test/reducers/skeletontracing_reducer.spec.ts index 32f90ce6a8d..bda09f6e269 100644 --- a/frontend/javascripts/test/reducers/skeletontracing_reducer.spec.ts +++ b/frontend/javascripts/test/reducers/skeletontracing_reducer.spec.ts @@ -64,6 +64,7 @@ const initialSkeletonTracing: SkeletonTracing = { activeIndex: -1, }, showSkeletons: true, + additionalAxes: [], }; initialSkeletonTracing.trees[1] = { treeId: 1, @@ -100,7 +101,13 @@ const rotation = [0.5, 0.5, 0.5] as Vector3; const viewport = 0; const resolution = 0; test("SkeletonTracing should add a new node", (t) => { - const action = SkeletonTracingActions.createNodeAction(position, rotation, viewport, resolution); + const action = SkeletonTracingActions.createNodeAction( + position, + null, + rotation, + viewport, + resolution, + ); const newState = SkeletonTracingReducer(initialState, action); t.not(newState, initialState); const newSkeletonTracing = enforceSkeletonTracing(newState.tracing); @@ -129,6 +136,7 @@ test("SkeletonTracing should add a new node", (t) => { test("SkeletonTracing should add a several nodes", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -162,6 +170,7 @@ test("SkeletonTracing should add a several nodes", (t) => { test("SkeletonTracing should add nodes to a different tree", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -212,6 +221,7 @@ test("SkeletonTracing should delete the tree if 'delete node as user' is initiat test("SkeletonTracing should delete a node from a tree", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -229,6 +239,7 @@ test("SkeletonTracing should delete a node from a tree", (t) => { test("SkeletonTracing should delete respective comments and branchpoints when deleting a node from a tree", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -251,6 +262,7 @@ test("SkeletonTracing should delete respective comments and branchpoints when de test("SkeletonTracing should not delete tree when last node is deleted from the tree", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -279,6 +291,7 @@ test("SkeletonTracing should delete nodes and split the tree", (t) => { bitDepth: 8, id, position: [0, 0, 0], + additionalCoordinates: null, radius: 10, resolution: 10, rotation: [0, 0, 0], @@ -386,6 +399,7 @@ test("SkeletonTracing should delete nodes and split the tree", (t) => { test("SkeletonTracing should not delete an edge if the two nodes are not neighbors", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -409,6 +423,7 @@ test("SkeletonTracing should not delete an edge if the both nodes are identical" test("SkeletonTracing should not delete any edge if the two nodes are in different trees", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -430,6 +445,7 @@ test("SkeletonTracing should delete an edge and split the tree", (t) => { bitDepth: 8, id, position: [0, 0, 0], + additionalCoordinates: null, radius: 10, resolution: 10, rotation: [0, 0, 0], @@ -540,6 +556,7 @@ test("SkeletonTracing should delete an edge and split the tree", (t) => { test("SkeletonTracing should set a new active node", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -558,6 +575,7 @@ test("SkeletonTracing should set a new active node in a different tree", (t) => const createTreeAction = SkeletonTracingActions.createTreeAction(); const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -579,6 +597,7 @@ test("SkeletonTracing should set a new node radius", (t) => { const newRadius = 10; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -594,6 +613,7 @@ test("SkeletonTracing should set a new node radius", (t) => { test("SkeletonTracing should create a branchpoint", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -619,6 +639,7 @@ test("SkeletonTracing shouldn't create a branchpoint in an empty tree", (t) => { test("SkeletonTracing shouldn't create a branchpoint without the correct permissions", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -639,6 +660,7 @@ test("SkeletonTracing shouldn't create a branchpoint without the correct permiss test("SkeletonTracing shouldn't create more branchpoints than nodes", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -659,6 +681,7 @@ test("SkeletonTracing shouldn't create more branchpoints than nodes", (t) => { test("SkeletonTracing should delete a branchpoint", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -680,6 +703,7 @@ test("SkeletonTracing should delete a branchpoint", (t) => { test("SkeletonTracing should delete specific selected branchpoint", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -703,6 +727,7 @@ test("SkeletonTracing should delete specific selected branchpoint", (t) => { test("SkeletonTracing should delete several branchpoints", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -727,6 +752,7 @@ test("SkeletonTracing should delete several branchpoints", (t) => { test("SkeletonTracing shouldn't delete more branchpoints than available", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -750,6 +776,7 @@ test("SkeletonTracing shouldn't delete more branchpoints than available", (t) => test("SkeletonTracing should delete a branchpoint from a different tree", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -771,6 +798,7 @@ test("SkeletonTracing should delete a branchpoint from a different tree", (t) => test("SkeletonTracing should delete a branchpoint from another tree than the active one", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -866,6 +894,7 @@ test("SkeletonTracing should set a different active tree", (t) => { const createTreeAction = SkeletonTracingActions.createTreeAction(); const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -892,6 +921,7 @@ test("SkeletonTracing should merge two trees", (t) => { const createTreeAction = SkeletonTracingActions.createTreeAction(); const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -928,6 +958,7 @@ test("SkeletonTracing should merge two trees", (t) => { test("SkeletonTracing shouldn't merge the same tree", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -946,6 +977,7 @@ test("SkeletonTracing should merge two trees with comments and branchPoints", (t const createTreeAction = SkeletonTracingActions.createTreeAction(); const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1067,6 +1099,7 @@ test("SkeletonTracing should create a comment for the active node", (t) => { const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1095,6 +1128,7 @@ test("SkeletonTracing shouldn't create more than one comment for the active node const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1116,6 +1150,7 @@ test("SkeletonTracing should create comments for several nodes", (t) => { const commentText2 = "Amaze test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1139,6 +1174,7 @@ test("SkeletonTracing should create comments for a different tree", (t) => { const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1159,6 +1195,7 @@ test("SkeletonTracing should delete a comment for a node", (t) => { const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1179,6 +1216,7 @@ test("SkeletonTracing should only delete the comment for the active node", (t) = const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1203,6 +1241,7 @@ test("SkeletonTracing should add a node in a specified tree", (t) => { const createTreeAction = SkeletonTracingActions.createTreeAction(); const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1223,6 +1262,7 @@ test("SkeletonTracing should add a node in a specified tree", (t) => { test("SkeletonTracing should delete a specified node (1/2)", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1246,6 +1286,7 @@ test("SkeletonTracing should delete a specified node (1/2)", (t) => { test("SkeletonTracing should delete a specified node (2/2)", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1269,6 +1310,7 @@ test("SkeletonTracing should delete a specified node (2/2)", (t) => { test("SkeletonTracing should create a branchpoint for a specified node (1/2)", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1289,6 +1331,7 @@ test("SkeletonTracing should create a branchpoint for a specified node (1/2)", ( test("SkeletonTracing should create a branchpoint for a specified node (2/2)", (t) => { const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1340,6 +1383,7 @@ test("SkeletonTracing should create a comment for a specified node", (t) => { const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, @@ -1362,6 +1406,7 @@ test("SkeletonTracing should delete a comment for a specified node (1/2)", (t) = const commentText = "Wow such test comment"; const createNodeAction = SkeletonTracingActions.createNodeAction( position, + null, rotation, viewport, resolution, diff --git a/frontend/javascripts/test/sagas/saga_integration.spec.ts b/frontend/javascripts/test/sagas/saga_integration.spec.ts index 38ee04bc297..06ce7186acc 100644 --- a/frontend/javascripts/test/sagas/saga_integration.spec.ts +++ b/frontend/javascripts/test/sagas/saga_integration.spec.ts @@ -56,6 +56,7 @@ test.serial( UpdateActions.updateSkeletonTracing( Store.getState().tracing.skeleton, [1, 2, 3], + [], [0, 0, 0], 2, ), diff --git a/frontend/javascripts/test/sagas/skeletontracing_saga.spec.ts b/frontend/javascripts/test/sagas/skeletontracing_saga.spec.ts index 7c6a4d4dbcc..8ea472a91f9 100644 --- a/frontend/javascripts/test/sagas/skeletontracing_saga.spec.ts +++ b/frontend/javascripts/test/sagas/skeletontracing_saga.spec.ts @@ -27,6 +27,7 @@ import { TreeTypeEnum } from "oxalis/constants"; import { Action } from "oxalis/model/actions/actions"; import { ServerSkeletonTracing } from "types/api_flow_types"; import { enforceSkeletonTracing } from "oxalis/model/accessors/skeletontracing_accessor"; + const TIMESTAMP = 1494347146379; const DateMock = { now: () => TIMESTAMP, @@ -103,6 +104,7 @@ const skeletonTracing: SkeletonTracing = { activeIndex: -1, }, showSkeletons: true, + additionalAxes: [], }; const serverSkeletonTracing: ServerSkeletonTracing = { ...skeletonTracing, @@ -112,11 +114,13 @@ const serverSkeletonTracing: ServerSkeletonTracing = { y: 0, z: 0, }, + editPositionAdditionalCoordinates: null, editRotation: { x: 0, y: 0, z: 0, }, + additionalAxes: [], zoomLevel: 2, userBoundingBoxes: [], typ: "Skeleton", @@ -153,7 +157,13 @@ const initialState = update(defaultState, { }, }); -const createNodeAction = SkeletonTracingActions.createNodeAction([1, 2, 3], [0, 1, 0], 0, 1.2); +const createNodeAction = SkeletonTracingActions.createNodeAction( + [1, 2, 3], + null, + [0, 1, 0], + 0, + 1.2, +); const deleteNodeAction = SkeletonTracingActions.deleteNodeAction(); const createTreeAction = SkeletonTracingActions.createTreeAction(12345678); const deleteTreeAction = SkeletonTracingActions.deleteTreeAction(); diff --git a/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga.spec.ts b/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga.spec.ts index 4c6215e3205..4b3f2bf8c80 100644 --- a/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga.spec.ts +++ b/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga.spec.ts @@ -19,6 +19,7 @@ import sinon from "sinon"; import test from "ava"; import { expectValueDeepEqual, execCall } from "test/helpers/sagaHelpers"; import { withoutUpdateTracing } from "test/helpers/saveHelpers"; + mockRequire("app", { currentUser: { firstName: "SCM", @@ -63,11 +64,13 @@ const serverVolumeTracing: ServerVolumeTracing = { y: 0, z: 0, }, + editPositionAdditionalCoordinates: null, editRotation: { x: 0, y: 0, z: 0, }, + additionalAxes: [], userBoundingBoxes: [], largestSegmentId: 0, }; @@ -80,6 +83,7 @@ const volumeTracingLayer: APISegmentationLayer = { elementClass: serverVolumeTracing.elementClass, largestSegmentId: serverVolumeTracing.largestSegmentId, tracingId: volumeTracing.tracingId, + additionalAxes: [], }; const initialState = update(defaultState, { tracing: { @@ -180,14 +184,16 @@ test("VolumeTracingSaga should create a volume layer (saga test)", (t) => { resolution: [1, 1, 1], zoomStep: 0, }); + saga.next(ACTIVE_CELL_ID); // pass active cell id expectValueDeepEqual( t, - saga.next(ACTIVE_CELL_ID), // pass active cell id + saga.next([]), // pass empty additional coords put( VolumeTracingActions.updateSegmentAction( ACTIVE_CELL_ID, { somePosition: startEditingAction.position, + someAdditionalCoordinates: [], }, volumeTracing.tracingId, ), @@ -218,14 +224,17 @@ test("VolumeTracingSaga should add values to volume layer (saga test)", (t) => { zoomStep: 0, }); // pass labeled resolution + saga.next(ACTIVE_CELL_ID); // pass active cell id expectValueDeepEqual( t, - saga.next(ACTIVE_CELL_ID), // pass active cell id + saga.next([]), // pass empty additional coords + put( VolumeTracingActions.updateSegmentAction( ACTIVE_CELL_ID, { somePosition: startEditingAction.position, + someAdditionalCoordinates: [], }, volumeTracing.tracingId, ), @@ -265,14 +274,17 @@ test("VolumeTracingSaga should finish a volume layer (saga test)", (t) => { zoomStep: 0, }); // pass labeled resolution + saga.next(ACTIVE_CELL_ID); // pass active cell id expectValueDeepEqual( t, - saga.next(ACTIVE_CELL_ID), // pass active cell id + saga.next([]), // pass empty additional coords + put( VolumeTracingActions.updateSegmentAction( ACTIVE_CELL_ID, { somePosition: startEditingAction.position, + someAdditionalCoordinates: [], }, volumeTracing.tracingId, ), @@ -319,15 +331,16 @@ test("VolumeTracingSaga should finish a volume layer in delete mode (saga test)" resolution: [1, 1, 1], zoomStep: 0, }); // pass labeled resolution - + saga.next(ACTIVE_CELL_ID); // pass active cell id expectValueDeepEqual( t, - saga.next(ACTIVE_CELL_ID), // pass active cell id + saga.next([]), // pass empty additional coords put( VolumeTracingActions.updateSegmentAction( ACTIVE_CELL_ID, { somePosition: startEditingAction.position, + someAdditionalCoordinates: [], }, volumeTracing.tracingId, ), diff --git a/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga_integration.spec.ts b/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga_integration.spec.ts index 3e2f9052d99..21466bd559c 100644 --- a/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga_integration.spec.ts +++ b/frontend/javascripts/test/sagas/volumetracing/volumetracing_saga_integration.spec.ts @@ -862,10 +862,10 @@ test.serial("Provoke race condition when bucket compression is very slow", async test.serial("Undo for deleting segment group (without recursion)", async (t) => { const volumeTracingLayerName = t.context.api.data.getVolumeTracingLayerIds()[0]; const position = [1, 2, 3] as Vector3; - Store.dispatch(clickSegmentAction(1, position)); - Store.dispatch(clickSegmentAction(2, position)); - Store.dispatch(clickSegmentAction(3, position)); - Store.dispatch(clickSegmentAction(4, position)); + Store.dispatch(clickSegmentAction(1, position, undefined)); + Store.dispatch(clickSegmentAction(2, position, undefined)); + Store.dispatch(clickSegmentAction(3, position, undefined)); + Store.dispatch(clickSegmentAction(4, position, undefined)); Store.dispatch( setSegmentGroupsAction( @@ -917,10 +917,10 @@ test.serial("Undo for deleting segment group (without recursion)", async (t) => test.serial("Undo for deleting segment group (with recursion)", async (t) => { const volumeTracingLayerName = t.context.api.data.getVolumeTracingLayerIds()[0]; const position = [1, 2, 3] as Vector3; - Store.dispatch(clickSegmentAction(1, position)); - Store.dispatch(clickSegmentAction(2, position)); - Store.dispatch(clickSegmentAction(3, position)); - Store.dispatch(clickSegmentAction(4, position)); + Store.dispatch(clickSegmentAction(1, position, undefined)); + Store.dispatch(clickSegmentAction(2, position, undefined)); + Store.dispatch(clickSegmentAction(3, position, undefined)); + Store.dispatch(clickSegmentAction(4, position, undefined)); Store.dispatch( setSegmentGroupsAction( @@ -966,10 +966,10 @@ test.serial("Undo for deleting segment group (with recursion)", async (t) => { test.serial("Undo for deleting segment group (bug repro)", async (t) => { const volumeTracingLayerName = t.context.api.data.getVolumeTracingLayerIds()[0]; const position = [1, 2, 3] as Vector3; - Store.dispatch(clickSegmentAction(1, position)); - Store.dispatch(clickSegmentAction(2, position)); - Store.dispatch(clickSegmentAction(3, position)); - Store.dispatch(clickSegmentAction(4, position)); + Store.dispatch(clickSegmentAction(1, position, undefined)); + Store.dispatch(clickSegmentAction(2, position, undefined)); + Store.dispatch(clickSegmentAction(3, position, undefined)); + Store.dispatch(clickSegmentAction(4, position, undefined)); /* Set up Group 1 diff --git a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.md b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.md index 4be860867ee..51efee828c0 100644 --- a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.md +++ b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.md @@ -1060,12 +1060,14 @@ Generated by [AVA](https://avajs.dev). ## annotations-tracing { + additionalAxes: [], createdTimestamp: 'createdTimestamp', editPosition: { x: 256, y: 256, z: 128, }, + editPositionAdditionalCoordinates: [], editRotation: { x: 0, y: 0, @@ -1083,6 +1085,7 @@ Generated by [AVA](https://avajs.dev). ## annotations-tracing-volume { + additionalAxes: [], boundingBox: { depth: 256, height: 512, @@ -1099,6 +1102,7 @@ Generated by [AVA](https://avajs.dev). y: 256, z: 128, }, + editPositionAdditionalCoordinates: [], editRotation: { x: 0, y: 0, @@ -1147,12 +1151,14 @@ Generated by [AVA](https://avajs.dev). { skeleton: { + additionalAxes: [], createdTimestamp: 'createdTimestamp', editPosition: { x: 256, y: 256, z: 128, }, + editPositionAdditionalCoordinates: [], editRotation: { x: 0, y: 0, @@ -1167,6 +1173,7 @@ Generated by [AVA](https://avajs.dev). zoomLevel: 2, }, volume: { + additionalAxes: [], boundingBox: { depth: 256, height: 512, @@ -1183,6 +1190,7 @@ Generated by [AVA](https://avajs.dev). y: 256, z: 128, }, + editPositionAdditionalCoordinates: [], editRotation: { x: 0, y: 0, @@ -1231,12 +1239,14 @@ Generated by [AVA](https://avajs.dev). ## annotations-updateActions { + additionalAxes: [], createdTimestamp: 'createdTimestamp', editPosition: { x: 2, y: 3, z: 4, }, + editPositionAdditionalCoordinates: [], editRotation: { x: 1, y: 2, @@ -1254,12 +1264,14 @@ Generated by [AVA](https://avajs.dev). ## annotations-complexUpdateActions { + additionalAxes: [], createdTimestamp: 'createdTimestamp', editPosition: { x: 256, y: 256, z: 128, }, + editPositionAdditionalCoordinates: [], editRotation: { x: 0, y: 0, @@ -1321,6 +1333,7 @@ Generated by [AVA](https://avajs.dev). name: 'explorative_2017-10-09_SCM_Boy_023', nodes: [ { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1340,6 +1353,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1359,6 +1373,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1378,6 +1393,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1397,6 +1413,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1416,6 +1433,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1474,6 +1492,7 @@ Generated by [AVA](https://avajs.dev). name: 'explorative_2017-10-09_SCM_Boy_023', nodes: [ { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1493,6 +1512,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1512,6 +1532,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1531,6 +1552,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1550,6 +1572,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1569,6 +1592,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1627,6 +1651,7 @@ Generated by [AVA](https://avajs.dev). name: 'explorative_2017-10-09_SCM_Boy_023', nodes: [ { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1646,6 +1671,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1665,6 +1691,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1684,6 +1711,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1703,6 +1731,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1722,6 +1751,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1780,6 +1810,7 @@ Generated by [AVA](https://avajs.dev). name: 'explorative_2017-10-09_SCM_Boy_023', nodes: [ { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1799,6 +1830,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1818,6 +1850,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1837,6 +1870,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1856,6 +1890,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1875,6 +1910,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1933,6 +1969,7 @@ Generated by [AVA](https://avajs.dev). name: 'explorative_2017-10-09_SCM_Boy_023', nodes: [ { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1952,6 +1989,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1971,6 +2009,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -1990,6 +2029,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -2009,6 +2049,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', @@ -2028,6 +2069,7 @@ Generated by [AVA](https://avajs.dev). viewport: 1, }, { + additionalCoordinates: [], bitDepth: 4, createdTimestamp: 'createdTimestamp', id: 'id', diff --git a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.snap b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.snap index 444456b0b15..1bfc130d16b 100644 Binary files a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.snap and b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/annotations.e2e.js.snap differ diff --git a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.md b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.md index f8f44dc645e..672b873ace1 100644 --- a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.md +++ b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.md @@ -63,6 +63,68 @@ Generated by [AVA](https://avajs.dev). ␊ ` +## nml-with-additional-coordinates + + `␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ␊ + ` + ## nml-special-chars `␊ diff --git a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.snap b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.snap index 2eac657333f..94be274b569 100644 Binary files a/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.snap and b/frontend/javascripts/test/snapshots/public-test/test-bundle/test/libs/nml.spec.js.snap differ diff --git a/frontend/javascripts/types/api_flow_types.ts b/frontend/javascripts/types/api_flow_types.ts index c29a4eb4bb8..afaf133f5a5 100644 --- a/frontend/javascripts/types/api_flow_types.ts +++ b/frontend/javascripts/types/api_flow_types.ts @@ -19,6 +19,8 @@ import type { } from "oxalis/constants"; import { PricingPlanEnum } from "admin/organization/pricing_plan_utils"; +export type AdditionalCoordinate = { name: string; value: number }; + export type APIMessage = { [key in "info" | "warning" | "error"]?: string }; export type ElementClass = | "uint8" @@ -39,6 +41,18 @@ export type APIMapping = { readonly colors?: Array; readonly hideUnmappedIds?: boolean; }; +export type AdditionalAxis = { + bounds: [number, number]; + index: number; + name: string; +}; + +export type ServerAdditionalAxis = { + bounds: { x: number; y: number }; + index: number; + name: string; +}; + export type CoordinateTransformation = | { type: "affine"; @@ -54,6 +68,7 @@ type APIDataLayerBase = { readonly resolutions: Array; readonly elementClass: ElementClass; readonly dataFormat?: "wkw" | "zarr"; + readonly additionalAxes: Array | null; readonly coordinateTransformations?: CoordinateTransformation[] | null; }; type APIColorLayer = APIDataLayerBase & { @@ -632,6 +647,7 @@ export type APIUpdateActionBatch = { export type ServerNode = { id: number; position: Point3; + additionalCoordinates: AdditionalCoordinate[]; rotation: Point3; bitDepth: number; viewport: number; @@ -680,6 +696,7 @@ type ServerSegment = { segmentId: number; name: string | null | undefined; anchorPosition: Point3 | null | undefined; + additionalCoordinates: AdditionalCoordinate[] | null; creationTime: number | null | undefined; color: ColorObject | null; groupId: number | null | undefined; @@ -690,10 +707,12 @@ export type ServerTracingBase = { userBoundingBox?: ServerBoundingBox; createdTimestamp: number; editPosition: Point3; + editPositionAdditionalCoordinates: AdditionalCoordinate[] | null; editRotation: Point3; error?: string; version: number; zoomLevel: number; + additionalAxes: ServerAdditionalAxis[]; }; export type ServerSkeletonTracing = ServerTracingBase & { // The following property is added when fetching the diff --git a/frontend/javascripts/types/schemas/datasource.schema.ts b/frontend/javascripts/types/schemas/datasource.schema.ts index 51c0cfedb62..77ab85dfa8c 100644 --- a/frontend/javascripts/types/schemas/datasource.schema.ts +++ b/frontend/javascripts/types/schemas/datasource.schema.ts @@ -4,6 +4,14 @@ export default { $schema: "http://json-schema.org/draft-06/schema#", ...baseDatasetViewConfiguration, definitions: { + "types::Vector2": { + type: "array", + items: { + type: "number", + }, + minItems: 2, + maxItems: 2, + }, "types::Vector3": { type: "array", items: { @@ -78,6 +86,23 @@ export default { numChannels: { type: "number", }, + additionalAxes: { + type: "array", + items: { + type: "object", + properties: { + name: { + type: "string", + }, + bounds: { + $ref: "#/definitions/types::Vector2", + }, + index: { + type: "number", + }, + }, + }, + }, mags: { type: "array", items: { diff --git a/frontend/javascripts/types/schemas/datasource.types.ts b/frontend/javascripts/types/schemas/datasource.types.ts index 000992eca23..a7892a3deb4 100644 --- a/frontend/javascripts/types/schemas/datasource.types.ts +++ b/frontend/javascripts/types/schemas/datasource.types.ts @@ -1,7 +1,9 @@ // This file is only for documentation: // Types which were used for creating the datasource.schema.js // The `flow2schema` node module has been used for conversion. + // Please note that some manual changes to the schema are required. +type Vector2 = [number, number]; type Vector3 = [number, number, number]; type BoundingBox = { topLeft: Vector3; @@ -27,6 +29,11 @@ type BaseRemoteLayer = { path: string; axisOrder: Record; }>; + additionalCoordinates?: Array<{ + name: string; + index: number; + bounds: Vector2; + }>; }; type DataLayerZarrPartial = BaseRemoteLayer & { dataFormat: "zarr"; diff --git a/frontend/javascripts/types/schemas/url_state.schema.ts b/frontend/javascripts/types/schemas/url_state.schema.ts index f05a4933100..e3051dbfb97 100644 --- a/frontend/javascripts/types/schemas/url_state.schema.ts +++ b/frontend/javascripts/types/schemas/url_state.schema.ts @@ -15,6 +15,15 @@ export default { }, ], }, + "types::AdditionalCoordinates": { + type: "array", + items: [ + { + type: "object", + properties: { name: { type: "string" }, value: { type: "number" } }, + }, + ], + }, "types::ViewMode": { enum: ["orthogonal", "oblique", "flight", "volume"], }, @@ -160,6 +169,9 @@ export default { position: { $ref: "#/definitions/types::Vector3", }, + additionalCoordinates: { + $ref: "#/definitions/types::AdditionalCoordinates", + }, mode: { $ref: "#/definitions/types::ViewMode", }, diff --git a/frontend/stylesheets/antd_overwrites.less b/frontend/stylesheets/antd_overwrites.less index 61e547d7215..f8414cef1ba 100644 --- a/frontend/stylesheets/antd_overwrites.less +++ b/frontend/stylesheets/antd_overwrites.less @@ -155,7 +155,6 @@ label.ant-checkbox-wrapper { } .collapsed-nav-header .ant-menu:not(.right-navbar) { - .ant-menu-item, .ant-menu-submenu-title, li.ant-menu-submenu { @@ -254,3 +253,13 @@ label.ant-checkbox-wrapper { .ant-list-empty-text { color: fade(@text-color, 50%); } + +.action-bar { + // UI for additional coordinates should always be in dark mode + // since it is located in the navbar's action bar. + .ant-input-group-addon { + background-color: rgb(58, 61, 72); + border-color: #585867; + color: #f1f1f1 !important; + } +} diff --git a/frontend/stylesheets/trace_view/_tracing_view.less b/frontend/stylesheets/trace_view/_tracing_view.less index 05d1fc016d3..b38c52001c3 100644 --- a/frontend/stylesheets/trace_view/_tracing_view.less +++ b/frontend/stylesheets/trace_view/_tracing_view.less @@ -351,6 +351,7 @@ img.keyboard-mouse-icon:first-child { .ant-btn, .ant-input, + .ant-input-number, .ant-radio-button-wrapper, .ant-select > .ant-select-selector { background-color: @dark-bg; @@ -368,6 +369,7 @@ img.keyboard-mouse-icon:first-child { &.ant-btn-disabled, &.ant-input-disabled, + &.ant-input-number-disabled, &.ant-radio-button-wrapper-disabled, &.ant-select-selector-disabled { color: fade(@dark-fg, 50%); diff --git a/package.json b/package.json index ecc2b79fc58..6bfd32d003f 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "copy-webpack-plugin": "^11.0.0", "coveralls": "^3.0.2", "css-loader": "^6.5.1", - "documentation": "^13.2.5", + "documentation": "^14.0.2", "domexception": "^2.0.1", "enzyme": "^3.7.0", "enzyme-adapter-react-16": "^1.9.1", @@ -101,8 +101,8 @@ "test-help": "echo For development it is recommended to run yarn test-prepare-watch in one terminal and yarn test-watch in another. This is the fastest way to perform incremental testing. If you are only interested in running test once, use yarn test.", "remove-e2e-snapshots": "rm -rf frontend/javascripts/test/snapshots/public-test/test-bundle/test/backend-snapshot-tests/ && rm -rf frontend/javascripts/test/snapshots/public-test/test-bundle/test/enzyme/", "remove-all-snapshots": "rm -rf frontend/javascripts/test/snapshots/", - "refresh-e2e-snapshots": "yarn remove-e2e-snapshots && docker compose down && docker compose up e2e-tests", - "refresh-all-snapshots": "yarn remove-all-snapshots && yarn test && refresh-e2e-snapshots", + "refresh-e2e-snapshots": "yarn remove-e2e-snapshots && mkdir -p frontend/javascripts/test/snapshots/type-check && docker compose down && docker compose up e2e-tests", + "refresh-all-snapshots": "yarn remove-all-snapshots && yarn test && yarn refresh-e2e-snapshots", "lint": "yarn run rome check frontend && tools/assert-no-test-only.sh && node_modules/.bin/eslint --cache --quiet --ext .ts,.tsx frontend/javascripts/", "lint-fix": "yarn run rome check frontend --apply-suggested && echo Please proofread the applied suggestions, as they may not be safe.", "lintd": "node_modules/.bin/eslint_d --cache --quiet --ext .ts,.tsx frontend/javascripts/ # lintd uses a daemon for better speed (especially useful for hooking up to editors)", @@ -126,7 +126,8 @@ "ts-watch": "yarn tsc --watch", "ts-coverage": "typescript-coverage-report", "find-cyclic-dependencies": "madge --circular --extensions ts,tsx frontend/javascripts/main.tsx", - "check-cyclic-dependencies": "node ./tools/check-cyclic-dependencies.js" + "check-cyclic-dependencies": "node ./tools/check-cyclic-dependencies.js", + "startf": "yarn rm-fossil-lock; yarn kill-listeners; yarn start" }, "lint-staged": { "*.ts": [ diff --git a/test/backend/AdditionalCoordinateTestSuite.scala b/test/backend/AdditionalCoordinateTestSuite.scala new file mode 100644 index 00000000000..91f323b2bcf --- /dev/null +++ b/test/backend/AdditionalCoordinateTestSuite.scala @@ -0,0 +1,64 @@ +package backend + +import com.scalableminds.webknossos.datastore.helpers.ProtoGeometryImplicits +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis +import org.scalatestplus.play.PlaySpec + +class AdditionalCoordinateTestSuite extends PlaySpec with ProtoGeometryImplicits { + + private def definitionsEqual(a: AdditionalAxis, b: AdditionalAxis) = + a.name == b.name && a.index == b.index && a.lowerBound == b.lowerBound && a.upperBound == b.upperBound + + "Additional coordinate axis" when { + val axisT = AdditionalAxis("t", Array(0, 25), 0) + "converting to proto" should { + "return correct value" in { + val proto = AdditionalAxis.toProto(Some(Seq(axisT))) + assert(proto.size == 1) + assert(proto.head.name == axisT.name) + assert(proto.head.index == axisT.index) + assert(arrayFromVec2IntProto(proto.head.bounds).sameElements(axisT.bounds)) + } + "be reversible" in { + val proto = AdditionalAxis.toProto(Some(Seq(axisT))) + val deProtofied = AdditionalAxis.fromProto(proto) + assert(definitionsEqual(deProtofied.head, axisT)) + } + } + "merging" should { + val axisA = AdditionalAxis("a", Array(10, 30), 1) + val axisB = AdditionalAxis("b", Array(0, 100), 2) + val axisT2 = AdditionalAxis("t", Array(10, 40), 3) + "give merge" in { + val merged = + AdditionalAxis + .merge(Seq(Some(Seq(axisA, axisB)), Some(Seq(axisT)))) + .get + .sortBy(_.index) + + assert( + definitionsEqual(merged(0), axisT) && + definitionsEqual(merged(1), axisA) && + definitionsEqual(merged(2), axisB)) + } + "merge bounds" in { + val merged = AdditionalAxis.merge(Seq(Some(Seq(axisT2)), Some(Seq(axisT)))).get + assert(merged.head.bounds.sameElements(Array(0, 40))) + } + "using assert same coordinates" should { + "fail when coordinates are not the same" in { + val merged = AdditionalAxis.mergeAndAssertSameAdditionalAxes( + Seq(Some(Seq(axisA)), Some(Seq(axisT)))) + assert(merged.isEmpty) + } + "succeed when coordinates are the same" in { + val merged = AdditionalAxis.mergeAndAssertSameAdditionalAxes( + Seq(Some(Seq(axisT)), Some(Seq(axisT2)))) + assert(!merged.isEmpty) + assert(merged.openOrThrowException("test").get.head.name == "t") + } + } + + } + } +} diff --git a/test/backend/DataVaultTestSuite.scala b/test/backend/DataVaultTestSuite.scala index 03c96fe491b..0b649b69f76 100644 --- a/test/backend/DataVaultTestSuite.scala +++ b/test/backend/DataVaultTestSuite.scala @@ -15,7 +15,6 @@ import com.scalableminds.webknossos.datastore.datavault.{ } import com.scalableminds.webknossos.datastore.storage.{GoogleServiceAccountCredential, RemoteSourceDescriptor} import net.liftweb.common.{Box, Empty, EmptyBox, Failure, Full} -import org.scalatest.{Failed, Outcome, Succeeded} import play.api.libs.json.JsString import play.api.test.WsTestClient diff --git a/test/backend/NMLUnitTestSuite.scala b/test/backend/NMLUnitTestSuite.scala index 40d1e4877a1..a3f17de7605 100644 --- a/test/backend/NMLUnitTestSuite.scala +++ b/test/backend/NMLUnitTestSuite.scala @@ -1,8 +1,8 @@ package backend import java.io.ByteArrayInputStream - import com.scalableminds.webknossos.datastore.SkeletonTracing._ +import com.scalableminds.webknossos.datastore.geometry.{AdditionalAxisProto, Vec2IntProto} import com.scalableminds.webknossos.datastore.models.annotation.{AnnotationLayer, FetchedAnnotationLayer} import models.annotation.nml.{NmlParser, NmlWriter} import models.annotation.UploadedVolumeLayer @@ -132,5 +132,13 @@ class NMLUnitTestSuite extends PlaySpec { assert(!isParseSuccessful(writeAndParseTracing(newTracing))) } + + "throw an error for multiple additional coordinates of the same name" in { + val newTracing = dummyTracing.copy( + additionalAxes = Seq(new AdditionalAxisProto("t", 0, Vec2IntProto(0, 10)), + new AdditionalAxisProto("t", 1, Vec2IntProto(10, 20)))) + + assert(!isParseSuccessful(writeAndParseTracing(newTracing))) + } } } diff --git a/test/backend/SkeletonUpdateActionsUnitTestSuite.scala b/test/backend/SkeletonUpdateActionsUnitTestSuite.scala index 13a81d7ac31..a3e5c647c9b 100644 --- a/test/backend/SkeletonUpdateActionsUnitTestSuite.scala +++ b/test/backend/SkeletonUpdateActionsUnitTestSuite.scala @@ -11,13 +11,12 @@ class SkeletonUpdateActionsUnitTestSuite extends PlaySpec { private def applyUpdateAction(action: UpdateAction.SkeletonUpdateAction): SkeletonTracing = action.applyOn(Dummies.skeletonTracing) - def listConsistsOfLists[T](joinedList: Seq[T], sublist1: Seq[T], sublist2: Seq[T]): Boolean = { + def listConsistsOfLists[T](joinedList: Seq[T], sublist1: Seq[T], sublist2: Seq[T]): Boolean = // assuming sublist1 & sublist2 are different if (joinedList.length != sublist1.length + sublist2.length) false else joinedList.forall(el => sublist1.contains(el) || sublist2.contains(el)) - } "CreateTreeSkeletonAction" should { "add the specified tree" in { @@ -152,7 +151,8 @@ class SkeletonUpdateActionsUnitTestSuite extends PlaySpec { Option(newNode.bitDepth), Option(newNode.interpolation), treeId = 1, - Dummies.timestamp + Dummies.timestamp, + None ) val result = applyUpdateAction(createNodeSkeletonAction) assert(result.trees.length == Dummies.skeletonTracing.trees.length) @@ -200,7 +200,8 @@ class SkeletonUpdateActionsUnitTestSuite extends PlaySpec { Option(newNode.bitDepth), Option(newNode.interpolation), treeId = 1, - Dummies.timestamp + Dummies.timestamp, + None ) val deleteNodeSkeletonAction = new DeleteNodeSkeletonAction(newNode.id, treeId = 1) val result = deleteNodeSkeletonAction.applyOn(createNodeSkeletonAction.applyOn(Dummies.skeletonTracing)) diff --git a/test/backend/VolumeBucketKeyTestSuite.scala b/test/backend/VolumeBucketKeyTestSuite.scala new file mode 100644 index 00000000000..9361e56277a --- /dev/null +++ b/test/backend/VolumeBucketKeyTestSuite.scala @@ -0,0 +1,80 @@ +package backend + +import com.scalableminds.util.geometry.Vec3Int +import com.scalableminds.webknossos.datastore.models.{AdditionalCoordinate, BucketPosition} +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis +import com.scalableminds.webknossos.tracingstore.tracings.volume.BucketKeys +import org.scalatestplus.play.PlaySpec + +class VolumeBucketKeyTestSuite extends PlaySpec { + + class BucketKeyBuilder extends BucketKeys { + def build(dataLayerName: String, + bucket: BucketPosition, + additionalAxes: Option[Seq[AdditionalAxis]] = None): String = + buildBucketKey(dataLayerName, bucket, additionalAxes) + + def parse(key: String, additionalAxes: Option[Seq[AdditionalAxis]]): Option[(String, BucketPosition)] = + parseBucketKey(key, additionalAxes) + } + val bucketKeyBuilder = new BucketKeyBuilder + + val layerName = "mylayer" + + "Bucket Key" when { + "built with xyz" should { + val bucketPos = BucketPosition(32, 64, 96, Vec3Int(1, 1, 1), None) + "match defined bucket key" in { + val key = bucketKeyBuilder.build(layerName, bucketPos) + assert(key == s"$layerName/1/53-[1,2,3]") + } + "expands mag when anisotropic" in { + val key = bucketKeyBuilder.build(layerName, BucketPosition(32, 64, 96, Vec3Int(4, 4, 1), None)) + assert(key == s"$layerName/4-4-1/36-[0,0,3]") + } + "is parsed as the same bucket position" in { + bucketKeyBuilder.parse(s"$layerName/1/53-[1,2,3]", None) match { + case Some((layer, parsedPos)) => + assert(layer == layerName) + assert(parsedPos == bucketPos) + case None => fail + } + } + + } + "built with additional coordinates" should { + val additionalAxes = + Seq(AdditionalAxis("a", Array(0, 10), 0), AdditionalAxis("b", Array(0, 10), 1)) + val additionalCoordinates = Seq(AdditionalCoordinate("a", 4), AdditionalCoordinate("b", 5)) + + val bucketPos = BucketPosition(32, 64, 96, Vec3Int(1, 1, 1), Some(additionalCoordinates)) + + "match defined bucket key" in { + + val key = bucketKeyBuilder.build( + layerName, + bucketPos, + Some(additionalAxes) + ) + assert(key == s"$layerName/1/53-[4,5][1,2,3]") + } + "is parsed as the same bucket position" in { + bucketKeyBuilder.parse(s"$layerName/1/53-[4,5][1,2,3]", Some(additionalAxes)) match { + case Some((layer, parsedPos)) => + assert(layer == layerName) + assert(parsedPos == bucketPos) + case None => fail + } + } + + "sort additional coordinates" in { + val key = bucketKeyBuilder.build( + layerName, + BucketPosition(32, 64, 96, Vec3Int(1, 1, 1), Some(additionalCoordinates.reverse)), + Some(additionalAxes) + ) + assert(key == s"$layerName/1/53-[4,5][1,2,3]") + } + } + } +} diff --git a/tools/postgres/schema.sql b/tools/postgres/schema.sql index e24dc0c1370..e742dbac97f 100644 --- a/tools/postgres/schema.sql +++ b/tools/postgres/schema.sql @@ -20,7 +20,7 @@ CREATE TABLE webknossos.releaseInformation ( schemaVersion BIGINT NOT NULL ); -INSERT INTO webknossos.releaseInformation(schemaVersion) values(107); +INSERT INTO webknossos.releaseInformation(schemaVersion) values(108); COMMIT TRANSACTION; @@ -156,6 +156,15 @@ CREATE TABLE webknossos.dataSet_layer_coordinateTransformations( insertionOrderIndex INT ); +CREATE TABLE webknossos.dataSet_layer_additionalAxes( + _dataSet CHAR(24) NOT NULL, + layerName VARCHAR(256) NOT NULL, + name VARCHAR(256) NOT NULL, + lowerBound INT NOT NULL, + upperBound INT NOT NULL, + index INT NOT NULL +); + CREATE TABLE webknossos.dataSet_allowedTeams( _dataSet CHAR(24) NOT NULL, _team CHAR(24) NOT NULL, @@ -759,6 +768,8 @@ ALTER TABLE webknossos.organizations ADD FOREIGN KEY (_rootFolder) REFERENCES webknossos.folders(_id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; ALTER TABLE webknossos.dataSet_layer_coordinateTransformations ADD CONSTRAINT dataSet_ref FOREIGN KEY(_dataSet) REFERENCES webknossos.dataSets(_id) DEFERRABLE; +ALTER TABLE webknossos.dataSet_layer_additionalAxes + ADD CONSTRAINT dataSet_ref FOREIGN KEY(_dataSet) REFERENCES webknossos.dataSets(_id) DEFERRABLE; ALTER TABLE webknossos.voxelytics_artifacts ADD FOREIGN KEY (_task) REFERENCES webknossos.voxelytics_tasks(_id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE; ALTER TABLE webknossos.voxelytics_runs diff --git a/tsconfig.json b/tsconfig.json index ee4de3e11ed..39c6ee08bea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "outDir": "build", "jsx": "react", "strict": true, - "target": "es2020", + "target": "es2021", "types": [], //reconsider this later "skipLibCheck": true, //reconsider this later "allowSyntheticDefaultImports": true, diff --git a/util/src/main/scala/com/scalableminds/util/geometry/Vec3Int.scala b/util/src/main/scala/com/scalableminds/util/geometry/Vec3Int.scala index d923a0e201f..19b7a536b23 100644 --- a/util/src/main/scala/com/scalableminds/util/geometry/Vec3Int.scala +++ b/util/src/main/scala/com/scalableminds/util/geometry/Vec3Int.scala @@ -35,6 +35,8 @@ case class Vec3Int(x: Int, y: Int, z: Int) { def toList: List[Int] = List(x, y, z) + def toArray: Array[Int] = toList.toArray + def toVec3Float: Vec3Float = Vec3Float(x.toFloat, y.toFloat, z.toFloat) def toVec3Double: Vec3Double = Vec3Double(x.toDouble, y.toDouble, z.toDouble) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/ZarrStreamingController.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/ZarrStreamingController.scala index 8779268971b..cfabbe3618c 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/ZarrStreamingController.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/ZarrStreamingController.scala @@ -127,7 +127,8 @@ class ZarrStreamingController @Inject()( d.boundingBox, d.elementClass, d.resolutions.map(x => MagLocator(x, None, None, Some(AxisOrder.cxyz), None, None)), - numChannels = Some(if (d.elementClass == ElementClass.uint24) 3 else 1) + numChannels = Some(if (d.elementClass == ElementClass.uint24) 3 else 1), + additionalAxes = None ) } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/BucketProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/BucketProvider.scala index 51c8b5509df..bacfce4f380 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/BucketProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/BucketProvider.scala @@ -20,7 +20,8 @@ trait BucketProvider extends FoxImplicits with LazyLogging { def load(readInstruction: DataReadInstruction, cache: DataCubeCache)( implicit ec: ExecutionContext): Fox[Array[Byte]] = - cache.withCache(readInstruction)(loadFromUnderlyingWithTimeout)(_.cutOutBucket(readInstruction.bucket)) + cache.withCache(readInstruction)(loadFromUnderlyingWithTimeout)( + _.cutOutBucket(readInstruction.bucket, readInstruction.dataLayer)) private def loadFromUnderlyingWithTimeout(readInstruction: DataReadInstruction)( implicit ec: ExecutionContext): Fox[DataCubeHandle] = { @@ -46,14 +47,21 @@ trait BucketProvider extends FoxImplicits with LazyLogging { protected def localPathFrom(readInstruction: DataReadInstruction, relativeMagPath: String)( implicit ec: ExecutionContext): Fox[VaultPath] = { - val magPath = FileSystemVaultPath.fromPath( + val magPathRelativeToDataset = FileSystemVaultPath.fromPath( + readInstruction.baseDir + .resolve(readInstruction.dataSource.id.team) + .resolve(readInstruction.dataSource.id.name) + .resolve(relativeMagPath)) + val magPathRelativeToLayer = FileSystemVaultPath.fromPath( readInstruction.baseDir .resolve(readInstruction.dataSource.id.team) .resolve(readInstruction.dataSource.id.name) .resolve(readInstruction.dataLayer.name) .resolve(relativeMagPath)) - if (magPath.exists) { - Fox.successful(magPath) + if (magPathRelativeToDataset.exists) { + Fox.successful(magPathRelativeToDataset) + } else if (magPathRelativeToLayer.exists) { + Fox.successful(magPathRelativeToLayer) } else Fox.empty } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/DataCubeHandle.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/DataCubeHandle.scala index 8c5008162e9..9aee27d3b8e 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/DataCubeHandle.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/DataCubeHandle.scala @@ -2,10 +2,11 @@ package com.scalableminds.webknossos.datastore.dataformats import com.scalableminds.util.tools.Fox import com.scalableminds.webknossos.datastore.models.BucketPosition +import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import scala.concurrent.ExecutionContext // To be implemented as handle for a cube (e.g. may correspond to one 1GB wkw file) trait DataCubeHandle extends SafeCachable { - def cutOutBucket(bucket: BucketPosition)(implicit ec: ExecutionContext): Fox[Array[Byte]] + def cutOutBucket(bucket: BucketPosition, dataLayer: DataLayer)(implicit ec: ExecutionContext): Fox[Array[Byte]] } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5BucketProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5BucketProvider.scala index fb86191016a..b24a4d519c0 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5BucketProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5BucketProvider.scala @@ -8,6 +8,7 @@ import com.scalableminds.webknossos.datastore.datareaders.n5.N5Array import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.BucketPosition import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService import com.typesafe.scalalogging.LazyLogging @@ -18,7 +19,7 @@ import scala.concurrent.ExecutionContext class N5CubeHandle(n5Array: N5Array) extends DataCubeHandle with LazyLogging { - def cutOutBucket(bucket: BucketPosition)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { + def cutOutBucket(bucket: BucketPosition, dataLayer: DataLayer)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { val shape = Vec3Int.full(bucket.bucketLength) val offset = Vec3Int(bucket.topLeft.voxelXInMag, bucket.topLeft.voxelYInMag, bucket.topLeft.voxelZInMag) n5Array.readBytesXYZ(shape, offset) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5DataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5DataLayers.scala index 945196c816b..205567872a2 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5DataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/n5/N5DataLayers.scala @@ -36,7 +36,8 @@ case class N5DataLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends N5Layer object N5DataLayer { @@ -53,7 +54,8 @@ case class N5SegmentationLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends SegmentationLayer with N5Layer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedBucketProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedBucketProvider.scala index 82e88e0e7e7..da0948bb880 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedBucketProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedBucketProvider.scala @@ -8,6 +8,7 @@ import com.scalableminds.webknossos.datastore.datareaders.precomputed.Precompute import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.BucketPosition import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService import com.typesafe.scalalogging.LazyLogging @@ -18,7 +19,7 @@ import ucar.ma2.{Array => MultiArray} class PrecomputedCubeHandle(precomputedArray: PrecomputedArray) extends DataCubeHandle with LazyLogging { - def cutOutBucket(bucket: BucketPosition)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { + def cutOutBucket(bucket: BucketPosition, dataLayer: DataLayer)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { val shape = Vec3Int.full(bucket.bucketLength) val offset = Vec3Int(bucket.topLeft.voxelXInMag, bucket.topLeft.voxelYInMag, bucket.topLeft.voxelZInMag) precomputedArray.readBytesXYZ(shape, offset) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedDataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedDataLayers.scala index 26107ae2572..4c0fac10bdf 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedDataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/precomputed/PrecomputedDataLayers.scala @@ -5,6 +5,7 @@ import com.scalableminds.util.geometry.{BoundingBox, Vec3Int} import com.scalableminds.webknossos.datastore.dataformats.MagLocator import com.scalableminds.webknossos.datastore.models.datasource.LayerViewConfiguration.LayerViewConfiguration import com.scalableminds.webknossos.datastore.models.datasource.{ + AdditionalAxis, Category, CoordinateTransformation, DataFormat, @@ -44,7 +45,8 @@ case class PrecomputedDataLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends PrecomputedLayer object PrecomputedDataLayer { @@ -61,7 +63,8 @@ case class PrecomputedSegmentationLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends SegmentationLayer with PrecomputedLayer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWBucketProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWBucketProvider.scala index bbffb136498..58451e6ef89 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWBucketProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWBucketProvider.scala @@ -3,6 +3,7 @@ package com.scalableminds.webknossos.datastore.dataformats.wkw import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.dataformats.{BucketProvider, DataCubeHandle} import com.scalableminds.webknossos.datastore.models.BucketPosition +import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService import com.scalableminds.webknossos.wrap.WKWFile @@ -13,7 +14,7 @@ import scala.concurrent.ExecutionContext class WKWCubeHandle(wkwFile: WKWFile, wkwFilePath: Path) extends DataCubeHandle with FoxImplicits { - def cutOutBucket(bucket: BucketPosition)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { + def cutOutBucket(bucket: BucketPosition, dataLayer: DataLayer)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { val numBlocksPerCubeDimension = wkwFile.header.numBlocksPerCubeDimension val blockOffsetX = bucket.bucketX % numBlocksPerCubeDimension val blockOffsetY = bucket.bucketY % numBlocksPerCubeDimension diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataFormatHelper.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataFormatHelper.scala index 0cb130b112f..9dd53a903f0 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataFormatHelper.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataFormatHelper.scala @@ -58,7 +58,8 @@ trait WKWDataFormatHelper { BucketPosition(x.toInt * mag.x * DataLayer.bucketLength, y.toInt * mag.y * DataLayer.bucketLength, z.toInt * mag.z * DataLayer.bucketLength, - mag) + mag, + None) } case _ => None diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala index 62709921a39..75330969176 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/wkw/WKWDataLayers.scala @@ -41,7 +41,8 @@ case class WKWDataLayer( elementClass: ElementClass.Value, defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, - coordinateTransformations: Option[List[CoordinateTransformation]] = None + coordinateTransformations: Option[List[CoordinateTransformation]] = None, + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends WKWLayer object WKWDataLayer { @@ -57,7 +58,8 @@ case class WKWSegmentationLayer( largestSegmentId: Option[Long] = None, defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, - coordinateTransformations: Option[List[CoordinateTransformation]] = None + coordinateTransformations: Option[List[CoordinateTransformation]] = None, + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends SegmentationLayer with WKWLayer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrBucketProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrBucketProvider.scala index 030daec1da8..c4dad706174 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrBucketProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrBucketProvider.scala @@ -8,6 +8,7 @@ import com.scalableminds.webknossos.datastore.datareaders.zarr.ZarrArray import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.BucketPosition import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService import com.typesafe.scalalogging.LazyLogging @@ -18,10 +19,15 @@ import scala.concurrent.ExecutionContext class ZarrCubeHandle(zarrArray: ZarrArray) extends DataCubeHandle with LazyLogging { - def cutOutBucket(bucket: BucketPosition)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { + def cutOutBucket(bucket: BucketPosition, dataLayer: DataLayer)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { val shape = Vec3Int.full(bucket.bucketLength) val offset = Vec3Int(bucket.topLeft.voxelXInMag, bucket.topLeft.voxelYInMag, bucket.topLeft.voxelZInMag) - zarrArray.readBytesXYZ(shape, offset) + + bucket.additionalCoordinates match { + case Some(additionalCoordinates) => + zarrArray.readBytesWithAdditionalCoordinates(shape, offset, additionalCoordinates, dataLayer.additionalAxisMap) + case None => zarrArray.readBytesXYZ(shape, offset) + } } override protected def onFinalize(): Unit = () diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrDataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrDataLayers.scala index 50bd0b5b4cb..f4c81efe243 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrDataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr/ZarrDataLayers.scala @@ -37,7 +37,8 @@ case class ZarrDataLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + override val additionalAxes: Option[Seq[AdditionalAxis]] ) extends ZarrLayer object ZarrDataLayer { @@ -54,7 +55,8 @@ case class ZarrSegmentationLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends SegmentationLayer with ZarrLayer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3BucketProvider.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3BucketProvider.scala index 3ea76ff384e..5a9dcb4fafc 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3BucketProvider.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3BucketProvider.scala @@ -8,6 +8,7 @@ import com.scalableminds.webknossos.datastore.datareaders.zarr3.Zarr3Array import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.BucketPosition import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import com.scalableminds.webknossos.datastore.models.requests.DataReadInstruction import com.scalableminds.webknossos.datastore.storage.RemoteSourceDescriptorService import com.typesafe.scalalogging.LazyLogging @@ -18,7 +19,7 @@ import ucar.ma2.{Array => MultiArray} class ZarrCubeHandle(zarrArray: Zarr3Array) extends DataCubeHandle with LazyLogging { - def cutOutBucket(bucket: BucketPosition)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { + def cutOutBucket(bucket: BucketPosition, dataLayer: DataLayer)(implicit ec: ExecutionContext): Fox[Array[Byte]] = { val shape = Vec3Int.full(bucket.bucketLength) val offset = Vec3Int(bucket.topLeft.voxelXInMag, bucket.topLeft.voxelYInMag, bucket.topLeft.voxelZInMag) zarrArray.readBytesXYZ(shape, offset) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3DataLayers.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3DataLayers.scala index 1bdc8abb746..beac981fc8f 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3DataLayers.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/dataformats/zarr3/Zarr3DataLayers.scala @@ -4,6 +4,7 @@ import com.scalableminds.util.cache.AlfuCache import com.scalableminds.util.geometry.{BoundingBox, Vec3Int} import com.scalableminds.webknossos.datastore.dataformats.MagLocator import com.scalableminds.webknossos.datastore.models.datasource.{ + AdditionalAxis, Category, CoordinateTransformation, DataFormat, @@ -45,7 +46,8 @@ case class Zarr3DataLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends Zarr3Layer object Zarr3DataLayer { @@ -62,7 +64,8 @@ case class Zarr3SegmentationLayer( defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, coordinateTransformations: Option[List[CoordinateTransformation]] = None, - override val numChannels: Option[Int] = Some(1) + override val numChannels: Option[Int] = Some(1), + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends SegmentationLayer with Zarr3Layer diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala index e3a64d96ab6..12156457e54 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/AxisOrder.scala @@ -2,7 +2,7 @@ package com.scalableminds.webknossos.datastore.datareaders import play.api.libs.json.{Json, OFormat} -case class AxisOrder(x: Int, y: Int, z: Int, c: Option[Int] = None, t: Option[Int] = None) { +case class AxisOrder(x: Int, y: Int, z: Int, c: Option[Int] = None) { def permutation(rank: Int): Array[Int] = c match { case Some(channel) => diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala index 92611331683..87b12b2b7f3 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/DatasetArray.scala @@ -7,6 +7,8 @@ import com.scalableminds.util.tools.Fox.{bool2Fox, box2Fox, option2Fox} import com.scalableminds.webknossos.datastore.datareaders.zarr.BytesConverter import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.AdditionalCoordinate +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.typesafe.scalalogging.LazyLogging import net.liftweb.util.Helpers.tryo import ucar.ma2.{Array => MultiArray} @@ -22,6 +24,7 @@ class DatasetArray(vaultPath: VaultPath, header: DatasetHeader, axisOrder: AxisOrder, channelIndex: Option[Int], + additionalAxes: Option[Seq[AdditionalAxis]], sharedChunkContentsCache: AlfuCache[String, MultiArray]) extends LazyLogging { @@ -40,6 +43,43 @@ class DatasetArray(vaultPath: VaultPath, readBytes(shapeArray, offsetArray) } + def readBytesWithAdditionalCoordinates( + shape: Vec3Int, + offset: Vec3Int, + additionalCoordinates: Seq[AdditionalCoordinate], + additionalAxesMap: Map[String, AdditionalAxis])(implicit ec: ExecutionContext): Fox[Array[Byte]] = { + val dimensionCount = 3 + (if (channelIndex.isDefined) 1 else 0) + additionalAxesMap.size + + /* + readAsFortranOrder only supports a shape/offset with XYZ at the end. This does not really make sense if we assume + that xyz and additional coordinates may have any index/axisorder. Since only ngff datasets are currently supported + for additional coordinates, and they follow the convention (t)(c) ... zyx, with additional coordinates before zyx, + this works for now. + */ + + val shapeArray: Array[Int] = Array.fill(dimensionCount)(1) + shapeArray(dimensionCount - 3) = shape.x + shapeArray(dimensionCount - 2) = shape.y + shapeArray(dimensionCount - 1) = shape.z + + val offsetArray: Array[Int] = Array.fill(dimensionCount)(0) + offsetArray(dimensionCount - 3) = offset.x + offsetArray(dimensionCount - 2) = offset.y + offsetArray(dimensionCount - 1) = offset.z + + channelIndex match { + case Some(c) => offsetArray(axisOrder.c.getOrElse(axisOrder.x - 1)) = c + case None => () + } + + for (additionalCoordinate <- additionalCoordinates) { + val index = additionalAxesMap(additionalCoordinate.name).index + offsetArray(index) = additionalCoordinate.value + // Shape for additional coordinates will always be 1 + } + readBytes(shapeArray, offsetArray) + } + // returns byte array in fortran-order with little-endian values private def readBytes(shape: Array[Int], offset: Array[Int])(implicit ec: ExecutionContext): Fox[Array[Byte]] = for { diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/n5/N5Array.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/n5/N5Array.scala index 096b214c46a..4405c322774 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/n5/N5Array.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/n5/N5Array.scala @@ -5,6 +5,7 @@ import com.scalableminds.util.cache.AlfuCache import com.scalableminds.webknossos.datastore.datareaders.{AxisOrder, ChunkReader, DatasetArray, DatasetHeader} import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.typesafe.scalalogging.LazyLogging import com.scalableminds.util.tools.Fox.box2Fox import ucar.ma2.{Array => MultiArray} @@ -31,6 +32,7 @@ object N5Array extends LazyLogging { header, axisOrderOpt.getOrElse(AxisOrder.asZyxFromRank(header.rank)), channelIndex, + None, sharedChunkContentsCache) } @@ -40,8 +42,16 @@ class N5Array(vaultPath: VaultPath, header: DatasetHeader, axisOrder: AxisOrder, channelIndex: Option[Int], + additionalAxes: Option[Seq[AdditionalAxis]], sharedChunkContentsCache: AlfuCache[String, MultiArray]) - extends DatasetArray(vaultPath, dataSourceId, layerName, header, axisOrder, channelIndex, sharedChunkContentsCache) + extends DatasetArray(vaultPath, + dataSourceId, + layerName, + header, + axisOrder, + channelIndex, + additionalAxes, + sharedChunkContentsCache) with LazyLogging { override protected lazy val chunkReader: ChunkReader = diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/precomputed/PrecomputedArray.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/precomputed/PrecomputedArray.scala index 0620a74f8b4..73e8e128485 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/precomputed/PrecomputedArray.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/precomputed/PrecomputedArray.scala @@ -6,6 +6,7 @@ import com.scalableminds.util.tools.{Fox, FoxImplicits, JsonHelper} import com.scalableminds.webknossos.datastore.datareaders.{AxisOrder, DatasetArray} import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.typesafe.scalalogging.LazyLogging import net.liftweb.util.Helpers.tryo @@ -32,13 +33,16 @@ object PrecomputedArray extends LazyLogging { scaleHeader = PrecomputedScaleHeader(scale, rootHeader) _ <- DatasetArray.assertChunkSizeLimit(scaleHeader.bytesPerChunk) } yield - new PrecomputedArray(magPath, - dataSourceId, - layerName, - scaleHeader, - axisOrderOpt.getOrElse(AxisOrder.asZyxFromRank(scaleHeader.rank)), - channelIndex, - sharedChunkContentsCache) + new PrecomputedArray( + magPath, + dataSourceId, + layerName, + scaleHeader, + axisOrderOpt.getOrElse(AxisOrder.asZyxFromRank(scaleHeader.rank)), + channelIndex, + None, + sharedChunkContentsCache + ) } class PrecomputedArray(vaultPath: VaultPath, @@ -47,8 +51,16 @@ class PrecomputedArray(vaultPath: VaultPath, header: PrecomputedScaleHeader, axisOrder: AxisOrder, channelIndex: Option[Int], - sharedChunkContentsCache: AlfuCache[String, MultiArray])(implicit ec: ExecutionContext) - extends DatasetArray(vaultPath, dataSourceId, layerName, header, axisOrder, channelIndex, sharedChunkContentsCache) + additionalAxes: Option[Seq[AdditionalAxis]], + sharedChunkContentsCache: AlfuCache[String, MultiArray]) + extends DatasetArray(vaultPath, + dataSourceId, + layerName, + header, + axisOrder, + channelIndex, + additionalAxes, + sharedChunkContentsCache) with FoxImplicits with LazyLogging { @@ -226,11 +238,10 @@ class PrecomputedArray(vaultPath: VaultPath, } yield minishardIndex } - private def getChunkRange(chunkId: Long, minishardIndex: Seq[(Long, Long, Long)]): Fox[NumericRange.Exclusive[Long]] = + private def getChunkRange(chunkId: Long, minishardIndex: Seq[(Long, Long, Long)])( + implicit ec: ExecutionContext): Fox[NumericRange.Exclusive[Long]] = for { - chunkSpecification <- minishardIndex - .find(_._1 == chunkId) - .toFox ?~> s"Could not find chunk id $chunkId in minishard index" + chunkSpecification <- Fox.option2Fox(minishardIndex.find(_._1 == chunkId)) ?~> s"Could not find chunk id $chunkId in minishard index" chunkStart = (shardIndexRange.end) + chunkSpecification._2 chunkEnd = (shardIndexRange.end) + chunkSpecification._2 + chunkSpecification._3 } yield Range.Long(chunkStart, chunkEnd, 1) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/ZarrArray.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/ZarrArray.scala index fc9093fba2b..5fa476fdbfa 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/ZarrArray.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr/ZarrArray.scala @@ -7,6 +7,7 @@ import com.scalableminds.webknossos.datastore.datareaders.{AxisOrder, DatasetArr import ucar.ma2.{Array => MultiArray} import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.typesafe.scalalogging.LazyLogging import scala.concurrent.ExecutionContext @@ -30,6 +31,7 @@ object ZarrArray extends LazyLogging { header, axisOrderOpt.getOrElse(AxisOrder.asZyxFromRank(header.rank)), channelIndex, + None, sharedChunkContentsCache) } @@ -39,5 +41,13 @@ class ZarrArray(vaultPath: VaultPath, header: DatasetHeader, axisOrder: AxisOrder, channelIndex: Option[Int], + additionalAxes: Option[Seq[AdditionalAxis]], sharedChunkContentsCache: AlfuCache[String, MultiArray]) - extends DatasetArray(vaultPath, dataSourceId, layerName, header, axisOrder, channelIndex, sharedChunkContentsCache) + extends DatasetArray(vaultPath, + dataSourceId, + layerName, + header, + axisOrder, + channelIndex, + additionalAxes, + sharedChunkContentsCache) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr3/Zarr3Array.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr3/Zarr3Array.scala index bbde671aa9f..bf621e44a66 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr3/Zarr3Array.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/datareaders/zarr3/Zarr3Array.scala @@ -6,6 +6,7 @@ import ucar.ma2.{Array => MultiArray} import com.scalableminds.webknossos.datastore.datareaders.{AxisOrder, ChunkReader, ChunkUtils, DatasetArray} import com.scalableminds.webknossos.datastore.datavault.VaultPath import com.scalableminds.webknossos.datastore.models.datasource.DataSourceId +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.typesafe.scalalogging.LazyLogging import com.scalableminds.util.tools.Fox.box2Fox import scala.collection.immutable.NumericRange @@ -30,6 +31,7 @@ object Zarr3Array extends LazyLogging { header, axisOrderOpt.getOrElse(AxisOrder.asCxyzFromRank(header.rank)), channelIndex, + None, sharedChunkContentsCache) } @@ -39,8 +41,16 @@ class Zarr3Array(vaultPath: VaultPath, header: Zarr3ArrayHeader, axisOrder: AxisOrder, channelIndex: Option[Int], + additionalAxes: Option[Seq[AdditionalAxis]], sharedChunkContentsCache: AlfuCache[String, MultiArray]) - extends DatasetArray(vaultPath, dataSourceId, layerName, header, axisOrder, channelIndex, sharedChunkContentsCache) + extends DatasetArray(vaultPath, + dataSourceId, + layerName, + header, + axisOrder, + channelIndex, + additionalAxes, + sharedChunkContentsCache) with LazyLogging { override protected def getChunkFilename(chunkIndex: Array[Int]): String = diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/ProtoGeometryImplicits.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/ProtoGeometryImplicits.scala index 54b2930a817..26a2345a357 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/ProtoGeometryImplicits.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/helpers/ProtoGeometryImplicits.scala @@ -2,7 +2,13 @@ package com.scalableminds.webknossos.datastore.helpers import com.scalableminds.util.geometry.{BoundingBox, Vec3Double, Vec3Int} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing.{ElementClass => ElementClassProto} -import com.scalableminds.webknossos.datastore.geometry.{BoundingBoxProto, ColorProto, Vec3DoubleProto, Vec3IntProto} +import com.scalableminds.webknossos.datastore.geometry.{ + BoundingBoxProto, + ColorProto, + Vec2IntProto, + Vec3DoubleProto, + Vec3IntProto +} import com.scalableminds.webknossos.datastore.models.datasource.ElementClass trait ProtoGeometryImplicits { @@ -39,4 +45,6 @@ trait ProtoGeometryImplicits { implicit def colorOptToProto(cOpt: Option[com.scalableminds.util.image.Color]): Option[ColorProto] = cOpt.map(colorToProto) + implicit def arrayFromVec2IntProto(p: Vec2IntProto): Array[Int] = Array(p.x, p.y) + } diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/DataRequests.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/DataRequests.scala index b96f55ed533..055f4ed2ca7 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/DataRequests.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/DataRequests.scala @@ -1,6 +1,7 @@ package com.scalableminds.webknossos.datastore.models import com.scalableminds.util.geometry.{Vec3Double, Vec3Int} +import com.scalableminds.webknossos.datastore.geometry.AdditionalCoordinateProto import com.scalableminds.webknossos.datastore.models.datasource.DataLayer import com.scalableminds.webknossos.datastore.models.requests.{Cuboid, DataServiceRequestSettings} import play.api.libs.json.{Json, OFormat} @@ -29,6 +30,7 @@ case class WebKnossosDataRequest( cubeSize: Int, fourBit: Option[Boolean], applyAgglomerate: Option[String], + additionalCoordinates: Option[Seq[AdditionalCoordinate]], version: Option[Long] ) extends AbstractDataRequest { @@ -36,7 +38,7 @@ case class WebKnossosDataRequest( Cuboid(VoxelPosition(position.x, position.y, position.z, mag), cubeSize, cubeSize, cubeSize) def settings: DataServiceRequestSettings = - DataServiceRequestSettings(halfByte = fourBit.getOrElse(false), applyAgglomerate, version) + DataServiceRequestSettings(halfByte = fourBit.getOrElse(false), applyAgglomerate, version, additionalCoordinates) } object WebKnossosDataRequest { @@ -67,3 +69,27 @@ object DataRequestCollection { implicit def requestToCollection(request: AbstractDataRequest): DataRequestCollection = List(request) } + +case class AdditionalCoordinate( + name: String, + value: Int +) { + override def toString = s"$name=$value" +} + +object AdditionalCoordinate { + implicit val jsonFormat: OFormat[AdditionalCoordinate] = Json.format[AdditionalCoordinate] + + def toProto(acOpt: Option[Seq[AdditionalCoordinate]]): Seq[AdditionalCoordinateProto] = + acOpt match { + case Some(additionalCoordinates) => + additionalCoordinates.map(ac => AdditionalCoordinateProto(ac.name, ac.value)) + case None => Seq() + } + + def hasNegativeValue(acOpt: Option[Seq[AdditionalCoordinate]]): Boolean = + acOpt match { + case Some(additionalCoordinates) => additionalCoordinates.exists(_.value < 0) + case None => false + } +} diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/Positions.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/Positions.scala index e87c3294ec6..67732cbd0c4 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/Positions.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/Positions.scala @@ -19,7 +19,7 @@ case class VoxelPosition( val voxelZInMag: Int = mag1Z / mag.z def toBucket: BucketPosition = - BucketPosition(mag1X, mag1Y, mag1Z, mag) + BucketPosition(mag1X, mag1Y, mag1Z, mag, None) def move(dx: Int, dy: Int, dz: Int): VoxelPosition = VoxelPosition(mag1X + dx, mag1Y + dy, mag1Z + dz, mag) @@ -45,7 +45,8 @@ case class BucketPosition( voxelMag1X: Int, voxelMag1Y: Int, voxelMag1Z: Int, - mag: Vec3Int + mag: Vec3Int, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] ) { val bucketLength: Int = DataLayer.bucketLength @@ -76,13 +77,13 @@ case class BucketPosition( } def nextBucketInX: BucketPosition = - BucketPosition(voxelMag1X + (bucketLength * mag.x), voxelMag1Y, voxelMag1Z, mag) + BucketPosition(voxelMag1X + (bucketLength * mag.x), voxelMag1Y, voxelMag1Z, mag, additionalCoordinates) def nextBucketInY: BucketPosition = - BucketPosition(voxelMag1X, voxelMag1Y + (bucketLength * mag.y), voxelMag1Z, mag) + BucketPosition(voxelMag1X, voxelMag1Y + (bucketLength * mag.y), voxelMag1Z, mag, additionalCoordinates) def nextBucketInZ: BucketPosition = - BucketPosition(voxelMag1X, voxelMag1Y, voxelMag1Z + (bucketLength * mag.z), mag) + BucketPosition(voxelMag1X, voxelMag1Y, voxelMag1Z + (bucketLength * mag.z), mag, additionalCoordinates) def toMag1BoundingBox: BoundingBox = new BoundingBox( @@ -93,12 +94,24 @@ case class BucketPosition( ) def hasNegativeComponent: Boolean = - voxelMag1X < 0 || voxelMag1Y < 0 || voxelMag1Z < 0 || mag.hasNegativeComponent + voxelMag1X < 0 || voxelMag1Y < 0 || voxelMag1Z < 0 || mag.hasNegativeComponent || AdditionalCoordinate + .hasNegativeValue(additionalCoordinates) def toVec3IntProto: Vec3IntProto = Vec3IntProto(bucketX, bucketY, bucketZ) + private def additionalCoordinateString = additionalCoordinates match { + case Some(coords) => s", additional coordinates: ${coords.map(_.toString()).mkString(",")}" + case None => "" + } + + def hasAdditionalCoordinates: Boolean = + additionalCoordinates match { + case Some(value) => value.nonEmpty + case None => false + } + override def toString: String = - s"BucketPosition(voxelMag1 at ($voxelMag1X, $voxelMag1Y, $voxelMag1Z), bucket at ($bucketX,$bucketY,$bucketZ), mag$mag)" + s"BucketPosition(voxelMag1 at ($voxelMag1X, $voxelMag1Y, $voxelMag1Z), bucket at ($bucketX,$bucketY,$bucketZ), mag$mag$additionalCoordinateString)" } class CubePosition( diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/AdditionalAxis.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/AdditionalAxis.scala new file mode 100644 index 00000000000..c56c6501a4a --- /dev/null +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/AdditionalAxis.scala @@ -0,0 +1,82 @@ +package com.scalableminds.webknossos.datastore.models.datasource + +import com.scalableminds.webknossos.datastore.geometry.{AdditionalAxisProto, Vec2IntProto} +import net.liftweb.common.{Box, Failure, Full} +import play.api.libs.json.{Format, Json} + +// bounds: lower bound inclusive, upper bound exclusive +case class AdditionalAxis(name: String, bounds: Array[Int], index: Int) { + def lowerBound: Int = bounds(0) + def upperBound: Int = bounds(1) +} + +object AdditionalAxis { + implicit val jsonFormat: Format[AdditionalAxis] = Json.format[AdditionalAxis] + + def toProto(additionalAxesOpt: Option[Seq[AdditionalAxis]]): Seq[AdditionalAxisProto] = + additionalAxesOpt match { + case Some(additionalCoordinates) => + additionalCoordinates.map( + additionalCoordinate => + AdditionalAxisProto(additionalCoordinate.name, + additionalCoordinate.index, + Vec2IntProto(additionalCoordinate.lowerBound, additionalCoordinate.upperBound))) + case None => Seq() + } + + def fromProto(additionalAxisProtos: Seq[AdditionalAxisProto]): Seq[AdditionalAxis] = + additionalAxisProtos.map( + p => AdditionalAxis(p.name, Array(p.bounds.x, p.bounds.y), p.index) + ) + + def merge(additionalAxeses: Seq[Option[Seq[AdditionalAxis]]]): Option[Seq[AdditionalAxis]] = { + val additionalAxesMap = scala.collection.mutable.Map[String, (Int, Int, Int)]() + additionalAxeses.foreach { + case Some(additionalAxes) => + for (additionalAxis <- additionalAxes) { + val additionalAxisToInsert = additionalAxesMap.get(additionalAxis.name) match { + case Some((existingIndex, existingLowerBound, existingUpperBound)) => + /* Index: The index can not be merged as it may describe data on a different server. Currently one index + is chosen arbitrarily. For annotations this is fine, since the index is only used for sorting there; + but merging additional coordinates describing data on a remote server with different indices is not + supported by this. + */ + (existingIndex, + math.min(existingLowerBound, additionalAxis.lowerBound), + math.max(existingUpperBound, additionalAxis.upperBound)) + case None => + (additionalAxis.index, additionalAxis.lowerBound, additionalAxis.upperBound) + } + additionalAxesMap(additionalAxis.name) = additionalAxisToInsert + } + case None => + } + val additionalAxes = additionalAxesMap.iterator.map { + case (name, (index, lowerBound, upperBound)) => + AdditionalAxis(name, Array(lowerBound, upperBound), index) + }.toSeq + if (additionalAxes.isEmpty) { + None + } else { + Some(additionalAxes) + } + } + + def mergeAndAssertSameAdditionalAxes( + additionalAxeses: Seq[Option[Seq[AdditionalAxis]]]): Box[Option[Seq[AdditionalAxis]]] = { + val merged = merge(additionalAxeses) + val mergedCount = merged match { + case Some(axes) => axes.size + case None => 0 + } + val sameAdditionalAxes = additionalAxeses.forall { + case Some(axes) => axes.size == mergedCount + case None => 0 == mergedCount + } + if (sameAdditionalAxes) { + Full(merged) + } else { + Failure("dataSet.additionalCoordinates.different") + } + } +} diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala index 8077c97689e..2bf5d8b5d6e 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataLayer.scala @@ -167,6 +167,16 @@ trait DataLayerLike { def adminViewConfiguration: Option[LayerViewConfiguration] def coordinateTransformations: Option[List[CoordinateTransformation]] + + // n-dimensional datasets = 3-dimensional datasets with additional coordinate axes + def additionalAxes: Option[Seq[AdditionalAxis]] + + def additionalAxisMap: Map[String, AdditionalAxis] = + additionalAxes match { + case Some(additionalAxis) => + additionalAxis.map(additionalAxis => (additionalAxis.name -> additionalAxis)).toMap + case None => Map() + } } object DataLayerLike { @@ -278,7 +288,8 @@ case class AbstractDataLayer( elementClass: ElementClass.Value, defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, - coordinateTransformations: Option[List[CoordinateTransformation]] = None + coordinateTransformations: Option[List[CoordinateTransformation]] = None, + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends DataLayerLike object AbstractDataLayer { @@ -292,7 +303,8 @@ object AbstractDataLayer { layer.elementClass, layer.defaultViewConfiguration, layer.adminViewConfiguration, - layer.coordinateTransformations + layer.coordinateTransformations, + layer.additionalAxes ) implicit val jsonFormat: OFormat[AbstractDataLayer] = Json.format[AbstractDataLayer] @@ -308,7 +320,8 @@ case class AbstractSegmentationLayer( mappings: Option[Set[String]], defaultViewConfiguration: Option[LayerViewConfiguration] = None, adminViewConfiguration: Option[LayerViewConfiguration] = None, - coordinateTransformations: Option[List[CoordinateTransformation]] = None + coordinateTransformations: Option[List[CoordinateTransformation]] = None, + additionalAxes: Option[Seq[AdditionalAxis]] = None ) extends SegmentationLayerLike object AbstractSegmentationLayer { @@ -324,7 +337,8 @@ object AbstractSegmentationLayer { layer.mappings, layer.defaultViewConfiguration, layer.adminViewConfiguration, - layer.coordinateTransformations + layer.coordinateTransformations, + layer.additionalAxes ) implicit val jsonFormat: OFormat[AbstractSegmentationLayer] = Json.format[AbstractSegmentationLayer] diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataSource.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataSource.scala index de23c48d3ba..fad5979dcce 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataSource.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/datasource/DataSource.scala @@ -47,6 +47,10 @@ package object datasource { case layer: SegmentationLayer => Some(layer) case _ => None } + + def additionalAxesUnion: Option[Seq[AdditionalAxis]] = + AdditionalAxis.merge(dataLayers.map(_.additionalAxes)) + } object GenericDataSource { diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/requests/DataServiceRequests.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/requests/DataServiceRequests.scala index 94cc3c3b143..a89365a4b5f 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/requests/DataServiceRequests.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/models/requests/DataServiceRequests.scala @@ -1,13 +1,15 @@ package com.scalableminds.webknossos.datastore.models.requests import com.scalableminds.util.geometry.Vec3Int -import com.scalableminds.webknossos.datastore.models.{BucketPosition, CubePosition} +import com.scalableminds.webknossos.datastore.models.{AdditionalCoordinate, BucketPosition, CubePosition} import com.scalableminds.webknossos.datastore.models.datasource.{DataLayer, DataSource, SegmentationLayer} + import java.nio.file.Path case class DataServiceRequestSettings(halfByte: Boolean = false, appliedAgglomerate: Option[String] = None, - version: Option[Long] = None) + version: Option[Long] = None, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) object DataServiceRequestSettings { val default: DataServiceRequestSettings = DataServiceRequestSettings(halfByte = false) diff --git a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/BinaryDataService.scala b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/BinaryDataService.scala index 5a2d64a54f9..78df5b846b9 100644 --- a/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/BinaryDataService.scala +++ b/webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/BinaryDataService.scala @@ -42,12 +42,13 @@ class BinaryDataService(val dataBaseDir: Path, Fox.failure("Invalid cuboid dimensions (must be > 0 and <= 512).") } else if (request.cuboid.isSingleBucket(DataLayer.bucketLength) && request.subsamplingStrides == Vec3Int(1, 1, 1)) { bucketQueue.headOption.toFox.flatMap { bucket => - handleBucketRequest(request, bucket) + handleBucketRequest(request, bucket.copy(additionalCoordinates = request.settings.additionalCoordinates)) } } else { Fox.sequence { bucketQueue.toList.map { bucket => - handleBucketRequest(request, bucket).map(r => bucket -> r) + handleBucketRequest(request, bucket.copy(additionalCoordinates = request.settings.additionalCoordinates)) + .map(r => bucket -> r) } }.map(buckets => cutOutCuboid(request, buckets.flatten)) } diff --git a/webknossos-datastore/proto/SkeletonTracing.proto b/webknossos-datastore/proto/SkeletonTracing.proto index 8d06f6e848f..987baf80a78 100644 --- a/webknossos-datastore/proto/SkeletonTracing.proto +++ b/webknossos-datastore/proto/SkeletonTracing.proto @@ -14,6 +14,7 @@ message Node { required int32 bitDepth = 7; required bool interpolation = 8; required int64 createdTimestamp = 9; + repeated AdditionalCoordinateProto additionalCoordinates = 10; } message Edge { @@ -70,6 +71,8 @@ message SkeletonTracing { repeated TreeGroup treeGroups = 11; repeated NamedBoundingBoxProto userBoundingBoxes = 12; optional string organizationName = 13; // used when parsing and handling nmls, not used in tracing store anymore, do not rely on correct values + repeated AdditionalCoordinateProto editPositionAdditionalCoordinates = 21; + repeated AdditionalAxisProto additionalAxes = 22; // Additional axes for which this tracing is defined } message SkeletonTracingOpt { diff --git a/webknossos-datastore/proto/VolumeTracing.proto b/webknossos-datastore/proto/VolumeTracing.proto index 0808ec28300..e8ac4183e39 100644 --- a/webknossos-datastore/proto/VolumeTracing.proto +++ b/webknossos-datastore/proto/VolumeTracing.proto @@ -11,6 +11,7 @@ message Segment { optional int64 creationTime = 4; optional ColorProto color = 5; optional int32 groupId = 6; + repeated AdditionalCoordinateProto anchorPositionAdditionalCoordinates = 7; } message VolumeTracing { @@ -42,6 +43,8 @@ message VolumeTracing { optional bool mappingIsEditable = 18; repeated SegmentGroup segmentGroups = 19; optional bool hasSegmentIndex = 20; + repeated AdditionalCoordinateProto editPositionAdditionalCoordinates = 21; + repeated AdditionalAxisProto additionalAxes = 22; // Additional axes for which this tracing is defined } message SegmentGroup { diff --git a/webknossos-datastore/proto/geometry.proto b/webknossos-datastore/proto/geometry.proto index 9297162d95e..4868bd6296e 100644 --- a/webknossos-datastore/proto/geometry.proto +++ b/webknossos-datastore/proto/geometry.proto @@ -9,6 +9,11 @@ message Vec3IntProto { required int32 z = 3; } +message Vec2IntProto { + required int32 x = 1; + required int32 y = 2; +} + message ListOfVec3IntProto { repeated Vec3IntProto values = 1; } @@ -40,3 +45,14 @@ message NamedBoundingBoxProto { optional ColorProto color = 4; required BoundingBoxProto boundingBox = 5; } + +message AdditionalCoordinateProto { + required string name = 1; + required int32 value = 2; +} + +message AdditionalAxisProto { + required string name = 1; + required int32 index = 2; + required Vec2IntProto bounds = 3; +} diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala index f88360febfe..9688bb73674 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/SkeletonTracingController.scala @@ -40,9 +40,9 @@ class SkeletonTracingController @Inject()(val tracingService: SkeletonTracingSer log() { accessTokenService.validateAccess(UserAccessRequest.webknossos, urlOrHeaderToken(token, request)) { val tracings: List[Option[SkeletonTracing]] = request.body - val mergedTracing = tracingService.merge(tracings.flatten, MergedVolumeStats.empty(), Empty) - val processedTracing = tracingService.remapTooLargeTreeIds(mergedTracing) for { + mergedTracing <- Fox.box2Fox(tracingService.merge(tracings.flatten, MergedVolumeStats.empty(), Empty)) + processedTracing = tracingService.remapTooLargeTreeIds(mergedTracing) newId <- tracingService.save(processedTracing, None, processedTracing.version, toCache = !persist) } yield Ok(Json.toJson(newId)) } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TracingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TracingController.scala index 704b161241e..3dafd8c6142 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TracingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/TracingController.scala @@ -257,7 +257,8 @@ trait TracingController[T <: GeneratedMessage, Ts <: GeneratedMessage] extends C case Empty => Fox.successful(None) case f: Failure => f.toFox } - mergedTracing = tracingService.merge(tracingsWithIds.map(_._1), mergedVolumeStats, newEditableMappingIdOpt) + mergedTracing <- Fox.box2Fox( + tracingService.merge(tracingsWithIds.map(_._1), mergedVolumeStats, newEditableMappingIdOpt)) _ <- tracingService.save(mergedTracing, Some(newId), version = 0, toCache = !persist) } yield Ok(Json.toJson(newId)) } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala index 01776d7b34f..2ac44c0a29b 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingController.scala @@ -96,16 +96,17 @@ class VolumeTracingController @Inject()( Action.async(validateProto[VolumeTracings]) { implicit request => log() { accessTokenService.validateAccess(UserAccessRequest.webknossos, urlOrHeaderToken(token, request)) { - val tracings: List[Option[VolumeTracing]] = request.body - val shouldCreateSegmentIndex = volumeSegmentIndexService.shouldCreateSegmentIndexForMerged(tracings.flatten) - val mergedTracing = - tracingService - .merge(tracings.flatten, MergedVolumeStats.empty(shouldCreateSegmentIndex), Empty) - // segment lists for multi-volume uploads are not supported yet, compare https://github.com/scalableminds/webknossos/issues/6887 - .copy(segments = List.empty) - tracingService.save(mergedTracing, None, mergedTracing.version, toCache = !persist).map { newId => - Ok(Json.toJson(newId)) - } + for { + _ <- Fox.successful(()) + tracings = request.body + shouldCreateSegmentIndex = volumeSegmentIndexService.shouldCreateSegmentIndexForMerged(tracings.flatten) + mt <- tracingService.merge(tracings.flatten, MergedVolumeStats.empty(shouldCreateSegmentIndex), Empty).toFox + + // segment lists for multi-volume uploads are not supported yet, compare https://github.com/scalableminds/webknossos/issues/6887 + mergedTracing = mt.copy(segments = List.empty) + + newId <- tracingService.save(mergedTracing, None, mergedTracing.version, toCache = !persist) + } yield Ok(Json.toJson(newId)) } } } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala index 6ffd7587086..5334147012c 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala @@ -199,7 +199,8 @@ class VolumeTracingZarrStreamingController @Inject()( cubeSize = cubeSize, fourBit = Some(false), applyAgglomerate = None, - version = None + version = None, + additionalCoordinates = None ) (data, missingBucketIndices) <- if (tracing.getMappingIsEditable) editableMappingService.volumeData(tracing, tracingId, List(wkRequest), urlOrHeaderToken(token, request)) @@ -234,7 +235,8 @@ class VolumeTracingZarrStreamingController @Inject()( cubeSize = cubeSize, fourBit = Some(false), applyAgglomerate = tracing.mappingName, - version = None + version = None, + additionalCoordinates = None ) (fallbackData, fallbackMissingBucketIndices) <- remoteDataStoreClient.getData(remoteFallbackLayer, List(request), diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TracingService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TracingService.scala index 24fc7de0a26..267b6c55a29 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TracingService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/TracingService.scala @@ -5,6 +5,7 @@ import com.scalableminds.webknossos.tracingstore.TracingStoreRedisStore import com.scalableminds.webknossos.tracingstore.tracings.TracingType.TracingType import com.scalableminds.webknossos.tracingstore.tracings.volume.MergedVolumeStats import com.typesafe.scalalogging.LazyLogging +import net.liftweb.common.Box import play.api.libs.json._ import scalapb.{GeneratedMessage, GeneratedMessageCompanion} @@ -184,7 +185,7 @@ trait TracingService[T <: GeneratedMessage] def handledGroupIdStoreContains(tracingId: String, transactionId: String, version: Long): Fox[Boolean] = handledGroupIdStore.contains(handledGroupKey(tracingId, transactionId, version)) - def merge(tracings: Seq[T], mergedVolumeStats: MergedVolumeStats, newEditableMappingIdOpt: Option[String]): T + def merge(tracings: Seq[T], mergedVolumeStats: MergedVolumeStats, newEditableMappingIdOpt: Option[String]): Box[T] def remapTooLargeTreeIds(tracing: T): T = tracing diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala index 7ede2f0602e..12f99843b7d 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/editablemapping/EditableMappingLayer.scala @@ -9,6 +9,7 @@ import com.scalableminds.webknossos.datastore.helpers.ProtoGeometryImplicits import com.scalableminds.webknossos.datastore.models.{BucketPosition, WebKnossosDataRequest} import com.scalableminds.webknossos.datastore.models.datasource.LayerViewConfiguration.LayerViewConfiguration import com.scalableminds.webknossos.datastore.models.datasource.{ + AdditionalAxis, CoordinateTransformation, DataFormat, DataLayer, @@ -46,7 +47,8 @@ class EditableMappingBucketProvider(layer: EditableMappingLayer) extends BucketP cubeSize = layer.lengthOfUnderlyingCubes(bucket.mag), fourBit = None, applyAgglomerate = None, - version = None + version = None, + additionalCoordinates = readInstruction.bucket.additionalCoordinates ) (unmappedData, indices) <- layer.editableMappingService.getFallbackDataFromDatastore(remoteFallbackLayer, List(dataRequest), @@ -93,4 +95,6 @@ case class EditableMappingLayer(name: String, override def defaultViewConfiguration: Option[LayerViewConfiguration] = None override def adminViewConfiguration: Option[LayerViewConfiguration] = None + + override def additionalAxes: Option[Seq[AdditionalAxis]] = None } 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 c5a9a9eb425..0ab51b05484 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 @@ -6,12 +6,13 @@ import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.SkeletonTracing.SkeletonTracing import com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto import com.scalableminds.webknossos.datastore.helpers.{ProtoGeometryImplicits, SkeletonTracingDefaults} +import com.scalableminds.webknossos.datastore.models.datasource.AdditionalAxis import com.scalableminds.webknossos.tracingstore.TracingStoreRedisStore import com.scalableminds.webknossos.tracingstore.tracings.UpdateAction.SkeletonUpdateAction -import com.scalableminds.webknossos.tracingstore.tracings.{SkeletonTracingMigrationService, TracingType, _} +import com.scalableminds.webknossos.tracingstore.tracings._ import com.scalableminds.webknossos.tracingstore.tracings.skeleton.updating._ import com.scalableminds.webknossos.tracingstore.tracings.volume.MergedVolumeStats -import net.liftweb.common.{Empty, Full} +import net.liftweb.common.{Box, Empty, Full} import play.api.libs.json.{JsObject, JsValue, Json} import scala.concurrent.ExecutionContext @@ -161,33 +162,39 @@ class SkeletonTracingService @Inject()( def merge(tracings: Seq[SkeletonTracing], mergedVolumeStats: MergedVolumeStats, - newEditableMappingIdOpt: Option[String]): SkeletonTracing = - tracings - .reduceLeft(mergeTwo) - .copy( + newEditableMappingIdOpt: Option[String]): Box[SkeletonTracing] = + for { + tracing <- tracings.map(Full(_)).reduceLeft(mergeTwo) + } yield + tracing.copy( createdTimestamp = System.currentTimeMillis(), version = 0L, ) - private def mergeTwo(tracingA: SkeletonTracing, tracingB: SkeletonTracing): SkeletonTracing = { - val nodeMapping = TreeUtils.calculateNodeMapping(tracingA.trees, tracingB.trees) - val groupMapping = GroupUtils.calculateTreeGroupMapping(tracingA.treeGroups, tracingB.treeGroups) - val mergedTrees = TreeUtils.mergeTrees(tracingA.trees, tracingB.trees, nodeMapping, groupMapping) - val mergedGroups = GroupUtils.mergeTreeGroups(tracingA.treeGroups, tracingB.treeGroups, groupMapping) - val mergedBoundingBox = combineBoundingBoxes(tracingA.boundingBox, tracingB.boundingBox) - val userBoundingBoxes = combineUserBoundingBoxes(tracingA.userBoundingBox, - tracingB.userBoundingBox, - tracingA.userBoundingBoxes, - tracingB.userBoundingBoxes) - - tracingA.copy( - trees = mergedTrees, - treeGroups = mergedGroups, - boundingBox = mergedBoundingBox, - userBoundingBox = None, - userBoundingBoxes = userBoundingBoxes - ) - } + private def mergeTwo(tracingA: Box[SkeletonTracing], tracingB: Box[SkeletonTracing]): Box[SkeletonTracing] = + for { + tracingA <- tracingA + tracingB <- tracingB + mergedAdditionalAxes <- AdditionalAxis.mergeAndAssertSameAdditionalAxes( + Seq(tracingA, tracingB).map(t => Some(AdditionalAxis.fromProto(t.additionalAxes)))) + nodeMapping = TreeUtils.calculateNodeMapping(tracingA.trees, tracingB.trees) + groupMapping = GroupUtils.calculateTreeGroupMapping(tracingA.treeGroups, tracingB.treeGroups) + mergedTrees = TreeUtils.mergeTrees(tracingA.trees, tracingB.trees, nodeMapping, groupMapping) + mergedGroups = GroupUtils.mergeTreeGroups(tracingA.treeGroups, tracingB.treeGroups, groupMapping) + mergedBoundingBox = combineBoundingBoxes(tracingA.boundingBox, tracingB.boundingBox) + userBoundingBoxes = combineUserBoundingBoxes(tracingA.userBoundingBox, + tracingB.userBoundingBox, + tracingA.userBoundingBoxes, + tracingB.userBoundingBoxes) + } yield + tracingA.copy( + trees = mergedTrees, + treeGroups = mergedGroups, + boundingBox = mergedBoundingBox, + userBoundingBox = None, + userBoundingBoxes = userBoundingBoxes, + additionalAxes = AdditionalAxis.toProto(mergedAdditionalAxes) + ) // Can be removed again when https://github.com/scalableminds/webknossos/issues/5009 is fixed override def remapTooLargeTreeIds(skeletonTracing: SkeletonTracing): SkeletonTracing = diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala index dd4b505ace7..0c6ce238975 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/updating/SkeletonUpdateActions.scala @@ -4,6 +4,7 @@ import com.scalableminds.webknossos.datastore.SkeletonTracing._ import com.scalableminds.webknossos.tracingstore.tracings._ import com.scalableminds.util.geometry.{Vec3Double, Vec3Int} import com.scalableminds.webknossos.datastore.helpers.{NodeDefaults, ProtoGeometryImplicits} +import com.scalableminds.webknossos.datastore.models.AdditionalCoordinate import com.scalableminds.webknossos.tracingstore.tracings.skeleton.updating.TreeType.TreeType import play.api.libs.json._ @@ -211,7 +212,8 @@ case class CreateNodeSkeletonAction(id: Int, timestamp: Long, actionTimestamp: Option[Long] = None, actionAuthorId: Option[String] = None, - info: Option[String] = None) + info: Option[String] = None, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends UpdateAction.SkeletonUpdateAction with SkeletonUpdateActionHelper with ProtoGeometryImplicits { @@ -226,7 +228,8 @@ case class CreateNodeSkeletonAction(id: Int, resolution getOrElse NodeDefaults.resolution, bitDepth getOrElse NodeDefaults.bitDepth, interpolation getOrElse NodeDefaults.interpolation, - createdTimestamp = timestamp + createdTimestamp = timestamp, + additionalCoordinates = AdditionalCoordinate.toProto(additionalCoordinates) ) def treeTransform(tree: Tree) = tree.withNodes(newNode +: tree.nodes) @@ -253,7 +256,8 @@ case class UpdateNodeSkeletonAction(id: Int, timestamp: Long, actionTimestamp: Option[Long] = None, actionAuthorId: Option[String] = None, - info: Option[String] = None) + info: Option[String] = None, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends UpdateAction.SkeletonUpdateAction with SkeletonUpdateActionHelper with ProtoGeometryImplicits { @@ -269,7 +273,8 @@ case class UpdateNodeSkeletonAction(id: Int, resolution getOrElse NodeDefaults.resolution, bitDepth getOrElse NodeDefaults.bitDepth, interpolation getOrElse NodeDefaults.interpolation, - createdTimestamp = timestamp + createdTimestamp = timestamp, + additionalCoordinates = AdditionalCoordinate.toProto(additionalCoordinates) ) def treeTransform(tree: Tree) = @@ -331,15 +336,19 @@ case class UpdateTracingSkeletonAction(activeNode: Option[Int], userBoundingBox: Option[com.scalableminds.util.geometry.BoundingBox], actionTimestamp: Option[Long] = None, actionAuthorId: Option[String] = None, - info: Option[String] = None) + info: Option[String] = None, + editPositionAdditionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends UpdateAction.SkeletonUpdateAction with ProtoGeometryImplicits { override def applyOn(tracing: SkeletonTracing): SkeletonTracing = - tracing.copy(editPosition = editPosition, - editRotation = editRotation, - zoomLevel = zoomLevel, - userBoundingBox = userBoundingBox, - activeNodeId = activeNode) + tracing.copy( + editPosition = editPosition, + editRotation = editRotation, + zoomLevel = zoomLevel, + userBoundingBox = userBoundingBox, + activeNodeId = activeNode, + editPositionAdditionalCoordinates = AdditionalCoordinate.toProto(editPositionAdditionalCoordinates) + ) override def addTimestamp(timestamp: Long): UpdateAction[SkeletonTracing] = this.copy(actionTimestamp = Some(timestamp)) diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala index e0a7be3c54a..555ff709bf2 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeSegmentStatisticsService.scala @@ -42,7 +42,8 @@ class VolumeSegmentStatisticsService @Inject()(volumeTracingService: VolumeTraci cubeSize = DataLayer.bucketLength, fourBit = Some(false), applyAgglomerate = None, - version = None + version = None, + additionalCoordinates = None ) }.toList for { diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingBucketHelper.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingBucketHelper.scala index 3143b9652ac..e89deee9cec 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingBucketHelper.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingBucketHelper.scala @@ -3,8 +3,8 @@ package com.scalableminds.webknossos.tracingstore.tracings.volume import com.scalableminds.util.geometry.Vec3Int import com.scalableminds.util.tools.{Fox, FoxImplicits} import com.scalableminds.webknossos.datastore.dataformats.wkw.WKWDataFormatHelper -import com.scalableminds.webknossos.datastore.models.datasource.{DataLayer, ElementClass} -import com.scalableminds.webknossos.datastore.models.{BucketPosition, WebKnossosDataRequest} +import com.scalableminds.webknossos.datastore.models.datasource.{AdditionalAxis, DataLayer, ElementClass} +import com.scalableminds.webknossos.datastore.models.{AdditionalCoordinate, BucketPosition, WebKnossosDataRequest} import com.scalableminds.webknossos.datastore.services.DataConverter import com.scalableminds.webknossos.tracingstore.tracings._ import com.scalableminds.webknossos.wrap.WKWMortonHelper @@ -65,38 +65,97 @@ trait VolumeBucketCompression extends LazyLogging { } trait BucketKeys extends WKWMortonHelper with WKWDataFormatHelper with LazyLogging { - protected def buildBucketKey(dataLayerName: String, bucket: BucketPosition): String = { + protected def buildBucketKey(dataLayerName: String, + bucket: BucketPosition, + additionalAxes: Option[Seq[AdditionalAxis]]): String = { val mortonIndex = mortonEncode(bucket.bucketX, bucket.bucketY, bucket.bucketZ) - s"$dataLayerName/${bucket.mag.toMagLiteral(allowScalar = true)}/$mortonIndex-[${bucket.bucketX},${bucket.bucketY},${bucket.bucketZ}]" + (bucket.additionalCoordinates, additionalAxes, bucket.hasAdditionalCoordinates) match { + case (Some(additionalCoordinates), Some(axes), true) => + // Bucket key additional coordinates need to be ordered to be found later. + val valueMap = additionalCoordinates.map(a => a.name -> a.value).toMap + val sortedValues = axes.sortBy(_.index).map(a => valueMap(a.name)) + + s"$dataLayerName/${bucket.mag.toMagLiteral(allowScalar = true)}/$mortonIndex-[${sortedValues + .map(_.toString) + .mkString(",")}][${bucket.bucketX},${bucket.bucketY},${bucket.bucketZ}]" + case _ => + s"$dataLayerName/${bucket.mag.toMagLiteral(allowScalar = true)}/$mortonIndex-[${bucket.bucketX},${bucket.bucketY},${bucket.bucketZ}]" + } + } protected def buildKeyPrefix(dataLayerName: String): String = s"$dataLayerName/" - protected def parseBucketKey(key: String): Option[(String, BucketPosition)] = { - val keyRx = "([0-9a-z-]+)/(\\d+|\\d+-\\d+-\\d+)/-?\\d+-\\[(\\d+),(\\d+),(\\d+)]".r + protected def parseBucketKey(key: String, + additionalAxes: Option[Seq[AdditionalAxis]]): Option[(String, BucketPosition)] = + additionalAxes match { + case Some(value) => parseBucketKeyWithAdditionalAxes(key, value) + case None => parseBucketKeyXYZ(key) + } + private def parseBucketKeyXYZ(key: String) = { + val keyRx = "([0-9a-z-]+)/(\\d+|\\d+-\\d+-\\d+)/-?\\d+-\\[(\\d+),(\\d+),(\\d+)]".r key match { case keyRx(name, resolutionStr, xStr, yStr, zStr) => - val resolutionOpt = Vec3Int.fromMagLiteral(resolutionStr, allowScalar = true) - resolutionOpt match { - case Some(resolution) => - val x = xStr.toInt - val y = yStr.toInt - val z = zStr.toInt - val bucket = BucketPosition(x * resolution.x * DataLayer.bucketLength, - y * resolution.y * DataLayer.bucketLength, - z * resolution.z * DataLayer.bucketLength, - resolution) - Some((name, bucket)) - case _ => None - } + getBucketPosition(xStr, yStr, zStr, resolutionStr, None).map(bucketPosition => (name, bucketPosition)) + case _ => + None + } + } + + private def parseBucketKeyWithAdditionalAxes( + key: String, + additionalAxes: Seq[AdditionalAxis]): Option[(String, BucketPosition)] = { + val additionalCoordinateCapture = Array.fill(additionalAxes.length)("(\\d+)").mkString(",") + val keyRx = s"([0-9a-z-]+)/(\\d+|\\d+-\\d+-\\d+)/-?\\d+-\\[$additionalCoordinateCapture]\\[(\\d+),(\\d+),(\\d+)]".r + val matchOpt = keyRx.findFirstMatchIn(key) + matchOpt match { + case Some(aMatch) => + val name = aMatch.group(1) + val resolutionStr = aMatch.group(2) + val xStr = aMatch.group(additionalAxes.length + 3) + val yStr = aMatch.group(additionalAxes.length + 4) + val zStr = aMatch.group(additionalAxes.length + 5) + + val additionalAxesIndexSorted = additionalAxes.sortBy(_.index) + val additionalCoordinates: Seq[AdditionalCoordinate] = + (3 until additionalAxes.length + 3).zipWithIndex.map( + groupIndexAndAxisIndex => + AdditionalCoordinate(additionalAxesIndexSorted(groupIndexAndAxisIndex._2).name, + aMatch.group(groupIndexAndAxisIndex._1).toInt)) + + getBucketPosition(xStr, yStr, zStr, resolutionStr, Some(additionalCoordinates)).map(bucketPosition => + (name, bucketPosition)) case _ => None } } + private def getBucketPosition(xStr: String, + yStr: String, + zStr: String, + resolutionStr: String, + additionalCoordinates: Option[Seq[AdditionalCoordinate]]): Option[BucketPosition] = { + val resolutionOpt = Vec3Int.fromMagLiteral(resolutionStr, allowScalar = true) + resolutionOpt match { + case Some(resolution) => + val x = xStr.toInt + val y = yStr.toInt + val z = zStr.toInt + val bucket = BucketPosition( + x * resolution.x * DataLayer.bucketLength, + y * resolution.y * DataLayer.bucketLength, + z * resolution.z * DataLayer.bucketLength, + resolution, + additionalCoordinates + ) + Some(bucket) + case _ => None + } + } + } trait VolumeTracingBucketHelper @@ -121,7 +180,7 @@ trait VolumeTracingBucketHelper def loadBucket(dataLayer: VolumeTracingLayer, bucket: BucketPosition, version: Option[Long] = None): Fox[Array[Byte]] = { - val key = buildBucketKey(dataLayer.name, bucket) + val key = buildBucketKey(dataLayer.name, bucket, dataLayer.additionalAxes) val dataFox = loadBucketFromTemporaryStore(key) match { case Some(data) => Fox.successful(data) @@ -153,7 +212,8 @@ trait VolumeTracingBucketHelper cubeSize = dataLayer.lengthOfUnderlyingCubes(bucket.mag), fourBit = None, applyAgglomerate = dataLayer.tracing.mappingName, - version = None + version = None, + additionalCoordinates = None ) for { remoteFallbackLayer <- dataLayer.volumeTracingService @@ -170,15 +230,22 @@ trait VolumeTracingBucketHelper data: Array[Byte], version: Long, toTemporaryStore: Boolean = false): Fox[Unit] = - saveBucket(dataLayer.name, dataLayer.elementClass, bucket, data, version, toTemporaryStore) + saveBucket(dataLayer.name, + dataLayer.elementClass, + bucket, + data, + version, + toTemporaryStore, + dataLayer.additionalAxes) protected def saveBucket(tracingId: String, elementClass: ElementClass.Value, bucket: BucketPosition, data: Array[Byte], version: Long, - toTemporaryStore: Boolean): Fox[Unit] = { - val key = buildBucketKey(tracingId, bucket) + toTemporaryStore: Boolean, + additionalAxes: Option[Seq[AdditionalAxis]]): Fox[Unit] = { + val key = buildBucketKey(tracingId, bucket, additionalAxes) val compressedBucket = compressVolumeBucket(data, expectedUncompressedBucketSizeFor(elementClass)) if (toTemporaryStore) { // Note that this temporary store is for temporary volumes only (e.g. compound projects) @@ -191,13 +258,21 @@ trait VolumeTracingBucketHelper def bucketStream(dataLayer: VolumeTracingLayer, version: Option[Long]): Iterator[(BucketPosition, Array[Byte])] = { val key = buildKeyPrefix(dataLayer.name) - new BucketIterator(key, volumeDataStore, expectedUncompressedBucketSizeFor(dataLayer), version) + new BucketIterator(key, + volumeDataStore, + expectedUncompressedBucketSizeFor(dataLayer), + version, + dataLayer.additionalAxes) } def bucketStreamWithVersion(dataLayer: VolumeTracingLayer, version: Option[Long]): Iterator[(BucketPosition, Array[Byte], Long)] = { val key = buildKeyPrefix(dataLayer.name) - new VersionedBucketIterator(key, volumeDataStore, expectedUncompressedBucketSizeFor(dataLayer), version) + new VersionedBucketIterator(key, + volumeDataStore, + expectedUncompressedBucketSizeFor(dataLayer), + version, + dataLayer.additionalAxes) } def bucketStreamFromTemporaryStore(dataLayer: VolumeTracingLayer): Iterator[(BucketPosition, Array[Byte])] = { @@ -205,7 +280,7 @@ trait VolumeTracingBucketHelper val keyValuePairs = temporaryVolumeDataStore.findAllConditionalWithKey(key => key.startsWith(keyPrefix)) keyValuePairs.flatMap { case (bucketKey, data) => - parseBucketKey(bucketKey).map(tuple => (tuple._2, data)) + parseBucketKey(bucketKey, dataLayer.additionalAxes).map(tuple => (tuple._2, data)) }.toIterator } } @@ -213,7 +288,8 @@ trait VolumeTracingBucketHelper class VersionedBucketIterator(prefix: String, volumeDataStore: FossilDBClient, expectedUncompressedBucketSize: Int, - version: Option[Long] = None) + version: Option[Long] = None, + additionalAxes: Option[Seq[AdditionalAxis]]) extends Iterator[(BucketPosition, Array[Byte], Long)] with KeyValueStoreImplicits with VolumeBucketCompression @@ -239,7 +315,7 @@ class VersionedBucketIterator(prefix: String, if (currentBatchIterator.hasNext) { val bucket = currentBatchIterator.next currentStartAfterKey = Some(bucket.key) - if (isRevertedBucket(bucket) || parseBucketKey(bucket.key).isEmpty) { + if (isRevertedBucket(bucket) || parseBucketKey(bucket.key, additionalAxes).isEmpty) { getNextNonRevertedBucket } else { Some(bucket) @@ -262,7 +338,7 @@ class VersionedBucketIterator(prefix: String, case None => getNextNonRevertedBucket.get } nextBucket = None - parseBucketKey(nextRes.key) + parseBucketKey(nextRes.key, additionalAxes) .map(key => { val debugInfo = s"key: ${nextRes.key}, ${nextRes.value.length} bytes, version ${nextRes.version}" (key._2, decompressIfNeeded(nextRes.value, expectedUncompressedBucketSize, debugInfo), nextRes.version) @@ -275,10 +351,11 @@ class VersionedBucketIterator(prefix: String, class BucketIterator(prefix: String, volumeDataStore: FossilDBClient, expectedUncompressedBucketSize: Int, - version: Option[Long] = None) + version: Option[Long] = None, + additionalAxes: Option[Seq[AdditionalAxis]]) extends Iterator[(BucketPosition, Array[Byte])] { private val versionedBucketIterator = - new VersionedBucketIterator(prefix, volumeDataStore, expectedUncompressedBucketSize, version) + new VersionedBucketIterator(prefix, volumeDataStore, expectedUncompressedBucketSize, version, additionalAxes) override def next: (BucketPosition, Array[Byte]) = { val tuple = versionedBucketIterator.next diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDownsampling.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDownsampling.scala index 58f693e263b..e36ea6c1b2f 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDownsampling.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingDownsampling.scala @@ -123,7 +123,7 @@ trait VolumeTracingDownsampling val data: List[VersionedKeyValuePair[Array[Byte]]] = tracingDataStore.volumeData.getMultipleKeys(None, Some(tracingId)) data.foreach { keyValuePair: VersionedKeyValuePair[Array[Byte]] => - val bucketPositionOpt = parseBucketKey(keyValuePair.key).map(_._2) + val bucketPositionOpt = parseBucketKey(keyValuePair.key, dataLayer.additionalAxes).map(_._2) bucketPositionOpt.foreach { bucketPosition => if (bucketPosition.mag == sourceMag) { bucketDataMap(bucketPosition) = decompressIfNeeded(keyValuePair.value, @@ -170,7 +170,8 @@ trait VolumeTracingDownsampling (bucketPosition.voxelMag1X / requiredMag.x / 32) * requiredMag.x * 32, (bucketPosition.voxelMag1Y / requiredMag.y / 32) * requiredMag.y * 32, (bucketPosition.voxelMag1Z / requiredMag.z / 32) * requiredMag.z * 32, - requiredMag + requiredMag, + bucketPosition.additionalCoordinates ) }.toSet @@ -186,7 +187,8 @@ trait VolumeTracingDownsampling bucketPosition.voxelMag1X + x * bucketPosition.bucketLength * previousMag.x, bucketPosition.voxelMag1Y + y * bucketPosition.bucketLength * previousMag.y, bucketPosition.voxelMag1Z + z * bucketPosition.bucketLength * previousMag.z, - previousMag + previousMag, + bucketPosition.additionalCoordinates ) } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala index 5edbafbb6ea..f55e190565d 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingLayer.scala @@ -78,6 +78,7 @@ case class VolumeTracingLayer( includeFallbackDataIfAvailable: Boolean = false, tracing: VolumeTracing, userToken: Option[String], + additionalAxes: Option[Seq[AdditionalAxis]] )(implicit val volumeDataStore: FossilDBClient, implicit val volumeDataCache: TemporaryVolumeDataStore, implicit val temporaryTracingStore: TemporaryTracingStore[VolumeTracing], @@ -117,4 +118,5 @@ case class VolumeTracingLayer( override def containsResolution(resolution: Vec3Int) = true // allow requesting buckets of all resolutions. database takes care of missing. + } diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala index b8227114243..99a3646f59f 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeTracingService.scala @@ -9,10 +9,10 @@ import com.scalableminds.webknossos.datastore.dataformats.wkw.{WKWBucketStreamSi import com.scalableminds.webknossos.datastore.geometry.NamedBoundingBoxProto import com.scalableminds.webknossos.datastore.helpers.ProtoGeometryImplicits import com.scalableminds.webknossos.datastore.models.DataRequestCollection.DataRequestCollection -import com.scalableminds.webknossos.datastore.models.datasource.ElementClass +import com.scalableminds.webknossos.datastore.models.datasource.{AdditionalAxis, ElementClass} import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing.{ElementClass => ElementClassProto} import com.scalableminds.webknossos.datastore.models.requests.DataServiceDataRequest -import com.scalableminds.webknossos.datastore.models.{BucketPosition, WebKnossosIsosurfaceRequest} +import com.scalableminds.webknossos.datastore.models.{AdditionalCoordinate, BucketPosition, WebKnossosIsosurfaceRequest} import com.scalableminds.webknossos.datastore.services._ import com.scalableminds.webknossos.tracingstore.tracings.TracingType.TracingType import com.scalableminds.webknossos.tracingstore.tracings._ @@ -122,7 +122,9 @@ class VolumeTracingService @Inject()( editPosition = a.editPosition, editRotation = a.editRotation, largestSegmentId = a.largestSegmentId, - zoomLevel = a.zoomLevel + zoomLevel = a.zoomLevel, + editPositionAdditionalCoordinates = + AdditionalCoordinate.toProto(a.editPositionAdditionalCoordinates) )) case a: RevertToVersionVolumeAction => revertToVolumeVersion(tracingId, a.sourceVersion, updateGroup.version, tracing) @@ -152,7 +154,11 @@ class VolumeTracingService @Inject()( userToken: Option[String]): Fox[VolumeTracing] = for { _ <- assertMagIsValid(volumeTracing, action.mag) ?~> s"Received a mag-${action.mag.toMagLiteral(allowScalar = true)} bucket, which is invalid for this annotation." - bucketPosition = BucketPosition(action.position.x, action.position.y, action.position.z, action.mag) + bucketPosition = BucketPosition(action.position.x, + action.position.y, + action.position.z, + action.mag, + action.additionalCoordinates) _ <- bool2Fox(!bucketPosition.hasNegativeComponent) ?~> s"Received a bucket at negative position ($bucketPosition), must be positive" dataLayer = volumeTracingLayer(tracingId, volumeTracing) _ <- saveBucket(dataLayer, bucketPosition, action.data, updateGroupVersion) ?~> "failed to save bucket" @@ -420,7 +426,8 @@ class VolumeTracingService @Inject()( volumeTracingService = this, includeFallbackDataIfAvailable = includeFallbackDataIfAvailable, tracing = tracing, - userToken = userToken + userToken = userToken, + additionalAxes = Some(AdditionalAxis.fromProto(tracing.additionalAxes)) ) def updateActionLog(tracingId: String, @@ -504,16 +511,21 @@ class VolumeTracingService @Inject()( def merge(tracings: Seq[VolumeTracing], mergedVolumeStats: MergedVolumeStats, - newEditableMappingIdOpt: Option[String]): VolumeTracing = { - def mergeTwoWithStats(tracingAWithIndex: (VolumeTracing, Int), - tracingBWithIndex: (VolumeTracing, Int)): (VolumeTracing, Int) = - (mergeTwo(tracingAWithIndex._1, tracingBWithIndex._1, tracingBWithIndex._2, mergedVolumeStats), - tracingAWithIndex._2) - - tracings.zipWithIndex - .reduceLeft(mergeTwoWithStats) - ._1 - .copy( + newEditableMappingIdOpt: Option[String]): Box[VolumeTracing] = { + def mergeTwoWithStats(tracingAWithIndex: Box[(VolumeTracing, Int)], + tracingBWithIndex: Box[(VolumeTracing, Int)]): Box[(VolumeTracing, Int)] = + for { + tracingAWithIndex <- tracingAWithIndex + tracingBWithIndex <- tracingBWithIndex + merged <- mergeTwo(tracingAWithIndex._1, tracingBWithIndex._1, tracingBWithIndex._2, mergedVolumeStats) + } yield (merged, tracingAWithIndex._2) + + for { + tracingsWithIndex <- Full(tracings.zipWithIndex.map(Full(_))) + tracingAndIndex <- tracingsWithIndex.reduceLeft(mergeTwoWithStats) + tracing <- tracingAndIndex._1 + } yield + tracing.copy( createdTimestamp = System.currentTimeMillis(), version = 0L, mappingName = newEditableMappingIdOpt, @@ -524,7 +536,7 @@ class VolumeTracingService @Inject()( private def mergeTwo(tracingA: VolumeTracing, tracingB: VolumeTracing, indexB: Int, - mergedVolumeStats: MergedVolumeStats): VolumeTracing = { + mergedVolumeStats: MergedVolumeStats): Box[VolumeTracing] = { val largestSegmentId = combineLargestSegmentIdsByMaxDefined(tracingA.largestSegmentId, tracingB.largestSegmentId) val groupMapping = GroupUtils.calculateSegmentGroupMapping(tracingA.segmentGroups, tracingB.segmentGroups) val mergedGroups = GroupUtils.mergeSegmentGroups(tracingA.segmentGroups, tracingB.segmentGroups, groupMapping) @@ -533,8 +545,10 @@ class VolumeTracingService @Inject()( tracingB.userBoundingBox, tracingA.userBoundingBoxes, tracingB.userBoundingBoxes) - val tracingBSegments = - if (indexB >= mergedVolumeStats.labelMaps.length) tracingB.segments + for { + mergedAdditionalAxes <- AdditionalAxis.mergeAndAssertSameAdditionalAxes( + Seq(tracingA, tracingB).map(t => Some(AdditionalAxis.fromProto(t.additionalAxes)))) + tracingBSegments = if (indexB >= mergedVolumeStats.labelMaps.length) tracingB.segments else { val labelMap = mergedVolumeStats.labelMaps(indexB) tracingB.segments.map { segment => @@ -543,18 +557,20 @@ class VolumeTracingService @Inject()( ) } } - tracingA.copy( - largestSegmentId = largestSegmentId, - boundingBox = mergedBoundingBox.getOrElse( - com.scalableminds.webknossos.datastore.geometry.BoundingBoxProto( - com.scalableminds.webknossos.datastore.geometry.Vec3IntProto(0, 0, 0), - 0, - 0, - 0)), // should never be empty for volumes - userBoundingBoxes = userBoundingBoxes, - segments = tracingA.segments.toList ::: tracingBSegments.toList, - segmentGroups = mergedGroups - ) + } yield + tracingA.copy( + largestSegmentId = largestSegmentId, + boundingBox = mergedBoundingBox.getOrElse( + com.scalableminds.webknossos.datastore.geometry.BoundingBoxProto( + com.scalableminds.webknossos.datastore.geometry.Vec3IntProto(0, 0, 0), + 0, + 0, + 0)), // should never be empty for volumes + userBoundingBoxes = userBoundingBoxes, + segments = tracingA.segments.toList ::: tracingBSegments.toList, + segmentGroups = mergedGroups, + additionalAxes = AdditionalAxis.toProto(mergedAdditionalAxes) + ) } private def combineLargestSegmentIdsByMaxDefined(aOpt: Option[Long], bOpt: Option[Long]): Option[Long] = @@ -623,9 +639,11 @@ class VolumeTracingService @Inject()( } for { _ <- bool2Fox(ElementClass.largestSegmentIdIsInRange(mergedVolume.largestSegmentId.toLong, elementClass)) ?~> "annotation.volume.largestSegmentIdExceedsRange" + mergedAdditionalAxes <- Fox.box2Fox(AdditionalAxis.mergeAndAssertSameAdditionalAxes(tracings.map(t => + Some(AdditionalAxis.fromProto(t.additionalAxes))))) _ <- mergedVolume.withMergedBuckets { (bucketPosition, bucketBytes) => for { - _ <- saveBucket(newId, elementClass, bucketPosition, bucketBytes, newVersion, toCache) + _ <- saveBucket(newId, elementClass, bucketPosition, bucketBytes, newVersion, toCache, mergedAdditionalAxes) _ <- Fox.runIf(shouldCreateSegmentIndex)( updateSegmentIndex(segmentIndexBuffer, bucketPosition, bucketBytes, Empty, elementClass)) } yield () diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala index d8d20c5a04d..10f33ebd56c 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/volume/VolumeUpdateActions.scala @@ -5,6 +5,7 @@ import com.scalableminds.util.geometry.{Vec3Double, Vec3Int} import com.scalableminds.webknossos.datastore.VolumeTracing.{Segment, SegmentGroup, VolumeTracing} import com.scalableminds.webknossos.datastore.geometry import com.scalableminds.webknossos.datastore.helpers.ProtoGeometryImplicits +import com.scalableminds.webknossos.datastore.models.AdditionalCoordinate import com.scalableminds.webknossos.tracingstore.tracings.UpdateAction.VolumeUpdateAction import com.scalableminds.webknossos.tracingstore.tracings.{NamedBoundingBox, UpdateAction} import play.api.libs.json._ @@ -30,7 +31,8 @@ case class UpdateBucketVolumeAction(position: Vec3Int, base64Data: String, actionTimestamp: Option[Long] = None, actionAuthorId: Option[String] = None, - info: Option[String] = None) + info: Option[String] = None, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends VolumeUpdateAction { lazy val data: Array[Byte] = Base64.getDecoder.decode(base64Data) @@ -54,7 +56,8 @@ case class UpdateTracingVolumeAction( zoomLevel: Double, actionTimestamp: Option[Long] = None, actionAuthorId: Option[String] = None, - info: Option[String] = None + info: Option[String] = None, + editPositionAdditionalCoordinates: Option[Seq[AdditionalCoordinate]] = None ) extends VolumeUpdateAction { override def addTimestamp(timestamp: Long): VolumeUpdateAction = this.copy(actionTimestamp = Some(timestamp)) override def addAuthorId(authorId: Option[String]): VolumeUpdateAction = @@ -215,7 +218,8 @@ case class CreateSegmentVolumeAction(id: Long, groupId: Option[Int], creationTime: Option[Long], actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None) + actionAuthorId: Option[String] = None, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends ApplyableVolumeAction with ProtoGeometryImplicits { @@ -229,7 +233,13 @@ case class CreateSegmentVolumeAction(id: Long, override def applyOn(tracing: VolumeTracing): VolumeTracing = { val newSegment = - Segment(id, anchorPosition.map(vec3IntToProto), name, creationTime, colorOptToProto(color), groupId) + Segment(id, + anchorPosition.map(vec3IntToProto), + name, + creationTime, + colorOptToProto(color), + groupId, + AdditionalCoordinate.toProto(additionalCoordinates)) tracing.addSegments(newSegment) } } @@ -245,7 +255,8 @@ case class UpdateSegmentVolumeAction(id: Long, creationTime: Option[Long], groupId: Option[Int], actionTimestamp: Option[Long] = None, - actionAuthorId: Option[String] = None) + actionAuthorId: Option[String] = None, + additionalCoordinates: Option[Seq[AdditionalCoordinate]] = None) extends ApplyableVolumeAction with ProtoGeometryImplicits with VolumeUpdateActionHelper { @@ -265,7 +276,8 @@ case class UpdateSegmentVolumeAction(id: Long, name = name, creationTime = creationTime, color = colorOptToProto(color), - groupId = groupId + groupId = groupId, + anchorPositionAdditionalCoordinates = AdditionalCoordinate.toProto(additionalCoordinates) ) tracing.withSegments(mapSegments(tracing, id, segmentTransform)) } diff --git a/yarn.lock b/yarn.lock index b3b4c9b4d8b..0565ada23f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,6 +14,14 @@ promise-polyfill "^8.1.3" tdigest "^0.1.1" +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@ant-design/colors@^6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" @@ -71,13 +79,6 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== - dependencies: - "@babel/highlight" "^7.16.7" - "@babel/code-frame@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" @@ -85,38 +86,40 @@ dependencies: "@babel/highlight" "^7.16.0" -"@babel/core@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" - "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" +"@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== + +"@babel/core@^7.18.10": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" - integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== - dependencies: - "@babel/types" "^7.12.1" - jsesc "^2.5.1" - source-map "^0.5.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.1" -"@babel/generator@^7.12.1", "@babel/generator@^7.16.5": +"@babel/generator@^7.16.5": version "7.16.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.5.tgz#26e1192eb8f78e0a3acaf3eede3c6fc96d22bedf" integrity sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA== @@ -125,14 +128,26 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.17.3": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.7.tgz#8da2599beb4a86194a3b24df6c085931d9ee45ad" - integrity sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w== +"@babel/generator@^7.18.10", "@babel/generator@^7.22.7", "@babel/generator@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" - source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" "@babel/helper-environment-visitor@^7.16.5": version "7.16.5" @@ -141,12 +156,10 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== "@babel/helper-function-name@^7.16.0": version "7.16.0" @@ -157,14 +170,13 @@ "@babel/template" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== dependencies: - "@babel/helper-get-function-arity" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/helper-get-function-arity@^7.16.0": version "7.16.0" @@ -173,13 +185,6 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-get-function-arity@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" - integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== - dependencies: - "@babel/types" "^7.16.7" - "@babel/helper-hoist-variables@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" @@ -187,40 +192,44 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.22.5" -"@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.16.7": +"@babel/helper-module-imports@^7.14.5": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.12.1": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" - integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== +"@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" -"@babel/helper-simple-access@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" - integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.22.5" "@babel/helper-split-export-declaration@^7.16.0": version "7.16.0" @@ -229,12 +238,17 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== "@babel/helper-validator-identifier@^7.15.7": version "7.15.7" @@ -246,14 +260,24 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/helpers@^7.12.1": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.5.tgz#29a052d4b827846dd76ece16f565b9634c554ebd" - integrity sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== dependencies: - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" "@babel/highlight@^7.10.4": version "7.17.9" @@ -273,34 +297,29 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.22.5" chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.3.tgz#a305415ebe7a6c7023b40b5122a0662d928334cd" - integrity sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw== - "@babel/parser@^7.0.0": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== -"@babel/parser@^7.10.5", "@babel/parser@^7.12.3", "@babel/parser@^7.16.0", "@babel/parser@^7.16.4", "@babel/parser@^7.16.5": +"@babel/parser@^7.10.5", "@babel/parser@^7.16.0", "@babel/parser@^7.16.5": version "7.16.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314" integrity sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ== -"@babel/parser@^7.16.7", "@babel/parser@^7.17.3": - version "7.17.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" - integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== +"@babel/parser@^7.18.11", "@babel/parser@^7.20.15", "@babel/parser@^7.21.3", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== "@babel/parser@^7.9.4": version "7.12.10" @@ -349,15 +368,6 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.10.4", "@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - "@babel/template@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" @@ -367,7 +377,16 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/traverse@^7.10.5", "@babel/traverse@^7.16.5": +"@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.10.5": version "7.16.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.5.tgz#d7d400a8229c714a59b87624fc67b0f1fbd4b2b3" integrity sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ== @@ -383,23 +402,31 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.12.1", "@babel/traverse@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" - integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.3" - "@babel/types" "^7.17.0" +"@babel/traverse@^7.18.11", "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.12.1", "@babel/types@^7.16.7", "@babel/types@^7.17.0": +"@babel/types@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" + integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== + dependencies: + "@babel/helper-validator-identifier" "^7.15.7" + to-fast-properties "^2.0.0" + +"@babel/types@^7.16.7": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== @@ -407,12 +434,13 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" - integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== +"@babel/types@^7.18.10", "@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== dependencies: - "@babel/helper-validator-identifier" "^7.15.7" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -591,11 +619,6 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@hutson/parse-repository-url@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" - integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== - "@hypnosphi/create-react-context@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz#f8bfebdc7665f5d426cba3753e0e9c7d3154d7c6" @@ -937,6 +960,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" @@ -960,6 +992,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@^0.3.12": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -968,6 +1005,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.14" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" @@ -1580,6 +1625,13 @@ resolved "https://registry.yarnpkg.com/@types/dagre/-/dagre-0.7.48.tgz#e3ff1962df959c22af44d58d4275d35bd9309043" integrity sha512-rF3yXSwHIrDxEkN6edCE4TXknb5YSEpiXfLaspw1I08grC49ZFuAVGOQCmZGIuLUGoFgcqGlUFBL/XrpgYpQgw== +"@types/debug@^4.0.0": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" + integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ== + dependencies: + "@types/ms" "*" + "@types/eslint-scope@^3.7.3": version "3.7.3" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" @@ -1620,6 +1672,11 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/extend@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/extend/-/extend-3.0.1.tgz#923dc2d707d944382433e01d6cc0c69030ab2c75" + integrity sha512-R1g/VyKFFI2HLC1QGAeTtCBWCo6n75l41OnsVYNbmKG+kempOESaodf6BeJyUM3Q0rKa/NQcTHbB2+66lNnxLw== + "@types/file-saver@^2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.5.tgz#9ee342a5d1314bb0928375424a2f162f97c310c7" @@ -1630,6 +1687,13 @@ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== +"@types/hast@^2.0.0": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.5.tgz#08caac88b44d0fdd04dc17a19142355f43bd8a7a" + integrity sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg== + dependencies: + "@types/unist" "^2" + "@types/history@^4.7.11": version "4.7.11" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" @@ -1685,7 +1749,7 @@ resolved "https://registry.yarnpkg.com/@types/lz-string/-/lz-string-1.3.34.tgz#69bfadde419314b4a374bf2c8e58659c035ed0a5" integrity sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow== -"@types/mdast@^3.0.0", "@types/mdast@^3.0.3": +"@types/mdast@^3.0.0": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== @@ -1712,11 +1776,6 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== -"@types/minimist@^1.2.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" - integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== - "@types/mock-require@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/mock-require/-/mock-require-2.0.1.tgz#1546819af1d8d5b124f5f70aff130cc77c53573e" @@ -1724,6 +1783,11 @@ dependencies: "@types/node" "*" +"@types/ms@*": + version "0.7.31" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/ndarray-ops@^1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@types/ndarray-ops/-/ndarray-ops-1.2.4.tgz#64ef0c02870adf585b82240437771d14888b5264" @@ -1756,6 +1820,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/normalize-package-data@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + "@types/pako@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/pako/-/pako-2.0.0.tgz#12ab4c19107528452e73ac99132c875ccd43bdfb" @@ -1766,6 +1835,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/parse5@^6.0.0": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" + integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== + "@types/pixelmatch@^5.2.4": version "5.2.4" resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.4.tgz#ca145cc5ede1388c71c68edf2d1f5190e5ddd0f6" @@ -1946,6 +2020,11 @@ dependencies: "@types/node" "*" +"@types/supports-color@^8.0.0": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.1.tgz#1b44b1b096479273adf7f93c75fc4ecc40a61ee4" + integrity sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw== + "@types/three@^0.142.0": version "0.142.0" resolved "https://registry.yarnpkg.com/@types/three/-/three-0.142.0.tgz#32ca897afc925057174a398416777fcb141666d6" @@ -1958,15 +2037,15 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40" integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg== -"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.3": +"@types/unist@*", "@types/unist@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== -"@types/unist@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" - integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== +"@types/unist@^2": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.7.tgz#5b06ad6894b236a1d2bd6b2f07850ca5c59cf4d6" + integrity sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g== "@types/url-join@^4.0.0": version "4.0.1" @@ -2110,63 +2189,63 @@ resolved "https://registry.yarnpkg.com/@use-it/interval/-/interval-1.0.0.tgz#c42c68f22ca29a0dc929041746373d94496d2b3a" integrity sha512-WQFcnSt/xM/mS8ZtJ0ut5lhPrl+V0HDPPcI/J0eUClsfiD+/r8A7IeW/pVcfpSVGWRmN3+WnjNteWuKyWs2WZg== -"@vue/compiler-core@3.2.26": - version "3.2.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.26.tgz#9ab92ae624da51f7b6064f4679c2d4564f437cc8" - integrity sha512-N5XNBobZbaASdzY9Lga2D9Lul5vdCIOXvUMd6ThcN8zgqQhPKfCV+wfAJNNJKQkSHudnYRO2gEB+lp0iN3g2Tw== +"@vue/compiler-core@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.4.tgz#7fbf591c1c19e1acd28ffd284526e98b4f581128" + integrity sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g== dependencies: - "@babel/parser" "^7.16.4" - "@vue/shared" "3.2.26" + "@babel/parser" "^7.21.3" + "@vue/shared" "3.3.4" estree-walker "^2.0.2" - source-map "^0.6.1" + source-map-js "^1.0.2" -"@vue/compiler-dom@3.2.26": - version "3.2.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.26.tgz#c7a7b55d50a7b7981dd44fc28211df1450482667" - integrity sha512-smBfaOW6mQDxcT3p9TKT6mE22vjxjJL50GFVJiI0chXYGU/xzC05QRGrW3HHVuJrmLTLx5zBhsZ2dIATERbarg== - dependencies: - "@vue/compiler-core" "3.2.26" - "@vue/shared" "3.2.26" - -"@vue/compiler-sfc@^3.0.11": - version "3.2.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.26.tgz#3ce76677e4aa58311655a3bea9eb1cb804d2273f" - integrity sha512-ePpnfktV90UcLdsDQUh2JdiTuhV0Skv2iYXxfNMOK/F3Q+2BO0AulcVcfoksOpTJGmhhfosWfMyEaEf0UaWpIw== - dependencies: - "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.26" - "@vue/compiler-dom" "3.2.26" - "@vue/compiler-ssr" "3.2.26" - "@vue/reactivity-transform" "3.2.26" - "@vue/shared" "3.2.26" +"@vue/compiler-dom@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.3.4.tgz#f56e09b5f4d7dc350f981784de9713d823341151" + integrity sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w== + dependencies: + "@vue/compiler-core" "3.3.4" + "@vue/shared" "3.3.4" + +"@vue/compiler-sfc@^3.2.37": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.3.4.tgz#b19d942c71938893535b46226d602720593001df" + integrity sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ== + dependencies: + "@babel/parser" "^7.20.15" + "@vue/compiler-core" "3.3.4" + "@vue/compiler-dom" "3.3.4" + "@vue/compiler-ssr" "3.3.4" + "@vue/reactivity-transform" "3.3.4" + "@vue/shared" "3.3.4" estree-walker "^2.0.2" - magic-string "^0.25.7" + magic-string "^0.30.0" postcss "^8.1.10" - source-map "^0.6.1" + source-map-js "^1.0.2" -"@vue/compiler-ssr@3.2.26": - version "3.2.26" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.26.tgz#fd049523341fbf4ab5e88e25eef566d862894ba7" - integrity sha512-2mywLX0ODc4Zn8qBoA2PDCsLEZfpUGZcyoFRLSOjyGGK6wDy2/5kyDOWtf0S0UvtoyVq95OTSGIALjZ4k2q/ag== +"@vue/compiler-ssr@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.3.4.tgz#9d1379abffa4f2b0cd844174ceec4a9721138777" + integrity sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ== dependencies: - "@vue/compiler-dom" "3.2.26" - "@vue/shared" "3.2.26" + "@vue/compiler-dom" "3.3.4" + "@vue/shared" "3.3.4" -"@vue/reactivity-transform@3.2.26": - version "3.2.26" - resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.26.tgz#6d8f20a4aa2d19728f25de99962addbe7c4d03e9" - integrity sha512-XKMyuCmzNA7nvFlYhdKwD78rcnmPb7q46uoR00zkX6yZrUmcCQ5OikiwUEVbvNhL5hBJuvbSO95jB5zkUon+eQ== +"@vue/reactivity-transform@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.4.tgz#52908476e34d6a65c6c21cd2722d41ed8ae51929" + integrity sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw== dependencies: - "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.26" - "@vue/shared" "3.2.26" + "@babel/parser" "^7.20.15" + "@vue/compiler-core" "3.3.4" + "@vue/shared" "3.3.4" estree-walker "^2.0.2" - magic-string "^0.25.7" + magic-string "^0.30.0" -"@vue/shared@3.2.26": - version "3.2.26" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.26.tgz#7acd1621783571b9a82eca1f041b4a0a983481d9" - integrity sha512-vPV6Cq+NIWbH5pZu+V+2QHE9y1qfuTq49uNWw4f7FDEeZaDU2H2cx5jcUZOAKW7qTrUS4k6qZPbMy1x4N96nbA== +"@vue/shared@3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.4.tgz#06e83c5027f464eef861c329be81454bc8b70780" + integrity sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ== "@webassemblyjs/ast@1.11.1": version "1.11.1" @@ -2321,14 +2400,6 @@ resolved "https://registry.yarnpkg.com/@zip.js/zip.js/-/zip.js-2.6.81.tgz#c3c9618a8e02f3a24d359a0a14d46985fea971f5" integrity sha512-VXrwa5fthYq74sIZsHarCFVSwnKdispTd/WQBgcNEuB9X0N3L5s8odRCjx9Zw6XsvpG5krqB4ZN4X0lLMyjgDA== -JSONStream@^1.0.3, JSONStream@^1.0.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - abab@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -2374,25 +2445,11 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-node@^1.6.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - acorn-walk@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== -acorn-walk@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.0.tgz#56ae4c0f434a45fff4a125e7ea95fa9c98f67a16" @@ -2413,7 +2470,7 @@ acorn@^6.0.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^7.0.0, acorn@^7.1.1: +acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -2423,11 +2480,6 @@ acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.0, acorn@^8.7.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== -add-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" - integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= - agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -2533,11 +2585,6 @@ ansi-html-community@^0.0.8: resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== -ansi-html@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -2667,13 +2714,6 @@ app-module-path@^2.2.0: resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= - dependencies: - buffer-equal "^1.0.0" - argparse@^1.0.10, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2726,11 +2766,6 @@ array-flatten@^2.1.0: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -array-ify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" - integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= - array-tree-filter@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" @@ -2936,15 +2971,10 @@ babel-plugin-macros@^3.1.0: cosmiconfig "^7.0.0" resolve "^1.19.0" -babelify@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/babelify/-/babelify-10.0.0.tgz#fe73b1a22583f06680d8d072e25a1e0d1d1d7fb5" - integrity sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg== - -bail@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" - integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== balanced-match@^1.0.0: version "1.0.2" @@ -3108,16 +3138,6 @@ body-parser@1.19.2: raw-body "2.4.3" type-is "~1.6.18" -body@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" - integrity sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk= - dependencies: - continuable-cache "^0.3.1" - error "^7.0.0" - raw-body "~1.1.0" - safe-json-parse "~1.0.1" - bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" @@ -3192,13 +3212,6 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browser-resolve@^1.7.0: - version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" - browserslist-to-esbuild@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserslist-to-esbuild/-/browserslist-to-esbuild-1.2.0.tgz#5c5b9ca73106da02e0168007396c4ec4c1e6d643" @@ -3227,6 +3240,16 @@ browserslist@^4.17.3: node-releases "^2.0.6" update-browserslist-db "^1.0.9" +browserslist@^4.21.9: + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== + dependencies: + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -3237,11 +3260,6 @@ buffer-equal@0.0.1: resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= -buffer-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -3252,11 +3270,6 @@ buffer-indexof@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - integrity sha1-mXjOMXOIxkmth5MCjDR37wRKi1E= - buffer-writer@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" @@ -3270,11 +3283,6 @@ buffer@^5.2.0, buffer@^5.2.1, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bytes@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-1.0.0.tgz#3569ede8ba34315fab99c3e92cb04c7220de1fa8" - integrity sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g= - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -3331,11 +3339,6 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -cached-path-relative@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.1.0.tgz#865576dfef39c0d6a7defde794d078f5308e3ef3" - integrity sha512-WF0LihfemtesFcJgO7xfOoOcnWzY/QHR4qeDqV44jPU3HTI54+LnfXK3SA27AVVGCdZFgjjFFaqUA9Jx7dMJZA== - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -3375,21 +3378,12 @@ callsites@^3.0.0, callsites@^3.1.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase-keys@^6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" - integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== - dependencies: - camelcase "^5.3.1" - map-obj "^4.0.0" - quick-lru "^4.0.1" - camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -3404,6 +3398,11 @@ caniuse-lite@^1.0.30001400: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz#987437b266260b640a23cd18fbddb509d7f69f3e" integrity sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg== +caniuse-lite@^1.0.30001517: + version "1.0.30001518" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001518.tgz#b3ca93904cb4699c01218246c4d77a71dbe97150" + integrity sha512-rup09/e3I0BKjncL+FesTayKtPrdwKhUufQFd3riFw1hHg8JmIFoInYfB102cFcY/pPgGmdyl/iy+jgiDi2vdA== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -3416,10 +3415,10 @@ catharsis@^0.8.11: dependencies: lodash "^4.17.14" -ccount@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" - integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== center-align@^0.1.1: version "0.1.3" @@ -3440,7 +3439,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.1, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3478,25 +3477,20 @@ chalk@^5.0.1: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== -character-entities-html4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" - integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== - -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== -character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== -character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== cheerio@^1.0.0-rc.3: version "1.0.0-rc.3" @@ -3510,21 +3504,6 @@ cheerio@^1.0.0-rc.3: lodash "^4.15.0" parse5 "^3.0.1" -chokidar@^3.4.0, chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chokidar@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" @@ -3540,6 +3519,21 @@ chokidar@^3.4.3: optionalDependencies: fsevents "~2.1.2" +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -3680,15 +3674,6 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -3698,10 +3683,14 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" clone-deep@^4.0.1: version "4.0.1" @@ -3719,30 +3708,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - clsx@^1.0.4: version "1.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" @@ -3835,10 +3805,10 @@ comlink@^4.3.0: resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.3.0.tgz#80b3366baccd87897dab3638ebfcfae28b2f87c7" integrity sha512-mu4KKKNuW8TvkfpW/H88HBPeILubBS6T94BdD1VWBXNXfiyqVtwUCVNO1GeNOBTsIswzsMjWlycYr+77F5b84g== -comma-separated-tokens@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" - integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== commander@^10.0.0: version "10.0.0" @@ -3870,14 +3840,6 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== -compare-func@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" - integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== - dependencies: - array-ify "^1.0.0" - dot-prop "^5.1.0" - component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -3913,7 +3875,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.0, concat-stream@~1.6.0: +concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -3923,25 +3885,6 @@ concat-stream@^1.6.0, concat-stream@~1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" - integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.0.2" - typedarray "^0.0.6" - -concat-stream@~1.5.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" - integrity sha1-cIl4Yk2FavQaWnQd790mHadSwmY= - dependencies: - inherits "~2.0.1" - readable-stream "~2.0.0" - typedarray "~0.0.5" - concat-with-sourcemaps@*: version "1.1.0" resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" @@ -3992,209 +3935,41 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -continuable-cache@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" - integrity sha1-vXJ6f67XfnH/OYWskzUakSczrQ8= - -conventional-changelog-angular@^5.0.12: - version "5.0.13" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" - integrity sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA== - dependencies: - compare-func "^2.0.0" - q "^1.5.1" - -conventional-changelog-atom@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz#a759ec61c22d1c1196925fca88fe3ae89fd7d8de" - integrity sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw== +convert-source-map@^1.6.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== dependencies: - q "^1.5.1" + safe-buffer "~5.1.1" -conventional-changelog-codemirror@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz#398e9530f08ce34ec4640af98eeaf3022eb1f7dc" - integrity sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw== +convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: - q "^1.5.1" - -conventional-changelog-config-spec@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-config-spec/-/conventional-changelog-config-spec-2.1.0.tgz#874a635287ef8b581fd8558532bf655d4fb59f2d" - integrity sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ== + safe-buffer "~5.1.1" -conventional-changelog-conventionalcommits@4.6.1, conventional-changelog-conventionalcommits@^4.5.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.1.tgz#f4c0921937050674e578dc7875f908351ccf4014" - integrity sha512-lzWJpPZhbM1R0PIzkwzGBCnAkH5RKJzJfFQZcl/D+2lsJxAwGnDKBqn/F4C1RD31GJNn8NuKWQzAZDAVXPp2Mw== - dependencies: - compare-func "^2.0.0" - lodash "^4.17.15" - q "^1.5.1" +convert-to-spaces@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz#7e3e48bbe6d997b1417ddca2868204b4d3d85715" + integrity sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU= -conventional-changelog-core@^4.2.1: - version "4.2.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz#e50d047e8ebacf63fac3dc67bf918177001e1e9f" - integrity sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg== - dependencies: - add-stream "^1.0.0" - conventional-changelog-writer "^5.0.0" - conventional-commits-parser "^3.2.0" - dateformat "^3.0.0" - get-pkg-repo "^4.0.0" - git-raw-commits "^2.0.8" - git-remote-origin-url "^2.0.0" - git-semver-tags "^4.1.1" - lodash "^4.17.15" - normalize-package-data "^3.0.0" - q "^1.5.1" - read-pkg "^3.0.0" - read-pkg-up "^3.0.0" - through2 "^4.0.0" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -conventional-changelog-ember@^2.0.9: - version "2.0.9" - resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz#619b37ec708be9e74a220f4dcf79212ae1c92962" - integrity sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A== - dependencies: - q "^1.5.1" +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -conventional-changelog-eslint@^3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz#689bd0a470e02f7baafe21a495880deea18b7cdb" - integrity sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA== +copy-anything@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.3.tgz#842407ba02466b0df844819bbe3baebbe5d45d87" + integrity sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ== dependencies: - q "^1.5.1" - -conventional-changelog-express@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz#420c9d92a347b72a91544750bffa9387665a6ee8" - integrity sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ== - dependencies: - q "^1.5.1" - -conventional-changelog-jquery@^3.0.11: - version "3.0.11" - resolved "https://registry.yarnpkg.com/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz#d142207400f51c9e5bb588596598e24bba8994bf" - integrity sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw== - dependencies: - q "^1.5.1" - -conventional-changelog-jshint@^2.0.9: - version "2.0.9" - resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz#f2d7f23e6acd4927a238555d92c09b50fe3852ff" - integrity sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA== - dependencies: - compare-func "^2.0.0" - q "^1.5.1" - -conventional-changelog-preset-loader@^2.3.4: - version "2.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz#14a855abbffd59027fd602581f1f34d9862ea44c" - integrity sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g== - -conventional-changelog-writer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.0.tgz#c4042f3f1542f2f41d7d2e0d6cad23aba8df8eec" - integrity sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g== - dependencies: - conventional-commits-filter "^2.0.7" - dateformat "^3.0.0" - handlebars "^4.7.6" - json-stringify-safe "^5.0.1" - lodash "^4.17.15" - meow "^8.0.0" - semver "^6.0.0" - split "^1.0.0" - through2 "^4.0.0" - -conventional-changelog@3.1.24: - version "3.1.24" - resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-3.1.24.tgz#ebd180b0fd1b2e1f0095c4b04fd088698348a464" - integrity sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg== - dependencies: - conventional-changelog-angular "^5.0.12" - conventional-changelog-atom "^2.0.8" - conventional-changelog-codemirror "^2.0.8" - conventional-changelog-conventionalcommits "^4.5.0" - conventional-changelog-core "^4.2.1" - conventional-changelog-ember "^2.0.9" - conventional-changelog-eslint "^3.0.9" - conventional-changelog-express "^2.0.6" - conventional-changelog-jquery "^3.0.11" - conventional-changelog-jshint "^2.0.9" - conventional-changelog-preset-loader "^2.3.4" - -conventional-commits-filter@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz#f8d9b4f182fce00c9af7139da49365b136c8a0b3" - integrity sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA== - dependencies: - lodash.ismatch "^4.4.0" - modify-values "^1.0.0" - -conventional-commits-parser@^3.2.0: - version "3.2.3" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.2.3.tgz#fc43704698239451e3ef35fd1d8ed644f46bd86e" - integrity sha512-YyRDR7On9H07ICFpRm/igcdjIqebXbvf4Cff+Pf0BrBys1i1EOzx9iFXNlAbdrLAR8jf7bkUYkDAr8pEy0q4Pw== - dependencies: - JSONStream "^1.0.4" - is-text-path "^1.0.1" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - -conventional-recommended-bump@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-6.1.0.tgz#cfa623285d1de554012f2ffde70d9c8a22231f55" - integrity sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw== - dependencies: - concat-stream "^2.0.0" - conventional-changelog-preset-loader "^2.3.4" - conventional-commits-filter "^2.0.7" - conventional-commits-parser "^3.2.0" - git-raw-commits "^2.0.8" - git-semver-tags "^4.1.1" - meow "^8.0.0" - q "^1.5.1" - -convert-source-map@^1.5.0, convert-source-map@^1.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -convert-to-spaces@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz#7e3e48bbe6d997b1417ddca2868204b4d3d85715" - integrity sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU= - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - -copy-anything@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.3.tgz#842407ba02466b0df844819bbe3baebbe5d45d87" - integrity sha512-GK6QUtisv4fNS+XcI7shX0Gx9ORg7QqIznyfho79JTnX1XhLiyZHfftvGiziqzRiEi/Bjhgpi+D2o7HxJFPnDQ== - dependencies: - is-what "^3.12.0" + is-what "^3.12.0" copy-descriptor@^0.1.0: version "0.1.1" @@ -4507,11 +4282,6 @@ dagre@^0.8.5: graphlib "^2.1.8" lodash "^4.17.15" -dargs@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" - integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -4550,11 +4320,6 @@ date-time@^3.1.0: dependencies: time-zone "^1.0.0" -dateformat@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" - integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== - dayjs@1.x: version "1.11.2" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.2.tgz#fa0f5223ef0d6724b3d8327134890cfe3d72fbe5" @@ -4605,19 +4370,18 @@ debug@^4.3.1: dependencies: ms "2.1.2" -decamelize-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" - integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= - dependencies: - decamelize "^1.1.0" - map-obj "^1.0.0" - -decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.2.0: +decamelize@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decode-named-character-reference@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" + integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== + dependencies: + character-entities "^2.0.0" + decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -4727,11 +4491,6 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= - del@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" @@ -4767,21 +4526,16 @@ dependency-tree@^8.1.1: precinct "^8.0.0" typescript "^3.9.7" +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== - -detect-newline@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -4871,15 +4625,6 @@ detective-typescript@^7.0.0: node-source-walk "^4.2.0" typescript "^3.9.10" -detective@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" - integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg== - dependencies: - acorn-node "^1.6.1" - defined "^1.0.0" - minimist "^1.1.1" - devtools-protocol@0.0.1094867: version "0.0.1094867" resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz#2ab93908e9376bd85d4e0604aa2651258f13e374" @@ -4892,16 +4637,16 @@ dice-coefficient@^2.1.0: dependencies: n-gram "^2.0.0" -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +diff@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4994,60 +4739,51 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -documentation@^13.2.5: - version "13.2.5" - resolved "https://registry.yarnpkg.com/documentation/-/documentation-13.2.5.tgz#2d4c8a94ce60a0342d6981d34488ad6184e463a0" - integrity sha512-d1TrfrHXYZR63xrOzkYwwe297vkSwBoEhyyMBOi20T+7Ohe1aX1dW4nqXncQmdmE5MxluSaxxa3BW1dCvbF5AQ== - dependencies: - "@babel/core" "7.12.3" - "@babel/generator" "7.12.1" - "@babel/parser" "7.12.3" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" - ansi-html "^0.0.7" - babelify "^10.0.0" - chalk "^2.3.0" - chokidar "^3.4.0" - concat-stream "^1.6.0" - diff "^4.0.1" +documentation@^14.0.2: + version "14.0.2" + resolved "https://registry.yarnpkg.com/documentation/-/documentation-14.0.2.tgz#d8c379ba08835953ba5c3b9bd5f9db66a62e66a3" + integrity sha512-hWoTf8/u4pOjib02L7w94hwmhPfcSwyJNGtlPdGVe8GFyq8HkzcFzQQltaaikKunHEp0YSwDAbwBAO7nxrWIfA== + dependencies: + "@babel/core" "^7.18.10" + "@babel/generator" "^7.18.10" + "@babel/parser" "^7.18.11" + "@babel/traverse" "^7.18.11" + "@babel/types" "^7.18.10" + chalk "^5.0.1" + chokidar "^3.5.3" + diff "^5.1.0" doctrine-temporary-fork "2.1.0" - get-port "^5.0.0" - git-url-parse "^11.1.2" - github-slugger "1.2.0" - glob "^7.1.2" - globals-docs "^2.4.0" - highlight.js "^10.7.2" - ini "^1.3.5" - js-yaml "^3.10.0" - lodash "^4.17.10" - mdast-util-find-and-replace "^1.1.1" + git-url-parse "^13.1.0" + github-slugger "1.4.0" + glob "^8.0.3" + globals-docs "^2.4.1" + highlight.js "^11.6.0" + ini "^3.0.0" + js-yaml "^4.1.0" + konan "^2.1.1" + lodash "^4.17.21" + mdast-util-find-and-replace "^2.2.1" mdast-util-inject "^1.1.0" - micromatch "^3.1.5" - mime "^2.2.0" - module-deps-sortable "^5.0.3" + micromark-util-character "^1.1.0" parse-filepath "^1.0.2" - pify "^5.0.0" - read-pkg-up "^4.0.0" - remark "^13.0.0" - remark-gfm "^1.0.0" - remark-html "^13.0.1" - remark-reference-links "^5.0.0" - remark-toc "^7.2.0" - resolve "^1.8.1" - stream-array "^1.1.2" - strip-json-comments "^2.0.1" - tiny-lr "^1.1.0" - unist-builder "^2.0.3" - unist-util-visit "^2.0.3" - vfile "^4.0.0" - vfile-reporter "^6.0.0" - vfile-sort "^2.1.0" - vinyl "^2.1.0" - vinyl-fs "^3.0.2" - yargs "^15.3.1" + pify "^6.0.0" + read-pkg-up "^9.1.0" + remark "^14.0.2" + remark-gfm "^3.0.1" + remark-html "^15.0.1" + remark-reference-links "^6.0.1" + remark-toc "^8.0.1" + resolve "^1.22.1" + strip-json-comments "^5.0.0" + unist-builder "^3.0.0" + unist-util-visit "^4.1.0" + vfile "^5.3.4" + vfile-reporter "^7.0.4" + vfile-sort "^3.0.0" + yargs "^17.5.1" optionalDependencies: - "@vue/compiler-sfc" "^3.0.11" - vue-template-compiler "^2.6.12" + "@vue/compiler-sfc" "^3.2.37" + vue-template-compiler "^2.7.8" dom-align@^1.7.0: version "1.12.0" @@ -5130,21 +4866,13 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" -dot-prop@^5.1.0, dot-prop@^5.2.0: +dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" -dotgitignore@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" - integrity sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA== - dependencies: - find-up "^3.0.0" - minimatch "^3.0.4" - dup@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dup/-/dup-1.0.0.tgz#51fc5ac685f8196469df0b905e934b20af5b4029" @@ -5155,13 +4883,6 @@ dup@~0.0.0: resolved "https://registry.yarnpkg.com/dup/-/dup-0.0.0.tgz#680c4c51ace6ab9fe3bc6e48d9cab26c0396abd7" integrity sha512-gfPVn3o0/vNDPsVcLGQqwrVXBELWi4EyIo+Pl6ImnYpXEHbfGPfc6m5bfJlBQI8dr8d8A4AlDaH/inn331GIEA== -duplexer2@^0.1.2, duplexer2@~0.1.0: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= - dependencies: - readable-stream "^2.0.2" - duplexer2@~0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" @@ -5174,15 +4895,10 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== ecc-jsbn@~0.1.1: version "0.1.2" @@ -5202,6 +4918,11 @@ electron-to-chromium@^1.4.251: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.477: + version "1.4.479" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.479.tgz#ec9f676f23d3a0b0e429bc454d25e0b3253d2118" + integrity sha512-ABv1nHMIR8I5n3O3Een0gr6i0mfM+YcTZqjHy3pAYaOjgFG+BMquuKrSyfYf5CbEkLr9uM05RA3pOk4udNB/aQ== + electron-to-chromium@^1.4.84: version "1.4.103" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz#abfe376a4d70fa1e1b4b353b95df5d6dfd05da3a" @@ -5217,11 +4938,6 @@ emittery@^0.7.2: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== -"emoji-regex@>=6.0.0 <=6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" - integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= - emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5232,6 +4948,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -5254,7 +4975,7 @@ encoding@^0.1.11: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -5395,13 +5116,6 @@ error-stack-parser@^2.0.4: dependencies: stackframe "^1.1.1" -error@^7.0.0: - version "7.2.1" - resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894" - integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== - dependencies: - string-template "~0.2.1" - es-abstract@^1.17.4: version "1.17.7" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" @@ -5674,6 +5388,11 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + escodegen@^1.9.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -6016,7 +5735,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: +extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -6127,13 +5846,6 @@ faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" -faye-websocket@~0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - fbjs@^0.8.16: version "0.8.17" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" @@ -6169,7 +5881,7 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.1.0, figures@^3.2.0: +figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -6241,11 +5953,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -filter-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" - integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= - finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -6264,13 +5971,6 @@ find-parent-dir@^0.3.0: resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" integrity sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ= -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -6278,7 +5978,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0, find-up@^4.1.0: +find-up@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -6294,6 +5994,14 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== + dependencies: + locate-path "^7.1.0" + path-exists "^5.0.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -6322,14 +6030,6 @@ flexlayout-react@^0.5.5: resolved "https://registry.yarnpkg.com/flexlayout-react/-/flexlayout-react-0.5.5.tgz#e96dbb61bd29868836aff5139d558fb13abf1287" integrity sha512-y8fQQ1nfBdSmBELoD/nEplEIxRZfDtHT1Lg50M4iXkvlF9blnkQktmngiYR2tO/wr6fy2+2cV+tX0HXoeHY8cQ== -flush-write-stream@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - follow-redirects@^1.0.0: version "1.14.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" @@ -6406,26 +6106,11 @@ frontend-collective-react-dnd-scrollzone@^1.0.2: react-display-name "^0.2.0" react-dom "^16.3.0" -fs-access@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a" - integrity sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o= - dependencies: - null-check "^1.0.0" - fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= - dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" - fs-monkey@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" @@ -6471,7 +6156,7 @@ functions-have-names@^1.2.1: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91" integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA== -gensync@^1.0.0-beta.1: +gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== @@ -6484,7 +6169,7 @@ get-amd-module-type@^3.0.0: ast-module-types "^3.0.0" node-source-walk "^4.2.2" -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -6503,21 +6188,6 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== -get-pkg-repo@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz#75973e1c8050c73f48190c52047c4cee3acbf385" - integrity sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA== - dependencies: - "@hutson/parse-repository-url" "^3.0.0" - hosted-git-info "^4.0.0" - through2 "^2.0.0" - yargs "^16.2.0" - -get-port@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -6562,76 +6232,30 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -git-raw-commits@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" - integrity sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ== - dependencies: - dargs "^7.0.0" - lodash "^4.17.15" - meow "^8.0.0" - split2 "^3.0.0" - through2 "^4.0.0" - -git-remote-origin-url@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" - integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= - dependencies: - gitconfiglocal "^1.0.0" - pify "^2.3.0" - -git-semver-tags@^4.0.0, git-semver-tags@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz#63191bcd809b0ec3e151ba4751c16c444e5b5780" - integrity sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA== - dependencies: - meow "^8.0.0" - semver "^6.0.0" - -git-up@^4.0.0: - version "4.0.5" - resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.5.tgz#e7bb70981a37ea2fb8fe049669800a1f9a01d759" - integrity sha512-YUvVDg/vX3d0syBsk/CKUTib0srcQME0JyHkL5BaYdwLsiCslPWmDSi8PUMo9pXYjrryMcmsCoCgsTpSCJEQaA== - dependencies: - is-ssh "^1.3.0" - parse-url "^6.0.0" - -git-url-parse@^11.1.2: - version "11.6.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.6.0.tgz#c634b8de7faa66498a2b88932df31702c67df605" - integrity sha512-WWUxvJs5HsyHL6L08wOusa/IXYtMuCAhrMmnTjQPpBU0TTHyDhnOATNH3xNQz7YOQUsqIIPTGr4xiVti1Hsk5g== - dependencies: - git-up "^4.0.0" - -gitconfiglocal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" - integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= +git-up@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-7.0.0.tgz#bace30786e36f56ea341b6f69adfd83286337467" + integrity sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ== dependencies: - ini "^1.3.2" + is-ssh "^1.4.0" + parse-url "^8.1.0" -github-slugger@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.0.tgz#8ada3286fd046d8951c3c952a8d7854cfd90fd9a" - integrity sha512-wIaa75k1vZhyPm9yWrD08A5Xnx/V+RmzGrpjQuLemGKSb77Qukiaei58Bogrl/LZSADDfPzKJX8jhLs4CRTl7Q== +git-url-parse@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-13.1.0.tgz#07e136b5baa08d59fabdf0e33170de425adf07b4" + integrity sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA== dependencies: - emoji-regex ">=6.0.0 <=6.1.1" + git-up "^7.0.0" -github-slugger@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" - integrity sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q== - dependencies: - emoji-regex ">=6.0.0 <=6.1.1" +github-slugger@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" + integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" @@ -6647,22 +6271,6 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= - dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" - glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -6680,7 +6288,7 @@ glob@^7.0.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: +glob@^7.1.3: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -6704,6 +6312,17 @@ glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + global-dirs@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" @@ -6719,7 +6338,7 @@ global@~4.4.0: min-document "^2.19.0" process "^0.11.10" -globals-docs@^2.4.0: +globals-docs@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/globals-docs/-/globals-docs-2.4.1.tgz#d16887709f4a15eb22d97e96343591f87a2ee3db" integrity sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg== @@ -6783,21 +6402,21 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.0.0, graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@^4.1.15, graceful-fs@^4.1.9: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -graceful-fs@^4.1.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== - graceful-fs@^4.1.2: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + graphlib@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" @@ -6841,18 +6460,6 @@ handle-thing@^2.0.0: resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== -handlebars@^4.7.6: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -6866,11 +6473,6 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" -hard-rejection@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" - integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== - has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -6948,48 +6550,104 @@ has@^1.0.0, has@^1.0.3: dependencies: function-bind "^1.1.1" -hast-util-is-element@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz#3b3ed5159a2707c6137b48637fbfe068e175a425" - integrity sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ== +hast-util-from-parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz#aecfef73e3ceafdfa4550716443e4eb7b02e22b0" + integrity sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw== + dependencies: + "@types/hast" "^2.0.0" + "@types/unist" "^2.0.0" + hastscript "^7.0.0" + property-information "^6.0.0" + vfile "^5.0.0" + vfile-location "^4.0.0" + web-namespaces "^2.0.0" -hast-util-sanitize@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-3.0.2.tgz#b0b783220af528ba8fe6999f092d138908678520" - integrity sha512-+2I0x2ZCAyiZOO/sb4yNLFmdwPBnyJ4PBkVTUMKMqBwYNA+lXSgOmoRXlJFazoyid9QPogRRKgKhVEodv181sA== +hast-util-parse-selector@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz#25ab00ae9e75cbc62cf7a901f68a247eade659e2" + integrity sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA== dependencies: - xtend "^4.0.0" + "@types/hast" "^2.0.0" -hast-util-to-html@^7.0.0: - version "7.1.3" - resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-7.1.3.tgz#9f339ca9bea71246e565fc79ff7dbfe98bb50f5e" - integrity sha512-yk2+1p3EJTEE9ZEUkgHsUSVhIpCsL/bvT8E5GzmWc+N1Po5gBw+0F8bo7dpxXR0nu0bQVxVZGX2lBGF21CmeDw== - dependencies: - ccount "^1.0.0" - comma-separated-tokens "^1.0.0" - hast-util-is-element "^1.0.0" - hast-util-whitespace "^1.0.0" - html-void-elements "^1.0.0" - property-information "^5.0.0" - space-separated-tokens "^1.0.0" - stringify-entities "^3.0.1" - unist-util-is "^4.0.0" - xtend "^4.0.0" +hast-util-raw@^7.0.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-7.2.3.tgz#dcb5b22a22073436dbdc4aa09660a644f4991d99" + integrity sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg== + dependencies: + "@types/hast" "^2.0.0" + "@types/parse5" "^6.0.0" + hast-util-from-parse5 "^7.0.0" + hast-util-to-parse5 "^7.0.0" + html-void-elements "^2.0.0" + parse5 "^6.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile "^5.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-sanitize@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-4.1.0.tgz#d90f8521f5083547095c5c63a7e03150303e0286" + integrity sha512-Hd9tU0ltknMGRDv+d6Ro/4XKzBqQnP/EZrpiTbpFYfXv/uOhWeKc+2uajcbEvAEH98VZd7eII2PiXm13RihnLw== + dependencies: + "@types/hast" "^2.0.0" -hast-util-whitespace@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz#e4fe77c4a9ae1cb2e6c25e02df0043d0164f6e41" - integrity sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A== +hast-util-to-html@^8.0.0: + version "8.0.4" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz#0269ef33fa3f6599b260a8dc94f733b8e39e41fc" + integrity sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA== + dependencies: + "@types/hast" "^2.0.0" + "@types/unist" "^2.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-raw "^7.0.0" + hast-util-whitespace "^2.0.0" + html-void-elements "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-to-parse5@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz#c49391bf8f151973e0c9adcd116b561e8daf29f3" + integrity sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^2.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" -he@^1.1.0: +hast-util-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz#0ec64e257e6fc216c7d14c8a1b74d27d650b4557" + integrity sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng== + +hastscript@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-7.2.0.tgz#0eafb7afb153d047077fa2a833dc9b7ec604d10b" + integrity sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^3.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + +he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -highlight.js@^10.7.2: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== +highlight.js@^11.6.0: + version "11.8.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65" + integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg== history@^4.7.2, history@^4.9.0: version "4.10.1" @@ -7025,7 +6683,7 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -hosted-git-info@^4.0.0, hosted-git-info@^4.0.1: +hosted-git-info@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961" integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg== @@ -7066,10 +6724,10 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-void-elements@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" - integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== +html-void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f" + integrity sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A== html2canvas@^1.4.1: version "1.4.1" @@ -7289,11 +6947,16 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.5, ini@~1.3.0: +ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.1.tgz#c76ec81007875bc44d544ff7a11a55d12294102d" + integrity sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ== + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -7372,19 +7035,6 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== - -is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== - dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-any-array@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-2.0.1.tgz#9233242a9c098220290aa2ec28f82ca7fa79899e" @@ -7471,13 +7121,20 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.8.1: +is-core-module@^2.1.0, is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" +is-core-module@^2.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + is-core-module@^2.5.0: version "2.8.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" @@ -7513,11 +7170,6 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" -is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== - is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -7563,7 +7215,7 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== @@ -7590,13 +7242,6 @@ is-function@^1.0.1: resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -7604,11 +7249,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== - is-installed-globally@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" @@ -7622,11 +7262,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= - is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -7693,16 +7328,16 @@ is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-plain-obj@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - is-plain-obj@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -7764,12 +7399,12 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" -is-ssh@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" - integrity sha512-elEw0/0c2UscLrNG+OAorbP539E3rhliKPg+hDMWN9VwrDXfYK+4PBEykDPfxlYYtQvl84TascnQyobfQLHEhQ== +is-ssh@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" + integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== dependencies: - protocols "^1.1.0" + protocols "^2.0.1" is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" @@ -7800,13 +7435,6 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-text-path@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" - integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= - dependencies: - text-extensions "^1.0.0" - is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -7834,16 +7462,6 @@ is-url@^1.2.4: resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== -is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= - is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -8156,7 +7774,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -8190,10 +7808,10 @@ json5@^2.2.0: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.2.tgz#64471c5bdcc564c18f7c1d4df2e2297f2457c5ab" integrity sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ== -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonschema@^1.2.4: version "1.4.0" @@ -8246,7 +7864,7 @@ kind-of@^5.0.0: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -8258,6 +7876,11 @@ klaw@^3.0.0: dependencies: graceful-fs "^4.1.9" +kleur@^4.0.3: + version "4.1.5" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" + integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== + klona@^2.0.4: version "2.0.5" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" @@ -8283,25 +7906,11 @@ lazy-cache@^1.0.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - lcov-parse@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A= -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= - dependencies: - flush-write-stream "^1.0.2" - left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -8436,11 +8045,6 @@ listr@^0.14.1: p-map "^2.0.0" rxjs "^6.3.3" -livereload-js@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" - integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== - load-bmfont@^1.2.3, load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.1.tgz#c0f5f4711a1e2ccff725a7b6078087ccfcddd3e9" @@ -8455,20 +8059,10 @@ load-bmfont@^1.2.3, load-bmfont@^1.3.1, load-bmfont@^1.4.0: xhr "^2.0.1" xtend "^4.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -load-json-file@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" - integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== +load-json-file@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" + integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== dependencies: graceful-fs "^4.1.15" parse-json "^4.0.0" @@ -8500,14 +8094,6 @@ loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -8530,6 +8116,13 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + lodash-es@^4.2.1: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" @@ -8570,11 +8163,6 @@ lodash.isequal@^4.4.0, lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.ismatch@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" - integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= - lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -8610,7 +8198,7 @@ lodash.throttle@^4.0.1, lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= -lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1: +lodash@^4.15.0, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8663,10 +8251,10 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -longest-streak@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" - integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== longest@^1.0.1: version "1.0.1" @@ -8698,6 +8286,13 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -8748,12 +8343,12 @@ madge@^5.0.1: typescript "^3.9.5" walkdir "^0.4.1" -magic-string@^0.25.7: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== +magic-string@^0.30.0: + version "0.30.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.2.tgz#dcf04aad3d0d1314bc743d076c50feb29b3c7aca" + integrity sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug== dependencies: - sourcemap-codec "^1.4.4" + "@jridgewell/sourcemap-codec" "^1.4.15" make-dir@^2.1.0: version "2.1.0" @@ -8782,16 +8377,6 @@ map-cache@^0.2.0, map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -map-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -map-obj@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" - integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== - map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -8815,12 +8400,10 @@ markdown-it@^10.0.0: mdurl "^1.0.1" uc.micro "^1.0.5" -markdown-table@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" - integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== - dependencies: - repeat-string "^1.0.0" +markdown-table@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" + integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== marked@^0.8.2: version "0.8.2" @@ -8841,74 +8424,100 @@ md5-hex@^3.0.1: dependencies: blueimp-md5 "^2.10.0" -mdast-util-definitions@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" - integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== +mdast-util-definitions@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7" + integrity sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA== dependencies: - unist-util-visit "^2.0.0" + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + unist-util-visit "^4.0.0" -mdast-util-find-and-replace@^1.1.0, mdast-util-find-and-replace@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-1.1.1.tgz#b7db1e873f96f66588c321f1363069abf607d1b5" - integrity sha512-9cKl33Y21lyckGzpSmEQnIDjEfeeWelN5s1kUW1LwdB0Fkuq2u+4GdqcGEygYxJE8GVqCl0741bYXHgamfWAZA== +mdast-util-find-and-replace@^2.0.0, mdast-util-find-and-replace@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz#cc2b774f7f3630da4bd592f61966fecade8b99b1" + integrity sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw== dependencies: - escape-string-regexp "^4.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" + "@types/mdast" "^3.0.0" + escape-string-regexp "^5.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.0.0" -mdast-util-from-markdown@^0.8.0: - version "0.8.5" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c" - integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ== +mdast-util-from-markdown@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz#9421a5a247f10d31d2faed2a30df5ec89ceafcf0" + integrity sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + decode-named-character-reference "^1.0.0" + mdast-util-to-string "^3.1.0" + micromark "^3.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-decode-string "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + unist-util-stringify-position "^3.0.0" + uvu "^0.5.0" + +mdast-util-gfm-autolink-literal@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz#67a13abe813d7eba350453a5333ae1bc0ec05c06" + integrity sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA== dependencies: "@types/mdast" "^3.0.0" - mdast-util-to-string "^2.0.0" - micromark "~2.11.0" - parse-entities "^2.0.0" - unist-util-stringify-position "^2.0.0" + ccount "^2.0.0" + mdast-util-find-and-replace "^2.0.0" + micromark-util-character "^1.0.0" -mdast-util-gfm-autolink-literal@^0.1.0: - version "0.1.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-0.1.3.tgz#9c4ff399c5ddd2ece40bd3b13e5447d84e385fb7" - integrity sha512-GjmLjWrXg1wqMIO9+ZsRik/s7PLwTaeCHVB7vRxUwLntZc8mzmTsLVr6HW1yLokcnhfURsn5zmSVdi3/xWWu1A== +mdast-util-gfm-footnote@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz#ce5e49b639c44de68d5bf5399877a14d5020424e" + integrity sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ== dependencies: - ccount "^1.0.0" - mdast-util-find-and-replace "^1.1.0" - micromark "^2.11.3" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.3.0" + micromark-util-normalize-identifier "^1.0.0" -mdast-util-gfm-strikethrough@^0.2.0: - version "0.2.3" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-0.2.3.tgz#45eea337b7fff0755a291844fbea79996c322890" - integrity sha512-5OQLXpt6qdbttcDG/UxYY7Yjj3e8P7X16LzvpX8pIQPYJ/C2Z1qFGMmcw+1PZMUM3Z8wt8NRfYTvCni93mgsgA== +mdast-util-gfm-strikethrough@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz#5470eb105b483f7746b8805b9b989342085795b7" + integrity sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ== dependencies: - mdast-util-to-markdown "^0.6.0" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.3.0" -mdast-util-gfm-table@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-0.1.6.tgz#af05aeadc8e5ee004eeddfb324b2ad8c029b6ecf" - integrity sha512-j4yDxQ66AJSBwGkbpFEp9uG/LS1tZV3P33fN1gkyRB2LoRL+RR3f76m0HPHaby6F4Z5xr9Fv1URmATlRRUIpRQ== +mdast-util-gfm-table@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz#3552153a146379f0f9c4c1101b071d70bbed1a46" + integrity sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg== dependencies: - markdown-table "^2.0.0" - mdast-util-to-markdown "~0.6.0" + "@types/mdast" "^3.0.0" + markdown-table "^3.0.0" + mdast-util-from-markdown "^1.0.0" + mdast-util-to-markdown "^1.3.0" -mdast-util-gfm-task-list-item@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-0.1.6.tgz#70c885e6b9f543ddd7e6b41f9703ee55b084af10" - integrity sha512-/d51FFIfPsSmCIRNp7E6pozM9z1GYPIkSy1urQ8s/o4TC22BZ7DqfHFWiqBD23bc7J3vV1Fc9O4QIHBlfuit8A== +mdast-util-gfm-task-list-item@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz#b280fcf3b7be6fd0cc012bbe67a59831eb34097b" + integrity sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ== dependencies: - mdast-util-to-markdown "~0.6.0" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.3.0" -mdast-util-gfm@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-0.1.2.tgz#8ecddafe57d266540f6881f5c57ff19725bd351c" - integrity sha512-NNkhDx/qYcuOWB7xHUGWZYVXvjPFFd6afg6/e2g+SV4r9q5XUcCbV4Wfa3DLYIiD+xAEZc6K4MGaE/m0KDcPwQ== +mdast-util-gfm@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz#e92f4d8717d74bdba6de57ed21cc8b9552e2d0b6" + integrity sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg== dependencies: - mdast-util-gfm-autolink-literal "^0.1.0" - mdast-util-gfm-strikethrough "^0.2.0" - mdast-util-gfm-table "^0.1.0" - mdast-util-gfm-task-list-item "^0.1.0" - mdast-util-to-markdown "^0.6.1" + mdast-util-from-markdown "^1.0.0" + mdast-util-gfm-autolink-literal "^1.0.0" + mdast-util-gfm-footnote "^1.0.0" + mdast-util-gfm-strikethrough "^1.0.0" + mdast-util-gfm-table "^1.0.0" + mdast-util-gfm-task-list-item "^1.0.0" + mdast-util-to-markdown "^1.0.0" mdast-util-inject@^1.1.0: version "1.1.0" @@ -8917,56 +8526,68 @@ mdast-util-inject@^1.1.0: dependencies: mdast-util-to-string "^1.0.0" -mdast-util-to-hast@^10.0.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.2.0.tgz#61875526a017d8857b71abc9333942700b2d3604" - integrity sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ== +mdast-util-phrasing@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz#c7c21d0d435d7fb90956038f02e8702781f95463" + integrity sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg== dependencies: "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - mdast-util-definitions "^4.0.0" - mdurl "^1.0.0" - unist-builder "^2.0.0" - unist-util-generated "^1.0.0" - unist-util-position "^3.0.0" - unist-util-visit "^2.0.0" - -mdast-util-to-markdown@^0.6.0, mdast-util-to-markdown@^0.6.1, mdast-util-to-markdown@~0.6.0: - version "0.6.5" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz#b33f67ca820d69e6cc527a93d4039249b504bebe" - integrity sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ== + unist-util-is "^5.0.0" + +mdast-util-to-hast@^12.0.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz#045d2825fb04374e59970f5b3f279b5700f6fb49" + integrity sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw== + dependencies: + "@types/hast" "^2.0.0" + "@types/mdast" "^3.0.0" + mdast-util-definitions "^5.0.0" + micromark-util-sanitize-uri "^1.1.0" + trim-lines "^3.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz#c13343cb3fc98621911d33b5cd42e7d0731171c6" + integrity sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A== dependencies: + "@types/mdast" "^3.0.0" "@types/unist" "^2.0.0" - longest-streak "^2.0.0" - mdast-util-to-string "^2.0.0" - parse-entities "^2.0.0" - repeat-string "^1.0.0" - zwitch "^1.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^3.0.0" + mdast-util-to-string "^3.0.0" + micromark-util-decode-string "^1.0.0" + unist-util-visit "^4.0.0" + zwitch "^2.0.0" mdast-util-to-string@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== -mdast-util-to-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" - integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== +mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz#66f7bb6324756741c5f47a53557f0cbf16b6f789" + integrity sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg== + dependencies: + "@types/mdast" "^3.0.0" -mdast-util-toc@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-toc/-/mdast-util-toc-5.1.0.tgz#3af0f9c9a764b993538af03f1f79f4e3cec22736" - integrity sha512-csimbRIVkiqc+PpFeKDGQ/Ck2N4f9FYH3zzBMMJzcxoKL8m+cM0n94xXm0I9eaxHnKdY9n145SGTdyJC7i273g== - dependencies: - "@types/mdast" "^3.0.3" - "@types/unist" "^2.0.3" - extend "^3.0.2" - github-slugger "^1.2.1" - mdast-util-to-string "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit "^2.0.0" - -mdurl@^1.0.0, mdurl@^1.0.1: +mdast-util-toc@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/mdast-util-toc/-/mdast-util-toc-6.1.1.tgz#28b81b0c99ca80c4442a3c95e20a825daf24518f" + integrity sha512-Er21728Kow8hehecK2GZtb7Ny3omcoPUVrmObiSUwmoRYVZaXLR751QROEFjR8W/vAQdHMLj49Lz20J55XaNpw== + dependencies: + "@types/extend" "^3.0.0" + "@types/mdast" "^3.0.0" + extend "^3.0.0" + github-slugger "^2.0.0" + mdast-util-to-string "^3.1.0" + unist-util-is "^5.0.0" + unist-util-visit "^4.0.0" + +mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= @@ -8996,23 +8617,6 @@ memoize-one@*, memoize-one@^6.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== -meow@^8.0.0: - version "8.1.2" - resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" - integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== - dependencies: - "@types/minimist" "^1.2.0" - camelcase-keys "^6.2.2" - decamelize-keys "^1.1.0" - hard-rejection "^2.1.0" - minimist-options "4.1.0" - normalize-package-data "^3.0.0" - read-pkg-up "^7.0.1" - redent "^3.0.0" - trim-newlines "^3.0.0" - type-fest "^0.18.0" - yargs-parser "^20.2.3" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -9041,60 +8645,280 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromark-extension-gfm-autolink-literal@~0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-0.5.7.tgz#53866c1f0c7ef940ae7ca1f72c6faef8fed9f204" - integrity sha512-ePiDGH0/lhcngCe8FtH4ARFoxKTUelMp4L7Gg2pujYD5CSMb9PbblnyL+AAMud/SNMyusbS2XDSiPIRcQoNFAw== +micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz#1386628df59946b2d39fb2edfd10f3e8e0a75bb8" + integrity sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-factory-destination "^1.0.0" + micromark-factory-label "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-factory-title "^1.0.0" + micromark-factory-whitespace "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-html-tag-name "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + uvu "^0.5.0" + +micromark-extension-gfm-autolink-literal@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz#5853f0e579bbd8ef9e39a7c0f0f27c5a063a66e7" + integrity sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg== dependencies: - micromark "~2.11.3" + micromark-util-character "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" -micromark-extension-gfm-strikethrough@~0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-0.6.5.tgz#96cb83356ff87bf31670eefb7ad7bba73e6514d1" - integrity sha512-PpOKlgokpQRwUesRwWEp+fHjGGkZEejj83k9gU5iXCbDG+XBA92BqnRKYJdfqfkrRcZRgGuPuXb7DaK/DmxOhw== +micromark-extension-gfm-footnote@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz#05e13034d68f95ca53c99679040bc88a6f92fe2e" + integrity sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q== + dependencies: + micromark-core-commonmark "^1.0.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + +micromark-extension-gfm-strikethrough@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz#c8212c9a616fa3bf47cb5c711da77f4fdc2f80af" + integrity sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw== dependencies: - micromark "~2.11.0" + micromark-util-chunked "^1.0.0" + micromark-util-classify-character "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm-table@~0.4.0: - version "0.4.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-0.4.3.tgz#4d49f1ce0ca84996c853880b9446698947f1802b" - integrity sha512-hVGvESPq0fk6ALWtomcwmgLvH8ZSVpcPjzi0AjPclB9FsVRgMtGZkUcpE0zgjOCFAznKepF4z3hX8z6e3HODdA== +micromark-extension-gfm-table@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz#dcb46074b0c6254c3fc9cc1f6f5002c162968008" + integrity sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw== dependencies: - micromark "~2.11.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm-tagfilter@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-0.3.0.tgz#d9f26a65adee984c9ccdd7e182220493562841ad" - integrity sha512-9GU0xBatryXifL//FJH+tAZ6i240xQuFrSL7mYi8f4oZSbc+NvXjkrHemeYP0+L4ZUT+Ptz3b95zhUZnMtoi/Q== +micromark-extension-gfm-tagfilter@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz#aa7c4dd92dabbcb80f313ebaaa8eb3dac05f13a7" + integrity sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g== + dependencies: + micromark-util-types "^1.0.0" -micromark-extension-gfm-task-list-item@~0.3.0: - version "0.3.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-0.3.3.tgz#d90c755f2533ed55a718129cee11257f136283b8" - integrity sha512-0zvM5iSLKrc/NQl84pZSjGo66aTGd57C1idmlWmE87lkMcXrTxg1uXa/nXomxJytoje9trP0NDLvw4bZ/Z/XCQ== +micromark-extension-gfm-task-list-item@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz#b52ce498dc4c69b6a9975abafc18f275b9dde9f4" + integrity sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ== dependencies: - micromark "~2.11.0" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" -micromark-extension-gfm@^0.3.0: - version "0.3.3" - resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-0.3.3.tgz#36d1a4c089ca8bdfd978c9bd2bf1a0cb24e2acfe" - integrity sha512-oVN4zv5/tAIA+l3GbMi7lWeYpJ14oQyJ3uEim20ktYFAcfX1x3LNlFGGlmrZHt7u9YlKExmyJdDGaTt6cMSR/A== +micromark-extension-gfm@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz#e517e8579949a5024a493e49204e884aa74f5acf" + integrity sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ== + dependencies: + micromark-extension-gfm-autolink-literal "^1.0.0" + micromark-extension-gfm-footnote "^1.0.0" + micromark-extension-gfm-strikethrough "^1.0.0" + micromark-extension-gfm-table "^1.0.0" + micromark-extension-gfm-tagfilter "^1.0.0" + micromark-extension-gfm-task-list-item "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-destination@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz#eb815957d83e6d44479b3df640f010edad667b9f" + integrity sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg== dependencies: - micromark "~2.11.0" - micromark-extension-gfm-autolink-literal "~0.5.0" - micromark-extension-gfm-strikethrough "~0.6.5" - micromark-extension-gfm-table "~0.4.0" - micromark-extension-gfm-tagfilter "~0.3.0" - micromark-extension-gfm-task-list-item "~0.3.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" -micromark@^2.11.3, micromark@~2.11.0, micromark@~2.11.3: - version "2.11.4" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a" - integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== +micromark-factory-label@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz#cc95d5478269085cfa2a7282b3de26eb2e2dec68" + integrity sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w== dependencies: - debug "^4.0.0" - parse-entities "^2.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + +micromark-factory-space@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz#c8f40b0640a0150751d3345ed885a080b0d15faf" + integrity sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-title@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz#dd0fe951d7a0ac71bdc5ee13e5d1465ad7f50ea1" + integrity sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-factory-whitespace@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz#798fb7489f4c8abafa7ca77eed6b5745853c9705" + integrity sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ== + dependencies: + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-character@^1.0.0, micromark-util-character@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.2.0.tgz#4fedaa3646db249bc58caeb000eb3549a8ca5dcc" + integrity sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg== + dependencies: + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-chunked@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz#37a24d33333c8c69a74ba12a14651fd9ea8a368b" + integrity sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-classify-character@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz#6a7f8c8838e8a120c8e3c4f2ae97a2bff9190e9d" + integrity sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-combine-extensions@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz#192e2b3d6567660a85f735e54d8ea6e3952dbe84" + integrity sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-types "^1.0.0" + +micromark-util-decode-numeric-character-reference@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz#b1e6e17009b1f20bc652a521309c5f22c85eb1c6" + integrity sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-decode-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz#dc12b078cba7a3ff690d0203f95b5d5537f2809c" + integrity sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-symbol "^1.0.0" + +micromark-util-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz#92e4f565fd4ccb19e0dcae1afab9a173bbeb19a5" + integrity sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw== + +micromark-util-html-tag-name@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz#48fd7a25826f29d2f71479d3b4e83e94829b3588" + integrity sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q== + +micromark-util-normalize-identifier@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz#7a73f824eb9f10d442b4d7f120fecb9b38ebf8b7" + integrity sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q== + dependencies: + micromark-util-symbol "^1.0.0" + +micromark-util-resolve-all@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz#4652a591ee8c8fa06714c9b54cd6c8e693671188" + integrity sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA== + dependencies: + micromark-util-types "^1.0.0" + +micromark-util-sanitize-uri@^1.0.0, micromark-util-sanitize-uri@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz#613f738e4400c6eedbc53590c67b197e30d7f90d" + integrity sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A== + dependencies: + micromark-util-character "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-symbol "^1.0.0" + +micromark-util-subtokenize@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz#941c74f93a93eaf687b9054aeb94642b0e92edb1" + integrity sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A== + dependencies: + micromark-util-chunked "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.0" + uvu "^0.5.0" + +micromark-util-symbol@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz#813cd17837bdb912d069a12ebe3a44b6f7063142" + integrity sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag== -micromatch@^3.1.5, micromatch@^3.1.8: +micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.1.0.tgz#e6676a8cae0bb86a2171c498167971886cb7e283" + integrity sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg== + +micromark@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.2.0.tgz#1af9fef3f995ea1ea4ac9c7e2f19c48fd5c006e9" + integrity sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + micromark-core-commonmark "^1.0.1" + micromark-factory-space "^1.0.0" + micromark-util-character "^1.0.0" + micromark-util-chunked "^1.0.0" + micromark-util-combine-extensions "^1.0.0" + micromark-util-decode-numeric-character-reference "^1.0.0" + micromark-util-encode "^1.0.0" + micromark-util-normalize-identifier "^1.0.0" + micromark-util-resolve-all "^1.0.0" + micromark-util-sanitize-uri "^1.0.0" + micromark-util-subtokenize "^1.0.0" + micromark-util-symbol "^1.0.0" + micromark-util-types "^1.0.1" + uvu "^0.5.0" + +micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -9150,11 +8974,6 @@ mime@1.6.0, mime@^1.3.4, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0: - version "2.4.6" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" - integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -9182,11 +9001,6 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - mini-create-react-context@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" @@ -9231,25 +9045,18 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimist-options@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" - integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - kind-of "^6.0.3" + brace-expansion "^2.0.1" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.0, minimist@^1.1.1: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -9333,11 +9140,6 @@ mock-require@^1.2.1: dependencies: caller-id "^0.1.0" -modify-values@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" - integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== - module-definition@^3.3.1: version "3.4.0" resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.4.0.tgz#953a3861f65df5e43e80487df98bb35b70614c2b" @@ -9346,28 +9148,6 @@ module-definition@^3.3.1: ast-module-types "^3.0.0" node-source-walk "^4.0.0" -module-deps-sortable@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/module-deps-sortable/-/module-deps-sortable-5.0.3.tgz#e640e7450e0869f4ae8e03437665ca2a8a28f843" - integrity sha512-eiyIZj/A0dj1o4ywXWqicazUL3l0HP3TydUR6xF0X3xh3LGBMLqW8a9aFe6MuNH4mxNMk53QKBHM6LOPR8kSgw== - dependencies: - JSONStream "^1.0.3" - browser-resolve "^1.7.0" - cached-path-relative "^1.0.0" - concat-stream "~1.5.0" - defined "^1.0.0" - detective "^5.2.0" - duplexer2 "^0.1.2" - inherits "^2.0.1" - konan "^2.1.1" - readable-stream "^2.0.2" - resolve "^1.1.3" - standard-version "^9.0.0" - stream-combiner2 "^1.1.1" - subarg "^1.0.0" - through2 "^2.0.0" - xtend "^4.0.0" - module-lookup-amd@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-7.0.1.tgz#d67c1a93f2ff8e38b8774b99a638e9a4395774b2" @@ -9394,6 +9174,11 @@ moo@^0.5.0: resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== +mri@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -9564,7 +9349,7 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.6.0, neo-async@^2.6.2: +neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -9600,6 +9385,11 @@ node-forge@^1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + node-releases@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" @@ -9617,7 +9407,7 @@ node-source-walk@^4.0.0, node-source-walk@^4.2.0, node-source-walk@^4.2.2: dependencies: "@babel/parser" "^7.0.0" -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -9627,7 +9417,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0: +normalize-package-data@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== @@ -9647,30 +9437,11 @@ normalize-path@^1.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" integrity sha1-MtDkcvkf80VwHBWoMRAY07CpA3k= -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - normalize-url@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== -normalize-url@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -now-and-later@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== - dependencies: - once "^1.3.2" - npm-path@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64" @@ -9708,11 +9479,6 @@ nth-check@~1.0.1: dependencies: boolbase "~1.0.0" -null-check@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" - integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0= - number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -9790,7 +9556,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2: +object.assign@^4.1.0, object.assign@^4.1.1, object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -9867,7 +9633,7 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -9974,13 +9740,6 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= - dependencies: - readable-stream "^2.0.1" - os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -10008,13 +9767,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -10029,12 +9781,12 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== dependencies: - p-limit "^1.1.0" + yocto-queue "^1.0.0" p-locate@^3.0.0: version "3.0.0" @@ -10057,6 +9809,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" @@ -10089,11 +9848,6 @@ p-timeout@^3.1.0: dependencies: p-finally "^1.0.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -10149,18 +9903,6 @@ parse-bmfont-xml@^1.1.4: xml-parse-from-string "^1.0.0" xml2js "^0.4.5" -parse-entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" - integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - parse-filepath@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" @@ -10193,6 +9935,16 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parse-ms@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" @@ -10203,25 +9955,19 @@ parse-node-version@^1.0.1: resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== -parse-path@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf" - integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA== +parse-path@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" + integrity sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog== dependencies: - is-ssh "^1.3.0" - protocols "^1.4.0" - qs "^6.9.4" - query-string "^6.13.8" + protocols "^2.0.0" -parse-url@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d" - integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw== +parse-url@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-8.1.0.tgz#972e0827ed4b57fc85f0ea6b0d839f0d8a57a57d" + integrity sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w== dependencies: - is-ssh "^1.3.0" - normalize-url "^6.1.0" - parse-path "^4.0.0" - protocols "^1.4.0" + parse-path "^7.0.0" parse5@4.0.0: version "4.0.0" @@ -10235,6 +9981,11 @@ parse5@^3.0.1: dependencies: "@types/node" "*" +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -10245,11 +9996,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -10260,6 +10006,11 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -10309,13 +10060,6 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -10403,11 +10147,6 @@ picomatch@^2.2.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -10418,10 +10157,10 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" - integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== +pify@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-6.1.0.tgz#db9f2ebfba65f0bc144db65ae84d1a486ab72909" + integrity sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw== pixelmatch@^4.0.0, pixelmatch@^4.0.2: version "4.0.2" @@ -10677,16 +10416,6 @@ pretty-ms@^7.0.1: dependencies: parse-ms "^2.1.0" -process-nextick-args@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -10741,12 +10470,10 @@ prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: object-assign "^4.1.1" react-is "^16.8.1" -property-information@^5.0.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" - integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== - dependencies: - xtend "^4.0.0" +property-information@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.2.0.tgz#b74f522c31c097b5149e3c3cb8d7f3defd986a1d" + integrity sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg== proto-loader6@^0.4.0: version "0.4.0" @@ -10794,10 +10521,10 @@ protobufjs@^6.8.8: "@types/node" ">=13.7.0" long "^4.0.0" -protocols@^1.1.0, protocols@^1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" - integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== +protocols@^2.0.0, protocols@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" + integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== proxy-addr@~2.0.7: version "2.0.7" @@ -10827,14 +10554,6 @@ psl@^1.1.28: resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -10843,15 +10562,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -10897,43 +10607,16 @@ puppeteer@^19.7.2: proxy-from-env "1.1.0" puppeteer-core "19.7.2" -q@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - qs@6.9.7: version "6.9.7" resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.4.0: - version "6.9.4" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" - integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== - -qs@^6.9.4: - version "6.10.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.2.tgz#c1431bea37fc5b24c5bdbafa20f16bdf2a4b9ffe" - integrity sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw== - dependencies: - side-channel "^1.0.4" - qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -query-string@^6.13.8: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== - dependencies: - decode-uri-component "^0.2.0" - filter-obj "^1.1.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" @@ -10944,11 +10627,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quick-lru@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" - integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== - quote-stream@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-0.0.0.tgz#cde29e94c409b16e19dc7098b89b6658f9721d3b" @@ -11004,14 +10682,6 @@ raw-body@2.4.3: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@~1.1.0: - version "1.1.7" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-1.1.7.tgz#1d027c2bfa116acc6623bca8f00016572a87d425" - integrity sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU= - dependencies: - bytes "1" - string_decoder "0.10" - rc-align@^4.0.0: version "4.0.9" resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.9.tgz#46d8801c4a139ff6a65ad1674e8efceac98f85f2" @@ -11796,60 +11466,36 @@ read-chunk@^1.0.1: resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194" integrity sha1-X2jKswfmY/GZk1J9m1icrORmEZQ= -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - -read-pkg-up@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" - integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== +read-pkg-up@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-9.1.0.tgz#38ca48e0bc6c6b260464b14aad9bcd4e5b1fbdc3" + integrity sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg== dependencies: - find-up "^3.0.0" - read-pkg "^3.0.0" + find-up "^6.3.0" + read-pkg "^7.1.0" + type-fest "^2.5.0" -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== dependencies: "@types/normalize-package-data" "^2.4.0" normalize-package-data "^2.5.0" parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +read-pkg@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-7.1.0.tgz#438b4caed1ad656ba359b3e00fd094f3c427a43e" + integrity sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg== dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + "@types/normalize-package-data" "^2.4.1" + normalize-package-data "^3.0.2" + parse-json "^5.2.0" + type-fest "^2.0.0" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.1, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -11862,6 +11508,15 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.0, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.1.0.tgz#280d0a29f559d3fb684a277254e02b6f61ae0631" @@ -11889,31 +11544,6 @@ readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readable-stream@~2.1.0: - version "2.1.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" - integrity sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA= - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" @@ -11942,14 +11572,6 @@ rechoir@^0.7.0: dependencies: resolve "^1.9.0" -redent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" - integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== - dependencies: - indent-string "^4.0.0" - strip-indent "^3.0.0" - redux-batched-actions@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/redux-batched-actions/-/redux-batched-actions-0.5.0.tgz#d3f0e359b2a95c7d80bab442df450bfafd57d122" @@ -12029,60 +11651,72 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -remark-gfm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-1.0.0.tgz#9213643001be3f277da6256464d56fd28c3b3c0d" - integrity sha512-KfexHJCiqvrdBZVbQ6RopMZGwaXz6wFJEfByIuEwGf0arvITHjiKKZ1dpXujjH9KZdm1//XJQwgfnJ3lmXaDPA== +remark-gfm@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f" + integrity sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig== dependencies: - mdast-util-gfm "^0.1.0" - micromark-extension-gfm "^0.3.0" + "@types/mdast" "^3.0.0" + mdast-util-gfm "^2.0.0" + micromark-extension-gfm "^2.0.0" + unified "^10.0.0" -remark-html@^13.0.1: - version "13.0.2" - resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-13.0.2.tgz#de5f052749ff61fc904c9708c155c88a2e2655dc" - integrity sha512-LhSRQ+3RKdBqB/RGesFWkNNfkGqprDUCwjq54SylfFeNyZby5kqOG8Dn/vYsRoM8htab6EWxFXCY6XIZvMoRiQ== +remark-html@^15.0.1: + version "15.0.2" + resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-15.0.2.tgz#44ff77c876f037658b406662b5ce15e26ed34d80" + integrity sha512-/CIOI7wzHJzsh48AiuIyIe1clxVkUtreul73zcCXLub0FmnevQE0UMFDQm7NUx8/3rl/4zCshlMfqBdWScQthw== dependencies: - hast-util-sanitize "^3.0.0" - hast-util-to-html "^7.0.0" - mdast-util-to-hast "^10.0.0" + "@types/mdast" "^3.0.0" + hast-util-sanitize "^4.0.0" + hast-util-to-html "^8.0.0" + mdast-util-to-hast "^12.0.0" + unified "^10.0.0" -remark-parse@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" - integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw== +remark-parse@^10.0.0: + version "10.0.2" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.2.tgz#ca241fde8751c2158933f031a4e3efbaeb8bc262" + integrity sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw== dependencies: - mdast-util-from-markdown "^0.8.0" + "@types/mdast" "^3.0.0" + mdast-util-from-markdown "^1.0.0" + unified "^10.0.0" -remark-reference-links@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/remark-reference-links/-/remark-reference-links-5.0.0.tgz#2c75b60a99c53251f25193566953b0c71e096b8d" - integrity sha512-oSIo6lfDyG/1yYl2jPZNXmD9dgyPxp07mSd7snJagVMsDU6NRlD8i54MwHWUgMoOHTs8lIKPkwaUok/tbr5syQ== +remark-reference-links@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/remark-reference-links/-/remark-reference-links-6.0.1.tgz#e1927ad8018585dfc97f7b9c5f781f6d246f1d96" + integrity sha512-34wY2C6HXSuKVTRtyJJwefkUD8zBOZOSHFZ4aSTnU2F656gr9WeuQ2dL6IJDK3NPd2F6xKF2t4XXcQY9MygAXg== dependencies: - unist-util-visit "^2.0.0" + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unist-util-visit "^4.0.0" -remark-stringify@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894" - integrity sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg== +remark-stringify@^10.0.0: + version "10.0.3" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-10.0.3.tgz#83b43f2445c4ffbb35b606f967d121b2b6d69717" + integrity sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A== dependencies: - mdast-util-to-markdown "^0.6.0" + "@types/mdast" "^3.0.0" + mdast-util-to-markdown "^1.0.0" + unified "^10.0.0" -remark-toc@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/remark-toc/-/remark-toc-7.2.0.tgz#1c5159e9091826150db14c97ac00c2ad5a7f1523" - integrity sha512-ppHepvpbg7j5kPFmU5rzDC4k2GTcPDvWcxXyr/7BZzO1cBSPk0stKtEJdsgAyw2WHKPGxadcHIZRjb2/sHxjkg== +remark-toc@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/remark-toc/-/remark-toc-8.0.1.tgz#f3e07ea13734f1c531e3d3460e58babe31d17cd7" + integrity sha512-7he2VOm/cy13zilnOTZcyAoyoolV26ULlon6XyCFU+vG54Z/LWJnwphj/xKIDLOt66QmJUgTyUvLVHi2aAElyg== dependencies: - "@types/unist" "^2.0.3" - mdast-util-toc "^5.0.0" + "@types/mdast" "^3.0.0" + mdast-util-toc "^6.0.0" + unified "^10.0.0" -remark@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/remark/-/remark-13.0.0.tgz#d15d9bf71a402f40287ebe36067b66d54868e425" - integrity sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA== +remark@^14.0.2: + version "14.0.3" + resolved "https://registry.yarnpkg.com/remark/-/remark-14.0.3.tgz#e477886a7579df612908f387c7753dc93cdaa3fc" + integrity sha512-bfmJW1dmR2LvaMJuAnE88pZP9DktIFYXazkTfOIKZzi3Knk9lT0roItIA24ydOucI3bV/g/tXBA6hzqq3FV9Ew== dependencies: - remark-parse "^9.0.0" - remark-stringify "^9.0.0" - unified "^9.1.0" + "@types/mdast" "^3.0.0" + remark-parse "^10.0.0" + remark-stringify "^10.0.0" + unified "^10.0.0" remarkable@^1.x: version "1.7.4" @@ -12092,43 +11726,16 @@ remarkable@^1.x: argparse "^1.0.10" autolinker "~0.28.0" -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== -repeat-string@^1.0.0, repeat-string@^1.5.0, repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -12181,11 +11788,6 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - requirejs-config-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" @@ -12243,13 +11845,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= - dependencies: - value-or-function "^3.0.0" - resolve-pathname@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" @@ -12260,12 +11855,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - -resolve@^1.1.3, resolve@^1.1.6, resolve@^1.8.1: +resolve@^1.1.6: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== @@ -12291,13 +11881,14 @@ resolve@^1.21.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.3.2: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== +resolve@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" responselike@^1.0.2: version "1.0.2" @@ -12395,6 +11986,13 @@ rxjs@^6.3.3: dependencies: tslib "^1.9.0" +sade@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" + integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== + dependencies: + mri "^1.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -12405,11 +12003,6 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-json-parse@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" - integrity sha1-PnZyPjjf3aE8mx0poeB//uSzC1c= - safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -12517,7 +12110,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -12532,12 +12125,10 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.1, semver@^7.3.4: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== - dependencies: - lru-cache "^6.0.0" +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.2: version "7.3.4" @@ -12546,6 +12137,13 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" +semver@^7.3.4: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + semver@^7.3.5, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" @@ -12607,11 +12205,6 @@ serve-static@1.14.2: parseurl "~1.3.3" send "0.17.2" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -12844,15 +12437,10 @@ source-map@~0.1.33: dependencies: amdefine ">=0.0.4" -sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -space-separated-tokens@^1.0.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" - integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== spdx-correct@^3.0.0: version "3.1.1" @@ -12903,11 +12491,6 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -12915,20 +12498,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -split2@^3.0.0, split2@^3.1.1: +split2@^3.1.1: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== dependencies: readable-stream "^3.0.0" -split@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" - integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== - dependencies: - through "2" - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -12971,27 +12547,6 @@ staged-git-files@1.1.1: resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.1.tgz#37c2218ef0d6d26178b1310719309a16a59f8f7b" integrity sha512-H89UNKr1rQJvI1c/PIR3kiAMBV23yvR7LItZiV74HWZwzt7f3YHuujJ9nJZlt58WlFox7XQsOahexwk7nTe69A== -standard-version@^9.0.0: - version "9.3.2" - resolved "https://registry.yarnpkg.com/standard-version/-/standard-version-9.3.2.tgz#28db8c1be66fd2d736f28f7c5de7619e64cd6dab" - integrity sha512-u1rfKP4o4ew7Yjbfycv80aNMN2feTiqseAhUhrrx2XtdQGmu7gucpziXe68Z4YfHVqlxVEzo4aUA0Iu3VQOTgQ== - dependencies: - chalk "^2.4.2" - conventional-changelog "3.1.24" - conventional-changelog-config-spec "2.1.0" - conventional-changelog-conventionalcommits "4.6.1" - conventional-recommended-bump "6.1.0" - detect-indent "^6.0.0" - detect-newline "^3.1.0" - dotgitignore "^2.1.0" - figures "^3.1.0" - find-up "^5.0.0" - fs-access "^1.0.1" - git-semver-tags "^4.0.0" - semver "^7.1.1" - stringify-package "^1.0.1" - yargs "^16.0.0" - static-eval@~0.2.0: version "0.2.4" resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-0.2.4.tgz#b7d34d838937b969f9641ca07d48f8ede263ea7b" @@ -13034,26 +12589,6 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= -stream-array@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/stream-array/-/stream-array-1.1.2.tgz#9e5f7345f2137c30ee3b498b9114e80b52bb7eb5" - integrity sha1-nl9zRfITfDDuO0mLkRToC1K7frU= - dependencies: - readable-stream "~2.1.0" - -stream-combiner2@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" - integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= - dependencies: - duplexer2 "~0.1.0" - readable-stream "^2.0.2" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - stream-to-buffer@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz#26799d903ab2025c9bd550ac47171b00f8dd80a9" @@ -13066,11 +12601,6 @@ stream-to@~0.2.0: resolved "https://registry.yarnpkg.com/stream-to/-/stream-to-0.2.2.tgz#84306098d85fdb990b9fa300b1b3ccf55e8ef01d" integrity sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0= -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - string-argv@^0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736" @@ -13081,11 +12611,6 @@ string-convert@^0.2.0: resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c= -string-template@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" - integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -13121,6 +12646,24 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trim@^1.2.1: version "1.2.3" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.3.tgz#d23a22fde01c1e6571a7fadcb9be11decd8061a7" @@ -13146,11 +12689,6 @@ string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@0.10, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -13158,6 +12696,11 @@ string_decoder@^1.1.1, string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -13165,14 +12708,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-3.1.0.tgz#b8d3feac256d9ffcc9fa1fefdcf3ca70576ee903" - integrity sha512-3FP+jGMmMV/ffZs86MoghGqAoqXAdxLrJP4GUdrDN1aIScYih5tuIO3eF4To5AJZ79KDZ8Fpdy7QJnK8SsL1Vg== +stringify-entities@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.3.tgz#cfabd7039d22ad30f3cc435b0ca2c1574fc88ef8" + integrity sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g== dependencies: - character-entities-html4 "^1.0.0" - character-entities-legacy "^1.0.0" - xtend "^4.0.0" + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" stringify-object@^3.2.1, stringify-object@^3.2.2: version "3.3.0" @@ -13183,11 +12725,6 @@ stringify-object@^3.2.1, stringify-object@^3.2.2: is-obj "^1.0.1" is-regexp "^1.0.0" -stringify-package@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" - integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -13243,23 +12780,21 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.1.tgz#0d8b7d01b23848ed7dbdf4baaaa31a8250d8cfa0" + integrity sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + stylus-lookup@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-3.0.2.tgz#c9eca3ff799691020f30b382260a67355fefdddd" @@ -13268,13 +12803,6 @@ stylus-lookup@^3.0.1: commander "^2.8.1" debug "^4.1.0" -subarg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" - integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= - dependencies: - minimist "^1.1.0" - supertap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supertap/-/supertap-1.0.0.tgz#bd9751c7fafd68c68cf8222a29892206a119fa9e" @@ -13298,13 +12826,6 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -13319,6 +12840,11 @@ supports-color@^8.0.0, supports-color@^8.1.0: dependencies: has-flag "^4.0.0" +supports-color@^9.0.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -13428,11 +12954,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-extensions@^1.0.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" - integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== - text-segmentation@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" @@ -13450,15 +12971,7 @@ three@^0.137.0: resolved "https://registry.yarnpkg.com/three/-/three-0.137.5.tgz#a1e34bedd0412f2d8797112973dfadac78022ce6" integrity sha512-rTyr+HDFxjnN8+N/guZjDgfVxgHptZQpf6xfL/Mo7a5JYIFwK6tAq3bzxYYB4Ae0RosDZlDuP+X5aXDXz+XnHQ== -through2-filter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" - integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: +through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -13466,13 +12979,6 @@ through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through2@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" - integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== - dependencies: - readable-stream "3" - through2@~0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b" @@ -13481,7 +12987,7 @@ through2@~0.4.1: readable-stream "~1.0.17" xtend "~2.1.1" -through@2, "through@>=2.2.7 <3", through@^2.3.8: +through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -13506,18 +13012,6 @@ tiny-invariant@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== -tiny-lr@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.1.1.tgz#9fa547412f238fedb068ee295af8b682c98b2aab" - integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== - dependencies: - body "^5.1.0" - debug "^3.1.0" - faye-websocket "~0.10.0" - livereload-js "^2.3.0" - object-assign "^4.1.0" - qs "^6.4.0" - tiny-warning@^1.0.0, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" @@ -13540,14 +13034,6 @@ tmp@0.0.33: dependencies: os-tmpdir "~1.0.2" -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -13590,13 +13076,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= - dependencies: - through2 "^2.0.3" - toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" @@ -13627,20 +13106,20 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -trim-newlines@^3.0.0: +trim-lines@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" - integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== trim-off-newlines@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz#8df24847fcb821b0ab27d58ab6efec9f2fe961a1" integrity sha512-kh6Tu6GbeSNMGfrrZh6Bb/4ZEHV1QlB4xNDBeog8Y9/QwFlKTRyWvY3Fs9tRDAMZliVUwieMgEdIeL/FtqjkJg== -trough@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" - integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +trough@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" + integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== ts-loader@^9.4.1: version "9.4.1" @@ -13731,11 +13210,6 @@ type-detect@4.0.8, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.18.0: - version "0.18.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" - integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -13756,6 +13230,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.0.0, type-fest@^2.5.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -13800,7 +13279,7 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6, typedarray@~0.0.5: +typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= @@ -13869,11 +13348,6 @@ uglify-js@^2.6.0: optionalDependencies: uglify-to-browserify "~1.0.0" -uglify-js@^3.1.4: - version "3.14.5" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.5.tgz#cdabb7d4954231d80cb4a927654c4655e51f4859" - integrity sha512-qZukoSxOG0urUTvjc2ERMTcAy+BiFh3weWAkeurLwjrCba73poHmG3E36XEjd/JGukMzwTL7uCxZiAexj8ppvQ== - uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" @@ -13907,17 +13381,18 @@ underscore@~1.10.2: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== -unified@^9.1.0: - version "9.2.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" - integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== +unified@^10.0.0: + version "10.1.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" + integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== dependencies: - bail "^1.0.0" + "@types/unist" "^2.0.0" + bail "^2.0.0" extend "^3.0.0" is-buffer "^2.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^5.0.0" union-value@^1.0.0: version "1.0.1" @@ -13939,14 +13414,6 @@ uniq@~0.0.2: resolved "https://registry.yarnpkg.com/uniq/-/uniq-0.0.2.tgz#614e868ba288651d351262369317acc43b901823" integrity sha512-5FZ2X1SBajH/GNCRkBaMJOpa5y9aEyeS6fLnTTDP0b25RmsmG+11QRt5b0852RcJw6+rqqIYUq8XuAZJS3eHPQ== -unique-stream@^2.0.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== - dependencies: - json-stable-stringify-without-jsonify "^1.0.1" - through2-filter "^3.0.0" - unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -13954,49 +13421,55 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-builder@^2.0.0, unist-builder@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" - integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== +unist-builder@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-3.0.1.tgz#258b89dcadd3c973656b2327b347863556907f58" + integrity sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ== + dependencies: + "@types/unist" "^2.0.0" -unist-util-generated@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" - integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== +unist-util-generated@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.1.tgz#e37c50af35d3ed185ac6ceacb6ca0afb28a85cae" + integrity sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A== -unist-util-is@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" - integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== +unist-util-is@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.2.1.tgz#b74960e145c18dcb6226bc57933597f5486deae9" + integrity sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== + dependencies: + "@types/unist" "^2.0.0" -unist-util-position@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" - integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== +unist-util-position@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-4.0.4.tgz#93f6d8c7d6b373d9b825844645877c127455f037" + integrity sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg== + dependencies: + "@types/unist" "^2.0.0" -unist-util-stringify-position@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" - integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== +unist-util-stringify-position@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d" + integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== dependencies: - "@types/unist" "^2.0.2" + "@types/unist" "^2.0.0" -unist-util-visit-parents@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" - integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== +unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" + integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== dependencies: "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" + unist-util-is "^5.0.0" -unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" - integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== +unist-util-visit@^4.0.0, unist-util-visit@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" + integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== dependencies: "@types/unist" "^2.0.0" - unist-util-is "^4.0.0" - unist-util-visit-parents "^3.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.1.1" unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" @@ -14011,6 +13484,14 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + update-browserslist-db@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" @@ -14122,6 +13603,16 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uvu@^0.5.0: + version "0.5.6" + resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" + integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== + dependencies: + dequal "^2.0.0" + diff "^5.0.0" + kleur "^4.0.3" + sade "^1.7.3" + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -14149,11 +13640,6 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= - vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -14168,101 +13654,69 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vfile-message@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" - integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== +vfile-location@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0" + integrity sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw== dependencies: "@types/unist" "^2.0.0" - unist-util-stringify-position "^2.0.0" + vfile "^5.0.0" -vfile-reporter@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-6.0.2.tgz#cbddaea2eec560f27574ce7b7b269822c191a676" - integrity sha512-GN2bH2gs4eLnw/4jPSgfBjo+XCuvnX9elHICJZjVD4+NM0nsUrMTvdjGY5Sc/XG69XVTgLwj7hknQVc6M9FukA== +vfile-message@^3.0.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea" + integrity sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw== dependencies: - repeat-string "^1.5.0" - string-width "^4.0.0" - supports-color "^6.0.0" - unist-util-stringify-position "^2.0.0" - vfile-sort "^2.1.2" - vfile-statistics "^1.1.0" - -vfile-sort@^2.1.0, vfile-sort@^2.1.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/vfile-sort/-/vfile-sort-2.2.2.tgz#720fe067ce156aba0b411a01bb0dc65596aa1190" - integrity sha512-tAyUqD2R1l/7Rn7ixdGkhXLD3zsg+XLAeUDUhXearjfIcpL1Hcsj5hHpCoy/gvfK/Ws61+e972fm0F7up7hfYA== + "@types/unist" "^2.0.0" + unist-util-stringify-position "^3.0.0" + +vfile-reporter@^7.0.4: + version "7.0.5" + resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-7.0.5.tgz#a0cbf3922c08ad428d6db1161ec64a53b5725785" + integrity sha512-NdWWXkv6gcd7AZMvDomlQbK3MqFWL1RlGzMn++/O2TI+68+nqxCPTvLugdOtfSzXmjh+xUyhp07HhlrbJjT+mw== + dependencies: + "@types/supports-color" "^8.0.0" + string-width "^5.0.0" + supports-color "^9.0.0" + unist-util-stringify-position "^3.0.0" + vfile "^5.0.0" + vfile-message "^3.0.0" + vfile-sort "^3.0.0" + vfile-statistics "^2.0.0" + +vfile-sort@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vfile-sort/-/vfile-sort-3.0.1.tgz#4b06ec63e2946749b0bb514e736554cd75e441a2" + integrity sha512-1os1733XY6y0D5x0ugqSeaVJm9lYgj0j5qdcZQFyxlZOSy1jYarL77lLyb5gK4Wqr1d5OxmuyflSO3zKyFnTFw== + dependencies: + vfile "^5.0.0" + vfile-message "^3.0.0" -vfile-statistics@^1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-1.1.4.tgz#b99fd15ecf0f44ba088cc973425d666cb7a9f245" - integrity sha512-lXhElVO0Rq3frgPvFBwahmed3X03vjPF8OcjKMy8+F1xU/3Q3QU3tKEDp743SFtb74PdF0UWpxPvtOP0GCLheA== +vfile-statistics@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-2.0.1.tgz#2e1adae1cd3a45c1ed4f2a24bd103c3d71e4bce3" + integrity sha512-W6dkECZmP32EG/l+dp2jCLdYzmnDBIw6jwiLZSER81oR5AHRcVqL+k3Z+pfH1R73le6ayDkJRMk0sutj1bMVeg== + dependencies: + vfile "^5.0.0" + vfile-message "^3.0.0" -vfile@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" - integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== +vfile@^5.0.0, vfile@^5.3.4: + version "5.3.7" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.7.tgz#de0677e6683e3380fafc46544cfe603118826ab7" + integrity sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g== dependencies: "@types/unist" "^2.0.0" is-buffer "^2.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" + unist-util-stringify-position "^3.0.0" + vfile-message "^3.0.0" -vinyl-fs@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== - dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" - is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= - dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" - -vinyl@^2.0.0, vinyl@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -vue-template-compiler@^2.6.12: - version "2.6.14" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763" - integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g== +vue-template-compiler@^2.7.8: + version "2.7.14" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz#4545b7dfb88090744c1577ae5ac3f964e61634b1" + integrity sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ== dependencies: de-indent "^1.0.2" - he "^1.1.0" + he "^1.2.0" w3c-hr-time@^1.0.1: version "1.0.2" @@ -14305,6 +13759,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-namespaces@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -14509,11 +13968,6 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - which@^1.2.10, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -14555,11 +14009,6 @@ wordwrap@0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= -wordwrap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= - worker-loader@^3.0.8: version "3.0.8" resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37" @@ -14576,15 +14025,6 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -14669,7 +14109,7 @@ xmlcreate@^2.0.3: resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.3.tgz#df9ecd518fd3890ab3548e1b811d040614993497" integrity sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ== -xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -14681,11 +14121,6 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== - y18n@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" @@ -14696,6 +14131,11 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -14706,42 +14146,22 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^20.2.2: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@^20.2.3, yargs-parser@^20.2.9: +yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^16.0.0, yargs@^16.2.0: +yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== @@ -14754,6 +14174,19 @@ yargs@^16.0.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.5.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" @@ -14777,12 +14210,17 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + zustand@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d" integrity sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA== -zwitch@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== +zwitch@^2.0.0, zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==