diff --git a/lib/commands.py b/lib/commands.py
index a0d81dce1..858a1d5b7 100644
--- a/lib/commands.py
+++ b/lib/commands.py
@@ -18,9 +18,9 @@ from .svg import (apply_transforms, generate_unique_id,
                   get_correction_transform, get_document, get_node_transform)
 from .svg.svg import copy_no_children, point_upwards
 from .svg.tags import (CONNECTION_END, CONNECTION_START, CONNECTOR_TYPE,
-                       INKSCAPE_LABEL, INKSTITCH_ATTRIBS, SVG_SYMBOL_TAG,
-                       SVG_USE_TAG, XLINK_HREF)
+                       INKSCAPE_LABEL, SVG_SYMBOL_TAG, SVG_USE_TAG, XLINK_HREF)
 from .utils import Point, cache, get_bundled_dir
+from .utils.geometry import ensure_multi_polygon
 
 COMMANDS = {
     # L10N command attached to an object
@@ -62,6 +62,7 @@ COMMANDS = {
 
 OBJECT_COMMANDS = ["starting_point", "ending_point", "target_point", "autoroute_start", "autoroute_end",
                    "stop", "trim", "ignore_object", "satin_cut_point"]
+HIDDEN_CONNECTOR_COMMANDS = ["starting_point", "ending_point", "autoroute_start", "autoroute_end"]
 FREE_MOVEMENT_OBJECT_COMMANDS = ["autoroute_start", "autoroute_end"]
 LAYER_COMMANDS = ["ignore_layer"]
 GLOBAL_COMMANDS = ["origin", "stop_position"]
@@ -125,24 +126,28 @@ class Command(BaseCommand):
             raise CommandParseError("connector has no path information")
 
         neighbors = [
-            (self.get_node_by_url(self.connector.get(CONNECTION_START)), path[0][0][1]),
-            (self.get_node_by_url(self.connector.get(CONNECTION_END)), path[0][-1][1])
+            self.get_node_by_url(self.connector.get(CONNECTION_START)),
+            self.get_node_by_url(self.connector.get(CONNECTION_END))
         ]
 
-        self.symbol_is_end = neighbors[0][0].tag != SVG_USE_TAG
+        self.symbol_is_end = neighbors[0].tag != SVG_USE_TAG
         if self.symbol_is_end:
             neighbors.reverse()
 
-        if neighbors[0][0].tag != SVG_USE_TAG:
+        if neighbors[0].tag != SVG_USE_TAG:
             raise CommandParseError("connector does not point to a use tag")
 
-        self.use = neighbors[0][0]
+        self.use = neighbors[0]
 
-        self.symbol = self.get_node_by_url(neighbors[0][0].get(XLINK_HREF))
+        self.symbol = self.get_node_by_url(neighbors[0].get(XLINK_HREF))
         self.parse_symbol()
 
-        self.target: inkex.BaseElement = neighbors[1][0]
-        self.target_point = neighbors[1][1]
+        self.target: inkex.BaseElement = neighbors[1]
+
+        pos = [float(self.use.get("x", 0)), float(self.use.get("y", 0))]
+        transform = get_node_transform(self.use)
+        pos = inkex.Transform(transform).apply_to_point(pos)
+        self.target_point = pos
 
     def __repr__(self):
         return "Command('%s', %s)" % (self.command, self.target_point)
@@ -331,7 +336,9 @@ def ensure_symbol(svg, command):
     path = "./*[@id='inkstitch_%s']" % command
     defs = svg.defs
     if defs.find(path) is None:
-        defs.append(deepcopy(symbol_defs().find(path)))
+        symbol = deepcopy(symbol_defs().find(path))
+        symbol.transform = 'scale(0.2)'
+        defs.append(symbol)
 
 
 def add_group(document, node, command):
@@ -373,7 +380,7 @@ def add_connector(document, symbol, command, element):
     path = inkex.PathElement(attrib={
         "id": generate_unique_id(document, "command_connector"),
         "d": f"M {start_pos[0]},{start_pos[1]} {end_pos[0]},{end_pos[1]}",
-        "style": "stroke:#000000;stroke-width:1px;stroke-opacity:0.5;fill:none;",
+        "style": "fill:none;stroke:#000000;stroke-width:1;stroke-opacity:0.5;vector-effect: non-scaling-stroke;-inkscape-stroke: hairline;",
         CONNECTION_START: f"#{symbol.get('id')}",
         CONNECTION_END: f"#{element.node.get('id')}",
 
@@ -383,6 +390,8 @@ def add_connector(document, symbol, command, element):
 
     if command not in FREE_MOVEMENT_OBJECT_COMMANDS:
         path.attrib[CONNECTOR_TYPE] = "polyline"
+    if command in HIDDEN_CONNECTOR_COMMANDS:
+        path.style['display'] = 'none'
 
     symbol.getparent().insert(0, path)
 
@@ -405,60 +414,28 @@ def add_symbol(document, group, command, pos):
 
 
 def get_command_pos(element, index, total):
-    # Put command symbols 30 pixels out from the shape, spaced evenly around it.
+    # Put command symbols on the outline of the shape, spaced evenly around it.
 
-    # get a line running 30 pixels out from the shape
-
-    if not isinstance(element.shape.buffer(30), shgeo.MultiPolygon):
-        outline = element.shape.buffer(30).exterior
+    if element.name == "Stroke":
+        shape = element.as_multi_line_string()
     else:
-        polygons = element.shape.buffer(30).geoms
-        polygon = polygons[len(polygons)-1]
-        outline = polygon.exterior
-
-    # find the top center point on the outline and start there
-    top_center = shgeo.Point(outline.centroid.x, outline.bounds[1])
-    start_position = outline.project(top_center, normalized=True)
+        shape = element.shape
+    polygon = ensure_multi_polygon(shape.buffer(0.01)).geoms[-1]
+    outline = polygon.exterior
 
     # pick this item's spot around the outline and perturb it a bit to avoid
     # stacking up commands if they add commands multiple times
     position = index / float(total)
     position += random() * 0.05
-    position += start_position
 
     return outline.interpolate(position, normalized=True)
 
 
-def remove_legacy_param(element, command):
-    if command == "trim" or command == "stop":
-        # If they had the old "TRIM after" or "STOP after" attributes set,
-        # automatically delete them.  The new commands will do the same
-        # thing.
-        #
-        # If we didn't delete these here, then things would get confusing.
-        # If the user were to delete a "trim" symbol added by this extension
-        # but the "embroider_trim_after" attribute is still set, then the
-        # trim would keep happening.
-
-        attribute = "embroider_%s_after" % command
-
-        if attribute in element.node.attrib:
-            del element.node.attrib[attribute]
-
-        # Attributes have changed to be namespaced.
-        # Let's check for them as well, they might have automatically changed.
-        attribute = INKSTITCH_ATTRIBS["%s_after" % command]
-
-        if attribute in element.node.attrib:
-            del element.node.attrib[attribute]
-
-
 def add_commands(element, commands, pos=None):
     svg = get_document(element.node)
 
     for i, command in enumerate(commands):
         ensure_symbol(svg, command)
-        remove_legacy_param(element, command)
 
         group = add_group(svg, element.node, command)
         position = pos
diff --git a/lib/elements/fill_stitch.py b/lib/elements/fill_stitch.py
index 03a8e1547..939d42d05 100644
--- a/lib/elements/fill_stitch.py
+++ b/lib/elements/fill_stitch.py
@@ -7,7 +7,7 @@ import math
 import re
 
 import numpy as np
-from inkex import LinearGradient, Transform
+from inkex import LinearGradient
 from shapely import geometry as shgeo
 from shapely import set_precision
 from shapely.errors import GEOSException
@@ -22,7 +22,7 @@ from ..stitches import (auto_fill, circular_fill, contour_fill, guided_fill,
                         legacy_fill, linear_gradient_fill, meander_fill,
                         tartan_fill)
 from ..stitches.linear_gradient_fill import gradient_angle
-from ..svg import PIXELS_PER_MM, get_node_transform
+from ..svg import PIXELS_PER_MM
 from ..svg.clip import get_clip_path
 from ..svg.tags import INKSCAPE_LABEL
 from ..tartan.utils import get_tartan_settings, get_tartan_stripes
@@ -1196,10 +1196,7 @@ class FillStitch(EmbroideryElement):
         # get target position
         command = self.get_command('target_point')
         if command:
-            pos = [float(command.use.get("x", 0)), float(command.use.get("y", 0))]
-            transform = get_node_transform(command.use)
-            pos = Transform(transform).apply_to_point(pos)
-            target = shgeo.Point(*pos)
+            target = shgeo.Point(*command.target_point)
         else:
             target = shape.centroid
         stitches = circular_fill(
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 14501c87c..96a9307f9 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -6,7 +6,6 @@
 from math import ceil
 
 import shapely.geometry as shgeo
-from inkex import Transform
 from shapely.errors import GEOSException
 
 from ..i18n import _
@@ -15,7 +14,7 @@ from ..stitch_plan import StitchGroup
 from ..stitches.ripple_stitch import ripple_stitch
 from ..stitches.running_stitch import (bean_stitch, running_stitch,
                                        zigzag_stitch)
-from ..svg import get_node_transform, parse_length_with_units
+from ..svg import parse_length_with_units
 from ..svg.clip import get_clip_path
 from ..threads import ThreadColor
 from ..utils import Point, cache
@@ -542,10 +541,7 @@ class Stroke(EmbroideryElement):
     def get_ripple_target(self):
         command = self.get_command('target_point')
         if command:
-            pos = [float(command.use.get("x", 0)), float(command.use.get("y", 0))]
-            transform = get_node_transform(command.use)
-            pos = Transform(transform).apply_to_point(pos)
-            return Point(*pos)
+            return shgeo.Point(*command.target_point)
         else:
             return self.shape.centroid
 
diff --git a/lib/extensions/commands_scale_symbols.py b/lib/extensions/commands_scale_symbols.py
index 0e5f482a9..fec8daf86 100644
--- a/lib/extensions/commands_scale_symbols.py
+++ b/lib/extensions/commands_scale_symbols.py
@@ -14,7 +14,8 @@ class CommandsScaleSymbols(InkstitchExtension):
         self.arg_parser.add_argument("-s", "--size", dest="size", type=int, default=100)
 
     def effect(self):
-        size = self.options.size / 100
+        # by default commands are scaled down to 0.2
+        size = 0.2 * self.options.size / 100
 
         # scale symbols
         svg = self.document.getroot()
diff --git a/lib/extensions/gradient_blocks.py b/lib/extensions/gradient_blocks.py
index b87e5f9e2..8e341cc8e 100644
--- a/lib/extensions/gradient_blocks.py
+++ b/lib/extensions/gradient_blocks.py
@@ -9,27 +9,23 @@ from inkex import (DirectedLineSegment, LinearGradient, PathElement, Transform,
                    errormsg)
 from shapely import geometry as shgeo
 from shapely.affinity import rotate
-from shapely.geometry import Point
-from shapely.ops import nearest_points, split
+from shapely.ops import split
 
-from ..commands import add_commands
 from ..elements import FillStitch
 from ..i18n import _
 from ..svg import PIXELS_PER_MM, get_correction_transform
 from ..svg.tags import INKSTITCH_ATTRIBS
-from .commands import CommandsExtension
 from .duplicate_params import get_inkstitch_attributes
+from .base import InkstitchExtension
 
 
-class GradientBlocks(CommandsExtension):
+class GradientBlocks(InkstitchExtension):
     '''
     This will break apart fill objects with a gradient fill into solid color blocks with end_row_spacing.
     '''
 
-    COMMANDS = ['starting_point', 'ending_point']
-
     def __init__(self, *args, **kwargs):
-        CommandsExtension.__init__(self, *args, **kwargs)
+        InkstitchExtension.__init__(self, *args, **kwargs)
         self.arg_parser.add_argument("--notebook", type=str, default=0.0)
         self.arg_parser.add_argument("--options", type=str, default=0.0)
         self.arg_parser.add_argument("--info", type=str, default=0.0)
@@ -64,8 +60,6 @@ class GradientBlocks(CommandsExtension):
                 end_row_spacing = element.row_spacing / PIXELS_PER_MM * 2
             end_row_spacing = f'{end_row_spacing: .2f}'
 
-            previous_color = None
-            previous_element = None
             for i, shape in enumerate(fill_shapes):
                 color = attributes[i]['color']
                 style['fill'] = color
@@ -98,29 +92,8 @@ class GradientBlocks(CommandsExtension):
                     block.set('inkstitch:fill_underlay_row_spacing_mm', end_row_spacing)
 
                 parent.insert(index, block)
-                if previous_color == color:
-                    self._add_block_commands(block, previous_element)
-                previous_color = color
-                previous_element = block
             parent.remove(element.node)
 
-    def _add_block_commands(self, block, previous_element):
-        current = FillStitch(block)
-        previous = FillStitch(previous_element)
-        if previous.shape.is_empty or current.shape.is_empty:
-            return
-        nearest = nearest_points(current.shape, previous.shape)
-        pos_current = self._get_command_postion(current, nearest[0])
-        pos_previous = self._get_command_postion(previous, nearest[1])
-        add_commands(current, ['ending_point'], pos_current)
-        add_commands(previous, ['starting_point'], pos_previous)
-
-    def _get_command_postion(self, fill, point):
-        center = fill.shape.centroid
-        line = DirectedLineSegment((center.x, center.y), (point.x, point.y))
-        pos = line.point_at_length(line.length + 20)
-        return Point(pos)
-
     def _element_to_path(self, shape):
         coords = list(shape.exterior.coords)
         for interior in shape.interiors:
diff --git a/lib/extensions/update_svg.py b/lib/extensions/update_svg.py
index f620af2d4..d51c1fa1a 100644
--- a/lib/extensions/update_svg.py
+++ b/lib/extensions/update_svg.py
@@ -3,9 +3,6 @@
 # Copyright (c) 2010 Authors
 # Licensed under the GNU GPL version 3.0 or later.  See the file LICENSE for details.
 
-from inkex import errormsg
-
-from ..i18n import _
 from ..update import update_inkstitch_document
 from .base import InkstitchExtension
 
@@ -14,24 +11,22 @@ class UpdateSvg(InkstitchExtension):
 
     def __init__(self, *args, **kwargs):
         InkstitchExtension.__init__(self, *args, **kwargs)
+        self.arg_parser.add_argument("--update-from", type=int, default=0, dest="update_from")
         # inkstitch_svg_version history:
         # 1 -> v3.0.0, May 2023
         # 2 -> v.3.1.0 May 2024
-
-        # TODO: When there are more legacy versions than only one, this can be transformed into a user input
-        self.update_from = 0
+        # 3 -> v.3.2.0 May2025
 
     def effect(self):
         if not self.svg.selection:
-            errormsg(_('Please select at least one element to update. '
-                       'This extension is designed to help you update copy and pasted elements from old designs.'))
+            update_inkstitch_document(self.document, warn_unversioned=False)
+        else:
+            # set the file version to the update_from value, so that the updater knows where to start from
+            # the updater will then reset it to the current version after the update has finished
+            metadata = self.get_inkstitch_metadata()
+            metadata['inkstitch_svg_version'] = self.options.update_from
 
-        # set the file version to the update_from value, so that the updater knows where to start from
-        # the updater will then reset it to the current version after the update has finished
-        metadata = self.get_inkstitch_metadata()
-        metadata['inkstitch_svg_version'] = self.update_from
-
-        update_inkstitch_document(self.document, self.get_selection())
+        update_inkstitch_document(self.document, self.get_selection(), warn_unversioned=False)
 
     def get_selection(self):
         selection = []
diff --git a/lib/gui/tartan/main_panel.py b/lib/gui/tartan/main_panel.py
index 9df6a748d..4435f4732 100644
--- a/lib/gui/tartan/main_panel.py
+++ b/lib/gui/tartan/main_panel.py
@@ -215,10 +215,14 @@ class TartanMainPanel(wx.Panel):
             self.save_settings()
             stitch_groups = []
             previous_stitch_group = None
-            for element in self.elements:
+            next_elements = [None]
+            if len(self.elements) > 1:
+                next_elements = self.elements[1:] + next_elements
+            for element, next_element in zip(self.elements, next_elements):
+                check_stop_flag()
                 try:
                     # copy the embroidery element to drop the cache
-                    stitch_groups.extend(copy(FillStitch(element)).embroider(previous_stitch_group))
+                    stitch_groups.extend(copy(FillStitch(element)).embroider(previous_stitch_group, next_element))
                     if stitch_groups:
                         previous_stitch_group = stitch_groups[-1]
                 except (SystemExit, ExitThread):
@@ -241,13 +245,16 @@ class TartanMainPanel(wx.Panel):
         for element in self.elements:
             parent = element.getparent()
             embroidery_elements = nodes_to_elements(parent.iterdescendants())
-            for embroidery_element in embroidery_elements:
+            next_elements = [None]
+            if len(embroidery_elements) > 1:
+                next_elements = embroidery_elements[1:] + next_elements
+            for embroidery_element, next_element in zip(embroidery_elements, next_elements):
                 check_stop_flag()
                 if embroidery_element.node == element:
                     continue
                 try:
                     # copy the embroidery element to drop the cache
-                    stitch_groups.extend(copy(embroidery_element).embroider(previous_stitch_group))
+                    stitch_groups.extend(copy(embroidery_element).embroider(previous_stitch_group, next_element))
                     if stitch_groups:
                         previous_stitch_group = stitch_groups[-1]
                 except InkstitchException:
diff --git a/lib/marker.py b/lib/marker.py
index ac19fe747..9765fbfb3 100644
--- a/lib/marker.py
+++ b/lib/marker.py
@@ -18,7 +18,9 @@ MARKER = ['anchor-line', 'pattern', 'guide-line']
 def ensure_marker(svg, marker):
     marker_path = ".//*[@id='inkstitch-%s-marker']" % marker
     if svg.defs.find(marker_path) is None:
-        svg.defs.append(deepcopy(_marker_svg().defs.find(marker_path)))
+        marker = deepcopy(_marker_svg().defs.find(marker_path))
+        marker.set('markerWidth', str(0.1))
+        svg.defs.append(marker)
 
 
 @cache
diff --git a/lib/tartan/svg.py b/lib/tartan/svg.py
index 15646629f..497d0199a 100644
--- a/lib/tartan/svg.py
+++ b/lib/tartan/svg.py
@@ -13,10 +13,8 @@ from inkex import BaseElement, Group, Path, PathElement
 from networkx import MultiGraph, is_empty
 from shapely import (LineString, MultiLineString, MultiPolygon, Point, Polygon,
                      dwithin, minimum_bounding_radius, reverse)
-from shapely.affinity import scale
 from shapely.ops import linemerge, substring
 
-from ..commands import add_commands
 from ..elements import FillStitch
 from ..stitches.auto_fill import (PathEdge, build_fill_stitch_graph,
                                   build_travel_graph, find_stitch_path,
@@ -127,52 +125,13 @@ class TartanSvgGroup:
         for color, fill_elements in fills.items():
             for element in fill_elements:
                 group.append(element)
-                if self.stitch_type == "auto_fill":
-                    self._add_command(element)
-                else:
-                    element.pop('inkstitch:start')
-                    element.pop('inkstitch:end')
+                element.pop('inkstitch:start')
+                element.pop('inkstitch:end')
 
         for color, stroke_elements in strokes.items():
             for element in stroke_elements:
                 group.append(element)
 
-    def _get_command_position(self, fill: FillStitch, point: Tuple[float, float]) -> Point:
-        """
-        Shift command position out of the element shape
-
-        :param fill: the fill element to which to attach the command
-        :param point: position where the command should point to
-        """
-        dimensions, center = self._get_dimensions(fill.shape)
-        line = LineString([center, point])
-        fact = 20 / line.length
-        line = scale(line, xfact=1+fact, yfact=1+fact, origin=center)
-        pos = line.coords[-1]
-        return Point(pos)
-
-    def _add_command(self, element: BaseElement) -> None:
-        """
-        Add a command to given svg element
-
-        :param element: svg element to which to attach the command
-        """
-        if not element.style('fill'):
-            return
-        fill = FillStitch(element)
-        if fill.shape.is_empty:
-            return
-        start = element.get('inkstitch:start')
-        end = element.get('inkstitch:end')
-        if start:
-            start = start[1:-1].split(',')
-            add_commands(fill, ['starting_point'], self._get_command_position(fill, (float(start[0]), float(start[1]))))
-            element.pop('inkstitch:start')
-        if end:
-            end = end[1:-1].split(',')
-            add_commands(fill, ['ending_point'], self._get_command_position(fill, (float(end[0]), float(end[1]))))
-            element.pop('inkstitch:end')
-
     def _route_shapes(self, routing_lines: defaultdict, outline_shape: MultiPolygon, shapes: defaultdict, weft: bool = False) -> defaultdict:
         """
         Route polygons and linestrings
diff --git a/lib/update.py b/lib/update.py
index 33ae6cc7e..4351beb7f 100644
--- a/lib/update.py
+++ b/lib/update.py
@@ -5,13 +5,15 @@
 
 from inkex import errormsg
 
-from .commands import ensure_symbol
-from .elements import EmbroideryElement
+from .commands import add_commands, ensure_symbol
+from .elements import EmbroideryElement, Stroke
 from .gui.request_update_svg_version import RequestUpdate
 from .i18n import _
 from .metadata import InkStitchMetadata
 from .svg import PIXELS_PER_MM
-from .svg.tags import EMBROIDERABLE_TAGS, INKSTITCH_ATTRIBS
+from .svg.tags import (CONNECTION_END, CONNECTION_START, EMBROIDERABLE_TAGS,
+                       INKSTITCH_ATTRIBS, SVG_USE_TAG)
+from .utils import Point as InkstitchPoint
 
 INKSTITCH_SVG_VERSION = 3
 
@@ -48,10 +50,10 @@ def update_inkstitch_document(svg, selection=None, warn_unversioned=True):
             return
 
         # update elements
-        if selection:
-            # this comes from the updater extension where we only update selected elements
+        if selection is not None:
+            # the updater extension might want to only update selected elements
             for element in selection:
-                update_legacy_params(EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
+                update_legacy_params(document, EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
         else:
             # this is the automatic update when a legacy inkstitch svg version was recognized
             automatic_version_update(document, file_version, INKSTITCH_SVG_VERSION, warn_unversioned)
@@ -69,9 +71,7 @@ def automatic_version_update(document, file_version, INKSTITCH_SVG_VERSION, warn
     # well then, let's update legeacy params
     for element in document.iterdescendants():
         if element.tag in EMBROIDERABLE_TAGS:
-            update_legacy_params(EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
-    if file_version < 3:
-        update_legacy_commands(document)
+            update_legacy_params(document, EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
 
 
 def _update_inkstitch_svg_version(svg):
@@ -80,24 +80,25 @@ def _update_inkstitch_svg_version(svg):
     metadata['inkstitch_svg_version'] = INKSTITCH_SVG_VERSION
 
 
-def update_legacy_params(element, file_version, inkstitch_svg_version):
+def update_legacy_params(document, element, file_version, inkstitch_svg_version):
     for version in range(file_version + 1, inkstitch_svg_version + 1):
-        _update_to(version, element)
+        _update_to(document, version, element)
 
 
-def _update_to(version, element):
+def _update_to(document, version, element):
     if version == 1:
         _update_to_one(element)
     elif version == 2:
         _update_to_two(element)
     elif version == 3:
-        _update_to_three(element)
+        _update_to_three(document, element)
 
 
-def _update_to_three(element):
+def _update_to_three(document, element):
     if element.get_boolean_param('satin_column', False):
         element.set_param('start_at_nearest_point', False)
         element.set_param('end_at_nearest_point', False)
+    update_legacy_commands(document, element)
 
 
 def _update_to_two(element):
@@ -193,7 +194,7 @@ Update legacy commands
 '''
 
 
-def update_legacy_commands(document):
+def update_legacy_commands(document, element):
     '''
     Changes for svg version 3
     '''
@@ -208,6 +209,39 @@ def update_legacy_commands(document):
         _rename_command(document, symbol, 'inkstitch_run_end', 'autoroute_end')
         _rename_command(document, symbol, 'inkstitch_ripple_target', 'target_point')
 
+    # reposition commands
+    start = element.get_command('starting_point')
+    if start:
+        reposition_legacy_command(start)
+    end = element.get_command('ending_point')
+    if end:
+        reposition_legacy_command(end)
+
+
+def reposition_legacy_command(command):
+    connector = command.connector
+    command_group = connector.getparent()
+    element = command.target
+    command_name = command.command
+
+    # get new target position
+    path = command.parse_connector_path()
+    if len(path) == 0:
+        pass
+    neighbors = [
+        (command.get_node_by_url(command.connector.get(CONNECTION_START)), path[0][0][1]),
+        (command.get_node_by_url(command.connector.get(CONNECTION_END)), path[0][-1][1])
+    ]
+    symbol_is_end = neighbors[0][0].tag != SVG_USE_TAG
+    if symbol_is_end:
+        neighbors.reverse()
+    target_point = neighbors[1][1]
+
+    # instead of calculating the transform for the new position, we take the easy route and remove
+    # the old commands and set new ones
+    add_commands(Stroke(element), [command_name], InkstitchPoint(*target_point))
+    command_group.getparent().remove(command_group)
+
 
 def _rename_command(document, symbol, old_name, new_name):
     symbol_id = symbol.get_id()
diff --git a/templates/update_svg.xml b/templates/update_svg.xml
index 924d97389..3cf6a0bf3 100644
--- a/templates/update_svg.xml
+++ b/templates/update_svg.xml
@@ -18,7 +18,9 @@
     <spacer />
     <label>Tip: You can prevent inserting legacy designs into new files by running any Ink/Stitch extension before you copy the design parts (for example open and re-apply parameters on a single element in the document).</label>
     <spacer />
-    <label appearance="header">This extension only updates selected elements.</label>
+    <label appearance="header">When there is an active selection, only selected elements will be updated.</label>
+    <spacer />
+    <param name="update-from" type="int" min="0" max="2" gui-text="Update from version" appearance="full">0</param>
     <script>
         {{ command_tag | safe }}
     </script>