emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/sketch-mode 443e095 15/15: Merge branch 'develop', publ


From: ELPA Syncer
Subject: [elpa] externals/sketch-mode 443e095 15/15: Merge branch 'develop', publish package :tada:
Date: Wed, 20 Oct 2021 05:57:37 -0400 (EDT)

branch: externals/sketch-mode
commit 443e095958cf66ec7fc6a24c28158a05674934d5
Merge: 457ba48 02b1c05
Author: Daniel Nicolai <dalanicolai@gmail.com>
Commit: Daniel Nicolai <dalanicolai@gmail.com>

    Merge branch 'develop', publish package :tada:
---
 README.org     |   91 +-
 sketch-mode.el | 2798 ++++++++++++++++++++++++++++----------------------------
 2 files changed, 1471 insertions(+), 1418 deletions(-)

diff --git a/README.org b/README.org
index f417424..9cb7585 100644
--- a/README.org
+++ b/README.org
@@ -1,26 +1,45 @@
 #+TITLE: Sketch mode
 #+DESCRIPTION: Quickly create simple SVG sketches using the mouse
 
+* Prepreliminary comment
+  The initial version with the transient can be found in the 
'transient-version'
+  branch. This version introduced a toolbar which made the transient
+  unnecessary. Also removing the transient frees up drawing space. It has been
+  replaced by a hydra which can togglable. Also an earlier version showed the
+  mouse coordinate position in the mode-line. However, this functionality
+  hinders the 'interactive' drawing (which might could be considered an emacs
+  bug). Anyway, you can toggle showing the coordinates by pressing =t c= (maybe
+  it works more fluently on your system).
+
 * Preliminary comment
-  This is a new package that is still in development. However, its main
-  functionality is very usable already. On the other hand, several (or most)
-  features are not implemented completely, simply because implementing these
-  things take time, and I should first focus on keeping myself alive:|. But if
-  you know some elisp, than it should be quite straightforward to complete the
-  implementation of those features. Any feedback, for example suggestions for
-  enhancing the interface/usability, is very welcome (probably best by opening
-  an issue). Also, any contributions are very welcome. The code of the package
-  is very accessible (especially if you quickly read how to use 
[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Edebug.html][edebug]]).
+  This is a new package that is still in development. It has been on ELPA-devel
+  for a while now, but it did not yet attract any code contributors. However,
+  despite that the code and docs are far from polished/finished, its main
+  functionality is very usable already, so that it is probably a good time to
+  publish it on ELPA. On the other hand, several (or most) features are not
+  implemented completely, simply because implementing these things take time,
+  and I should first focus on keeping myself alive 😐. But if you know some
+  elisp, than it should be quite straightforward to complete the implementation
+  of those features (and create a PR). The idea is that elisp users can add
+  functionalities easily so that the package becomes ever more versatile. Users
+  can also contribute by creating SVG snippets (in a separate repo, or create a
+  PR). Any feedback, for example suggestions for enhancing the
+  interface/usability (and of course bug reports), is very welcome (probably
+  best by opening an issue). Also, any contributions are very welcome. The code
+  of the package is very accessible (especially if you quickly read how to use
+  
[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Edebug.html][edebug]]).
 
   A list of ideas for implementation can be found in the preliminary comment in
   the =sketch.el= file and additionally in the 
[[https://github.com/dalanicolai/sketch-mode/wiki/vision][wiki]] section.
   
-  
 ** Included features
-   - snap to grid
+   - mnemonic shortcuts + hydra and (alternatively) a toolbar
+   - quickly insert image definition into new type (image) org-block (no
+     external file required)
+   - snap to grid (on minor-grid, however major and minor grid are fully
+     configurable)
    - draw text
    - crop finale image
-   - quickly insert image + definition into (org-mode) buffer
    - set stroke, fill, width etc.
    - show dom (lisp) in other window
    - draw angle arcs (between lines, available soon, I hope. See
@@ -30,16 +49,11 @@
      
 ** Incomplete features (merged into main)
    - Draw labels (not implemented for all type of objects. Easy to implement)
-   - Modify object (not, at all, fully implemented for all object. Easy to
-     implement).
      
      It would be handy to have a 'transform group' option also. 
[[https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform][SVG 
groups allow
      for easy transformations]]. Then it would probably be handy to wrap all
      objects in group tags.
 
-** Incomplete features (not merged into main)
-   - Implement layers (see/try out =implement-layers= branch)
-   
 ** Delicious low hanging fruit
    - use svg snippets (i.e. design object in external programs like inkscape,
      geogebra etc., end quickly insert them in your sketches)
@@ -95,24 +109,39 @@
   package, the usage is more or less self explanatory, it is wise to take note
   of the following comments:
 
-  - use =I= to quickly insert the xml-definition into the (org-mode) buffer 
from
-    which sketch-mode was called and create the image as an overlay.
-  - to remove an object (without using undo), you should toggle labels by
-    pressing =l=, and then to remove an object enter its label after pressing
-    =R=.
+  - use =C-c C-c= to quickly insert the xml-definition into the (org-mode)
+    buffer from which sketch-mode was called and create the image as an 
overlay.
+    The image will get inserted within a new =image= org block type. SVG/XML is
+    suitable for inserting directly in an org file so that you do not need to
+    store the image separately on disk (which is nice feature when sharing
+    files). The new block type is not yet 'officially supported' by org-mode, 
so
+    that it will not yet get exported as an image (HELP WANTED :nerd:), but the
+    image in the code block can be toggle with =C-c C-c=.
+  - Alternatively you can write the image to a file by pressing =S= (S-ave).
+  - Before you insert the image you can use =C-S mouse-drag= to crop the image.
+  - You can move an object by pressing =m= to open the 'modify-object' state.
+    This will select the object and activate the =translate= mouse action so
+    that you can drag the object using the mouse.
+  - to remove an object (without using undo), you should press =d=, and then 
the
+    label of the object you want removed.
   - You can also modify the drawing by changing the object definition (i.e.
     elisp). For that press =d= to open the definition in a side-window, then
     press =q= to hide (deactivate the) transient (keymap). Now modify the code
-    and press =C-c C=c=, to load it and update the =\*sketch\*= buffer. 
-  - After you've hidden the transient by pressing =q=, you can go back to
-    sketch mode via =M-x sketch= (or =C-c C-s= when still in the sketch-mode
-    buffer)
-
-  Create your sketch and then save
-  the file by pressing =S=.
-
+    and press =C-c C=c=, to load it and update the =\*sketch\*= buffer.    
+
+* Bugs
+  Currently when undoing all (drawing of) objects, sketch-mode gets confused 
and
+  further drawing is not possible anymore (although redoing is). This is
+  probably a very easy to solve bug, but has not been a priority yet.
+
+* Alternatives
+  
[[https://lifeofpenguin.blogspot.com/2021/08/scribble-notes-in-gnu-emacs.html][canvas-mode]]:
 An even newer package is being created which provides some
+  additional features (although =sketch-mode= is still in development and most
+  probably will get most of these features too). Unfortunately, the package is
+  not (yet?) very compatible with =sketch-mode=.
+  
 * Sponsor the project
-  It takes me a lot of time to develop these packages, while, as we would say 
in
+  It takes me a lot of time to develop (this) package(s), while, as we would 
say in
   the Netherlands, I have no penny to scratch my butt. Therefore, although I am
   also really happy to offer it for free, if you find 
[[https://github.com/dalanicolai][my package(s)]] (real
   projects page in the making) useful (e.g. for you work), and if you can 
afford
diff --git a/sketch-mode.el b/sketch-mode.el
index 74a8a40..6572349 100644
--- a/sketch-mode.el
+++ b/sketch-mode.el
@@ -4,7 +4,7 @@
 
 ;; Author: D.L. Nicolai <dalanicolai@gmail.com>
 ;; Created: 17 Jul 2021
-;; Version: 0
+;; Version: 1.0
 
 ;; Keywords: multimedia
 ;; URL: https://github.com/dalanicolai/sketch-mode
@@ -31,28 +31,10 @@
 
 ;; DONE implement (simple) undo mechanism
 
-;; DONE add remove (objects) functionality (see `svg-remove')
-
-;; DONE move font transient (also its suffix) into main sketch transient 
(suffix)
-
-;; DONE add functionality to crop/select part of image (on/before save)
-
-;; DONE(-partially) add functionality to modify objects (see 
`add-object-modify-feature' branch)
-
-;; TODO add functionality to customize markers
-
-;; TODO Add options to hide transient suffixes (e.g. commands are trivial and 
could be hidden to get more drawing space.
-;; unfortunately transient levels (de)activate instead of hide/show suffixes)
-
-;; TODO enable defining global svg settings (object properties)
-
-;; TODO maybe transform relevant transient argument (strings) to variables
-
-;; TODO add function to open svg code in 'other buffer' and quickly reload
-;; (after editing, DONE see `add-object-modify-feature' branch)
 
+;; TODO maybe transform relevant transient argument (strings) to variables ;; 
(af`add-object-modify-feature' branch)
 ;; TODO add functionality to start drawing from org-mode source block and 
update
-;; source block after each draw/edit
+;; source block after each draw/edit (showing the image as the block'ss output)
 
 ;; TODO maybe add keybindings (save/bind transient setting to specific 'mouse 
keys')
 
@@ -72,87 +54,49 @@
 ;; could implement a drawing DSL based on nodes (a la tikz/asymptote etc.)
 
 
-;;; Code:
+;;;; Code
 (require 'svg)
-(require 'transient)
-(require 'cl-lib)
-(require 'sgml-mode)
+;; (require 'seq)
 (require 'shr-color)
-
-(defgroup sketch nil
-  "Configure default sketch (object) properties."
-  :group 'Applications)
-
-(defcustom sketch-im-x-offset 7
-  "Horizontal offset in pixels of image position within frame.
-Set this value to correct for cursor 'bias'."
-  :type 'integer)
-
-(defcustom sketch-im-y-offset 1
-  "Vertical offset in pixels of image position within frame.
-Set this value to correct for cursor 'bias'."
-  :type 'integer)
-
-(defcustom sketch-default-image-size '(800 . 600)
-  "Default size for sketch canvas.
-Cons cell with car and cdr both integers, respectively
-representing the image width and image height
-default: (800 . 600)."
-  :type '(cons integer integer))
-
-(defcustom sketch-show-grid t
-  "When non-nil, show grid lines (default: t)."
-  :type 'boolean)
-
-(defcustom sketch-show-labels nil
-  "When non-nil, show object labels (default: t)."
-  :type 'boolean)
-
-(defcustom sketch-default-grid-parameter 25
-  "Default grid line separation distance (integer)."
-  :type 'integer)
-
-(defcustom sketch-label-size 15
-  "Size of object labels."
-  :type 'integer)
-
-(defcustom sketch-default-shape 'line
-  "Default object type for `sketch-interactively'."
-  :type '(choice
-               (const :tag "Line" 'line)
-               (const :tag "Rectangle" 'rectangle)
-          (const :tag "Circle" 'circle)
-          (const :tag "Ellipse" 'ellipse)))
-
-(defcustom sketch--snap-to-grid t
-  "Default value of snap to grid.
-If non-nil then snap to grid."
-  :type 'boolean)
-
-(defcustom sketch-include-start-marker nil
-  "Start marker type."
-  :type '(choice
-               (const :tag "No marker" nil)
-               (const :tag "Arrow" 'arrow)
-          (const :tag "Dot" 'dot)))
-
-(defcustom sketch-include-mid-marker nil
-  "Mid marker type."
-  :type '(choice
-               (const :tag "No marker" nil)
-               (const :tag "Arrow" 'arrow)
-          (const :tag "Dot" 'dot)))
-
-(defcustom sketch-include-end-marker nil
-  "End marker type."
-  :type '(choice
-               (const :tag "No marker" nil)
-               (const :tag "Arrow" 'arrow)
-          (const :tag "Dot" 'dot)))
-
-
-;;; SVG-definitions
-
+(require 'sgml-mode)
+(require 'hydra nil t)
+
+
+;;; Rendering
+(defvar sketch-svg nil)
+(defvar sketch-size '(1200 . 900))
+(defvar sketch-grid nil)
+(defvar sketch-show-grid t)
+(defvar sketch-background "white")
+(defvar sketch-grid-param 50)
+(defvar sketch-minor-grid-param nil)
+(defvar sketch-minor-grid-freq 4)
+(defvar sketch-grid-colors '("gray" . "gray"))
+(defvar sketch-default-stroke "black")
+(defvar sketch-snap-to-grid t)
+
+(defvar sketch-canvas nil)
+(defvar sketch-root nil)
+
+(defvar sketch-active-layer 0)
+(defvar sketch-layers-list nil)
+(defvar show-layers nil)
+
+(defvar sketch-show-labels nil)
+(defvar sketch-label-size 15)
+
+(defvar sketch-lisp-buffer-name nil)
+(defvar sketch-side-window-max-width (lambda () (- 1 (/ (float (car
+                                                                (image-size
+                                                                 
(get-text-property (point-min) 'display) t)))
+                                                        (frame-pixel-width)))))
+(defvar sketch-im-x-offset nil)
+(defvar sketch-cursor-position "")
+(defvar sketch-show-coords nil)
+(defvar sketch-coordless-mode-line-format nil)
+
+
+;;; Some snippets for svg.el
 (defun svg-marker (svg id width height &optional color reverse)
   "Define a marker with ID to SVG.
 TYPE is `linear' or `radial'.
@@ -190,493 +134,60 @@ STOPS is a list of percentage/color pairs."
          'g
          `(,(svg--arguments nil args))))
 
-
-;;; Resume sketch-code
-
 (defun sketch-group (id &rest args)
   (apply #'svg-group
          :id id
          :transform "translate(0,0)"
          args))
 
-;; Web/SVG colors
-(defun sketch-colors-sort (colors-rgb-alist)
-  (let ((list-colors-sort 'hsv))
-    ;; color sort function in courtesy of facemenu.el
-    ;; (colors-sorted (mapcar (lambda (c) (cons c (color-name-to-rgb c))) 
(defined-colors)))
-    ;; Schwartzian transform with `(color key1 key2 key3 ...)'.
-    (mapcar
-                'car
-                (sort (delq nil (mapcar
-                                             (lambda (c)
-                                               (let ((key (list-colors-sort-key
-                                                                       (car 
c))))
-                                                 (when key
-                                                         (cons c (if (consp 
key)
-                                        key
-                                                                         (list 
key))))))
-                                             colors-rgb-alist)) ;; HERE IS THE 
LIST
-                            (lambda (a b)
-                              (let* ((a-keys (cdr a))
-                                           (b-keys (cdr b))
-                                           (a-key (car a-keys))
-                                           (b-key (car b-keys)))
-                                ;; Skip common keys at the beginning of key 
lists.
-                                (while (and a-key b-key (equal a-key b-key))
-                                  (setq a-keys (cdr a-keys) a-key (car a-keys)
-                                              b-keys (cdr b-keys) b-key (car 
b-keys)))
-                                (cond
-                                 ((and (numberp a-key) (numberp b-key))
-                                  (< a-key b-key))
-                                 ((and (stringp a-key) (stringp b-key))
-                                  (string< a-key b-key)))))))))
-
-;; Adapted from `read-color'
-(defun read-color-web (&optional prompt convert-to-RGB)
-  "Read a color name or RGB triplet.
-Completion is available for color names, but not for RGB triplets.
-
-RGB triplets have the form \"#RRGGBB\".  Each of the R, G, and B
-components can have one to four digits, but all three components
-must have the same number of digits.  Each digit is a hex value
-between 0 and F; either upper case or lower case for A through F
-are acceptable.
-
-In addition to standard color names and RGB hex values, the
-following are available as color candidates.  In each case, the
-corresponding color is used.
-
- * `foreground at point'   - foreground under the cursor
- * `background at point'   - background under the cursor
-
-Optional arg PROMPT is the prompt; if nil, use a default prompt.
-
-Interactively, or with optional arg CONVERT-TO-RGB-P non-nil,
-convert an input color name to an RGB hex string.  Return the RGB
-hex string.
-
-Interactively, displays a list of colored completions.  If optional
-argument FOREGROUND is non-nil, shows them as foregrounds, otherwise
-as backgrounds."
-  (interactive "i\np")    ; Always convert to RGB interactively.
-  (let* ((completion-ignore-case t)
-              (colors (mapcar
-                  (lambda (color-name)
-                    (let ((color (copy-sequence color-name)))
-                      (propertize color 'face
-                                               (list :foreground 
(readable-foreground-color color-name)
-                                          :background color))))
-                  (mapcar #'car (sketch-colors-sort 
shr-color-html-colors-alist))))
-        (color (completing-read
-                (or prompt "Color (name or #RGB triplet): ")
-                ;; Completing function for reading colors, accepting
-                ;; both color names and RGB triplets.
-                (lambda (string pred flag)
-                  (cond
-                   ((null flag)        ; Try completion.
-                    (or (try-completion string colors pred)
-                        (if (color-defined-p string)
-                            string)))
-                   ((eq flag t)        ; List all completions.
-                    (or (all-completions string colors pred)
-                        (if (color-defined-p string)
-                            (list string))))
-                   ((eq flag 'lambda)  ; Test completion.
-                    (or (member string colors)
-                        (color-defined-p string)))))
-                nil t)))
-
-    ;; Process named colors.
-    (when (member color colors)
-      (cond ((string-equal color "foreground at point")
-            (setq color (foreground-color-at-point)))
-           ((string-equal color "background at point")
-            (setq color (background-color-at-point))))
-      (when (and convert-to-RGB
-                (not (string-equal color "")))
-       (let ((components (x-color-values color)))
-         (unless (string-match-p 
"^#\\(?:[[:xdigit:]][[:xdigit:]][[:xdigit:]]\\)+$" color)
-           (setq color (format "#%04X%04X%04X"
-                               (logand 65535 (nth 0 components))
-                               (logand 65535 (nth 1 components))
-                               (logand 65535 (nth 2 components))))))))
-    color))
-
-;; minor-mode
-(define-minor-mode sketch-mode
-  "Create svg images using the mouse.
-In sketch-mode buffer press \\[sketch-transient] to activate the
-transient."
-  :lighter "sketch-mode"
-  :keymap
-  `(
-    ;; ([sketch drag-mouse-1] . sketch-interactively)
-    ;; ([C-S-drag-mouse-1] . sketch-interactively)
-    (,(kbd "C-c C-s") . sketch-transient))
-  (if (boundp 'undo-tree-mode)
-      (undo-tree-mode)
-    (buffer-enable-undo)
-    (blink-cursor-mode 0)))
-
-(defun sketch-mapcons (fn &rest cons-cells)
-  "Apply FN to list of car's and cdr's of CONS-CELLS.
-Return a single cons cell."
-  (cons (apply fn (mapcar #'car cons-cells))
-        (apply fn (mapcar #'cdr cons-cells))))
-
-(defun sketch-norm (vec)
-  "Return norm of a vector.
-VEC should be a cons or a list containing only number elements."
-  (let ((sum-of-squares (if (consp vec)
-                            (+ (expt (car vec) 2)
-                               (expt (cdr vec) 2))
-                          (apply #'+
-                                 (mapcar (lambda (x) (* x x))
-                                         vec)))))
-    (expt sum-of-squares (/ 1.0 (if (consp vec)
-                                    2
-                                  (length vec))))))
-
-(defun sketch--circle-radius (start-coords end-coords)
-  (sketch-norm
-   (sketch-mapcons #'- end-coords start-coords)))
-
-(defun sketch--rectangle-coords (start-coords end-coords)
-  (let ((base-coords (cons (apply #'min (list (car start-coords) (car 
end-coords)))
-                           (apply #'min (list (cdr start-coords) (cdr 
end-coords))))))
-    (list (car base-coords)
-          (cdr base-coords)
-          (abs (- (car end-coords) (car start-coords)))
-          (abs (- (cdr end-coords) (cdr start-coords))))))
-
-(defun sketch--ellipse-coords (start-coords end-coords)
-  (list (/ (+ (car start-coords) (car end-coords)) 2)
-        (/ (+ (cdr start-coords) (cdr end-coords)) 2)
-        (abs (/ (- (car end-coords) (car start-coords)) 2))
-        (abs (/ (- (cdr end-coords) (cdr start-coords)) 2))))
+(defun sketch-create (w h &optional scale pan-x pan-y &rest args)
+  (let ((scale (or scale 1)))
+    (apply #'svg-create w h
+           :viewBox (format "%s %s %s %s"
+                            (or pan-x 0)
+                            (or pan-y 0)
+                            (/ (float w) scale)
+                            (/ (float h) scale))
+           args)))
 
-(defvar-local sketch-svg nil)
-(defvar-local svg-canvas nil)
-(defvar-local sketch-grid nil)
-(defvar-local sketch-root nil)
-(defvar-local sketch-layers-list nil)
-(defvar-local show-layers nil)
-(defvar-local sketch-cursor-position nil)
-
-(defun sketch--create-canvas (width height &optional grid-parameter)
-  "Create canvas of size WIDTH x HEIGHT for drawing svg.
-Optionally set a custom GRID-PARAMETER (default is value of
-`sketch-default-grid-parameter')."
-  (let ((width width)
-        (height height))
-    (setq svg-canvas (svg-create width height :stroke "gray"))
-    (svg-marker svg-canvas "arrow" 8 8 "black" t)
-    (svg-rectangle svg-canvas 0 0 width height :fill "white")
-    (setq sketch-grid (sketch-group "grid"))
-    (let ((dash t))
-      (dotimes (x (1- (/ width grid-parameter)))
-        (let ((pos (* (1+ x) grid-parameter)))
-          (svg-line sketch-grid pos 0 pos height :stroke-dasharray (when dash 
"2,4"))
-          (setq dash (if dash nil t)))))
-    (let ((dash t))
-      (dotimes (x (1- (/ height grid-parameter)))
-        (let ((pos (* (1+ x) grid-parameter)))
-          (svg-line sketch-grid 0 pos width pos :stroke-dasharray (when dash 
"2,4"))
-          (setq dash (if dash nil t)))))
-    (setq sketch-svg (append svg-canvas (when sketch-show-grid (list 
sketch-grid))))
-    (setq sketch-root (sketch-group "root"))
-    (setq sketch-layers-list (list (sketch-group "layer-0")))
-    (setq show-layers '(0))
-    (sketch-insert-image sketch-svg
-                         (prin1-to-string sketch-root)
-                                :grid-param grid-parameter
-                                :pointer 'arrow
-                                :map `(((rect . ((0 . 0) . (,(dom-attr 
sketch-svg 'width) . ,(dom-attr sketch-svg 'height))))
-                                        ;; :map '(((rect . ((0 . 0) . (800 . 
600)))
-                                        sketch
-                                        (pointer arrow help-echo (lambda (_ _ 
pos)
-                                                                   (let (
-                                                                         ;; 
(message-log-max nil)
-                                                                         
(coords (cdr (mouse-pixel-position))))
-                                                                     (setq 
sketch-cursor-position (format "(%s, %s)"
-                                                                               
                           (- (car coords) sketch-im-x-offset)
-                                                                               
                           (+ (cdr coords) sketch-im-y-offset))))
-                                                                               
                                   (force-mode-line-update))))))
-    (beginning-of-line)))
-
-;; FIXME: `defvar' can't be meaningfully inside a function like that.
-;; FIXME: Use a `sketch-' prefix for all dynbound vars.
-(defvar-local sketch-elements nil)
-(defvar-local sketch-grid-param 25)
-(defvar-local sketch-active-layer 0)
-(defvar-local sketch-call-buffer nil)
+(defun sketch-image (svg &rest props)
+  "Return an image object-label from SVG.
+PROPS is passed on to `create-image' as its PROPS list."
+  (apply
+   #'create-image
+   (with-temp-buffer
+     (insert "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
+<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" 
\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\";>\n")
+     (svg-print svg)
+     (buffer-string))
+   'svg t props))
 
-;;;###autoload
-(defun sketch (arg)
-  "Initialize or switch to (new) SVG image.
-With prefix ARG, create sketch using default (customizable)
-values"
-  (interactive "P")
-  (let ((call-buffer (current-buffer)) ;; to set value as local variable later 
in '*sketch*' buffer
-        (buffer (get-buffer "*sketch*")))
-    (if buffer
-        (progn (switch-to-buffer buffer)
-               (call-interactively 'sketch-transient))
-      (let ((width (if arg (car sketch-default-image-size) (read-number "Enter 
width: ") ))
-            (height (if arg 600 (read-number "Enter height: "))))
-        (switch-to-buffer (get-buffer-create "*sketch*"))
-        (add-to-list 'mode-line-format '(:eval sketch-cursor-position) t)
-        (setq sketch-grid-param (if arg 25 (read-number "Enter grid parameter 
(enter 0 for no grid): ")))
-        (sketch--create-canvas width height sketch-grid-param))
-      (setq sketch-call-buffer call-buffer) ;; variable is buffer local
-      (sketch-mode)
-      (call-interactively 'sketch-transient))))
-
-
-(defun sketch--snap-to-grid (coord grid-parameter)
-  (cons (* (round (/ (float (car coord)) grid-parameter)) grid-parameter)
-        (* (round (/ (float (cdr coord)) grid-parameter)) grid-parameter)))
-
-
-;;; Transient
-
-(defclass sketch-variable:choices (transient-variable)
-  ((choices     :initarg :choices)
-   (fallback    :initarg :fallback    :initform nil)
-   (default     :initarg :default     :initform nil)))
-
-(cl-defmethod transient-infix-read ((obj sketch-variable:choices))
-  (let ((choices (oref obj choices)))
-    (if-let ((value (oref obj value)))
-        (cadr (member value choices))
-      (car choices))))
-
-(cl-defmethod transient-infix-value ((obj sketch-variable:choices))
-  "Return the value of OBJ's `value' slot if non-nil,
-else return value of OBJ's `default' slot if non-nil,
-else return nil"
-  (let ((default (oref obj default)))
-    (if-let ((value (oref obj value)))
-        (concat (oref obj argument) value)
-      (when default
-        (concat (oref obj argument) default)))))
-
-(cl-defmethod transient-format-value ((obj sketch-variable:choices))
-  (let ((value (oref obj value))
-        (choices (oref obj choices))
-        (default  (oref obj default)))
-    (concat
-     (propertize "[" 'face 'transient-inactive-value)
-     (mapconcat (lambda (choice)
-                  (propertize choice 'face (if (equal choice value)
-                                               (if (member choice choices)
-                                                   'transient-value
-                                                 'font-lock-warning-face)
-                                             'transient-inactive-value)))
-                choices
-                (propertize "|" 'face 'transient-inactive-value))
-     (and (or default)
-          (concat
-           (propertize "|" 'face 'transient-inactive-value)
-           (cond
-            (default
-              (propertize (concat "default:" default)
-                          'face
-                          (if value
-                              'transient-inactive-value
-                            'transient-value))))))
-     (propertize "]" 'face 'transient-inactive-value))))
-
-(defclass sketch-variable:colors (transient-variable)
-  ((fallback    :initarg :fallback    :initform nil)
-   (default     :initarg :default     :initform nil)))
-
-(cl-defmethod transient-infix-read ((_obj sketch-variable:colors))
-  (read-color-web "Select color: "))
-
-(cl-defmethod transient-infix-value ((obj sketch-variable:colors))
-  (let ((default (oref obj default)))
-    (if-let ((value (oref obj value)))
-        (concat (oref obj argument) (substring-no-properties value))
-      (when default
-        (concat (oref obj argument) (substring-no-properties default))))))
-
-;; We always call the autoloaded `color-name-to-rgb' before calling this
-;; function, so we know it's available even tho the compiler doesn't.
-(declare-function color-rgb-to-hex "color"
-                  (red green blue &optional digits-per-component))
-
-(cl-defmethod transient-format-value ((obj sketch-variable:colors))
-  (let ((value (oref obj value))
-        (default  (oref obj default)))
-    (if value
-        (format "%s (%s)"
-                (propertize value 'face (cons 'foreground-color value))
-                (propertize (apply #'color-rgb-to-hex (color-name-to-rgb 
value))
-                            'face 'transient-inactive-argument))
-      (if (string= default "none")
-          (propertize "none" 'face 'transient-inactive-argument)
-        (format "%s (%s)"
-                (propertize default 'face (cons 'foreground-color default))
-                (propertize (apply #'color-rgb-to-hex (color-name-to-rgb 
default))
-                            'face 'transient-inactive-argument))))))
-
-;; (let* ((args (when transient-current-prefix (transient-args 
'sketch-transient)))
-;;        (print event))))
-;;      (start (event-start event))
-;;      (sketch-grid-param (plist-get (cdr (posn-image start)) :grid-param))
-;;      (snap (transient-arg-value "--snap-to-grid=" args))
-;;      (start-coords (if (or (not snap) (string= snap "nil"))
-;;                       (posn-object-x-y start)
-;;                     (sketch--snap-to-grid (posn-object-x-y start) 
sketch-grid-param)))
-;;      (end (event-end event))
-;;      (end-coords (if (or (not snap) (string= snap "nil"))
-;;                     (posn-object-x-y end)
-;;                   (sketch--snap-to-grid (posn-object-x-y end) 
sketch-grid-param)))
-;;      (object-props (list :stroke-width
-;;                          (transient-arg-value "--stroke-width=" args)
-;;                          :stroke
-;;                          (transient-arg-value "--stroke-color=" args)
-;;                          :fill
-;;                          (transient-arg-value "--fill-color=" args)
-;;                          :marker-end (if args (pcase (transient-arg-value 
"--marker=" args)
-;;                                                 ("arrow" "url(#arrow)")
-;;                                                 ("point" "url(#point)")
-;;                                                 (_ "none"))
-;;                                        (if sketch-include-end-marker
-;;                                            "url(#arrow)"
-;;                                          "none"))))
-;;      (command-and-coords (pcase (transient-arg-value "--object=" args)
-;;                            ("line" (list 'svg-line
-;;                                          (car start-coords) (cdr 
start-coords) (car end-coords) (cdr end-coords)))
-;;                            ("rectangle" `(svg-rectangle 
,@(sketch--rectangle-coords start-coords end-coords)))
-;;                            ("circle" (list 'svg-circle
-;;                                            (car start-coords) (cdr 
start-coords)
-;;                                            (sketch--circle-radius 
start-coords end-coords)))
-;;                            ("ellipse" `(svg-ellipse 
,@(sketch--ellipse-coords start-coords end-coords))))))
-;; (apply (car command-and-coords) sketch-root `(,@(cdr command-and-coords) 
,@object-props :id ,(sketch-create-label)))
-;; (sketch-redraw)))
-
-(transient-define-prefix sketch-transient ()
-  "Some Emacs magic"
-  :transient-suffix     'transient--do-call
-  :transient-non-suffix 'transient--do-stay
-  [["General definitions"
-    ("c" "stroke-color" sketch-stroke-color)
-    ("C" "fill-color" sketch-fill-color)
-    ("w" "stroke-width" sketch-stroke-width)
-    ("d" "stroke-dasharray" sketch-dasharray)]
-   ["Object definitions"
-    ("o" "object" sketch-object)
-    ("m" "end-marker" sketch-object-marker)]
-   ["Font definitions"
-    ("ff" "family" sketch-select-font)
-    ("fw" "font-weight" sketch-font-weight)
-    ("fs" "font-size" sketch-font-size)]]
-  [["Grid"
-    ("s" "Snap to grid" sketch-snap)
-    ("g" "Toggle grid" sketch-toggle-grid)]
-   ["Labels"
-    ("l" sketch-cycle-labels)]
-   ["Layers"
-    ("L" sketch-layer)
-    ("-L" sketch-layers)
-    ("A" "Add layer" sketch-add-layer)]]
-  ["Commands"
-   [([sketch down-mouse-1] "Draw object"  sketch-interactively-1)
-    ([sketch mouse-1] "Draw text"  sketch-text-interactively)
-    ([sketch C-S-drag-mouse-1] "Crop image" sketch-crop)
-    ;; ("T" "Polyline" test-mouse)
-    ;; ([sketch S-down-mouse-1] "Track mouse" sketch-line)
-    ]
-   [("t" "Transform object" sketch-modify-object)
-    ("r" "Remove object" sketch-remove-object)
-    ("i" "Import object" sketch-import)]
-   [("u" "Undo" sketch-undo)
-    ("U" "Redo" sketch-redo)]
-   [("D" "Show definition" sketch-show-definition)
-    ("K" "Copy definition" sketch-copy-definition)
-    ("S" "Save image" sketch-save)]
-   [("b" "Insert image to buffer" sketch-insert-image-to-buffer
-     :transient transient--do-exit)
-    ("I" "Insert image to buffer" sketch-quick-insert-image
-     :transient transient--do-exit)
-    ("q" "Quit transient" transient-quit-one)]])
-
-(transient-define-infix sketch-object ()
-  :description "Option with list"
-  :class 'sketch-variable:choices
-  :argument "--object="
-  :choices '("rectangle" "circle" "ellipse" "polyline" "polygon" "freehand")
-  :default "line")
-
-(transient-define-infix sketch-stroke-color ()
-  :description "Option with list"
-  :class 'sketch-variable:colors
-  :argument "--stroke-color="
-  :default "black")
-
-(transient-define-infix sketch-fill-color ()
-  :description "Option with list"
-  :class 'sketch-variable:colors
-  :argument "--fill-color="
-  :default "none")
-
-(transient-define-infix sketch-stroke-width ()
-  :description "Option with list"
-  :class 'transient-option
-  :argument "--stroke-width="
-  :choices (mapcar (lambda (x)
-                     (number-to-string x))
-                   (number-sequence 1 100)))
-
-(transient-define-infix sketch-dasharray ()
-  :description "stroke-dasharray"
-  :class 'sketch-variable:choices
-  :argument "--stroke-dasharray="
-  :choices '("8" "8,4")
-  :default "none")
-
-(transient-define-infix sketch-object-marker ()
-  :description "Option with list"
-  :class 'sketch-variable:choices
-  :argument "--marker="
-  :choices '("arrow" "dot")
-  :default "none")
-
-(transient-define-infix sketch-snap ()
-  :description "Option with list"
-  :class 'sketch-variable:choices
-  :argument "--snap-to-grid="
-  :choices '("t")
-  :default "nil")
+(defun sketch-insert-image (svg string &rest props)
+  "Insert SVG as an image at point.
+If the SVG is later changed, the image will also be updated."
+  (let ((image (apply #'sketch-image svg props))
+        (marker (point-marker)))
+    (insert-image image string)
+    (dom-set-attribute svg :image marker)))
 
-(defun sketch-toggle-grid ()
+(defun sketch-add-layer ()
   (interactive)
-  (with-current-buffer "*sketch*"
-    (setq sketch-show-grid (if sketch-show-grid nil t))
-    (sketch-redraw)))
+  (let ((new-layer (length sketch-layers-list)))
+    (setq sketch-layers-list (append
+                              sketch-layers-list
+                              (list (sketch-group
+                                     (format "layer-%s" new-layer)))))
+    (setq sketch-active-layer new-layer)
+    (setq show-layers (append show-layers (list new-layer)))
+    (message "Existing layers (indices): %s"
+             (mapconcat #'number-to-string
+                        (number-sequence 0 (1- (length sketch-layers-list)))
+                        ", "))))
 
-(cl-defmethod transient-infix-set ((obj sketch-variable:choices) value)
-  ;; (let ((variable (oref obj variable)))
-  (oset obj value value)
-  (setq sketch-show-labels value)
-  ;; (auto-revert-buffers)
-  (transient--redisplay)
-  (sketch-redraw))
-;; (unless (or value transient--prefix)
-;;   (message "Unset %s" variable)))
-
-(transient-define-infix sketch-cycle-labels ()
-  :description "Show labels"
-  :class 'sketch-variable:choices
-  ;; :variable "sketch-show-labels"
-  :variable 'sketch-show-labels
-  :argument "--labels="
-  :choices '("layer" "all")
-  :default "nil")
 
 (defun sketch-labels ()
+  "Create svg-group with svg text nodes for all elements in layer.
+If value of variable ‘sketch-show-labels' is ‘layer', create ..."
   (interactive)
   (let ((nodes (pcase sketch-show-labels
                  ("layer" (dom-children (nth sketch-active-layer 
sketch-layers-list)))
@@ -684,94 +195,85 @@ else return nil"
                                                   (dom-children (nth l 
sketch-layers-list)))
                                                 show-layers)))))
         (svg-labels (sketch-group "labels")))
+
+    (defun sketch-label-text-node (node x y &rest props)
+      (apply #'svg-text
+             svg-labels
+             (dom-attr node 'id)
+             (append (list :x x
+                           :y y
+                           :font-size sketch-label-size
+                           :stroke "red"
+                           :fill "red")
+                     (when-let (x (dom-attr node 'transform))
+                       (list :transform x))
+                     props)))
+
     (mapc (lambda (node)
             (pcase (dom-tag node)
-              ('rect (svg-text svg-labels
-                               (dom-attr node 'id)
-                               :x (+ (dom-attr node 'x) 2)
-                               :y (+ (dom-attr node 'y)
-                                     (- (dom-attr node 'height) 2))
-                               :font-size sketch-label-size
-                               :stroke "red"
-                               :fill "red"))
-              ('line (svg-text svg-labels
-                               (dom-attr node 'id)
-                               :x (dom-attr node 'x1)
-                               :y (dom-attr node 'y1)
-                               :font-size sketch-label-size
-                               :stroke "red"
-                               :fill "red"))
-              ((or 'circle 'ellipse) (svg-text svg-labels
-                                               (dom-attr node 'id)
-                                               :x (dom-attr node 'cx)
-                                               :y (dom-attr node 'cy)
-                                               :font-size sketch-label-size
-                                               :stroke "red"
-                                               :fill "red"))
-              ((or 'polyline 'polygon) (let ((coords (split-string
-                                                      (car (split-string 
(dom-attr node 'points) ","))
-                                                      nil
-                                                      t)))
-                                         (svg-text svg-labels
-                                                   (dom-attr node 'id)
-                                                   :x (string-to-number (car 
coords))
-                                                   :y (string-to-number (cadr 
coords))
-                                                   :font-size sketch-label-size
-                                                   :stroke "red"
-                                                   :fill "red")))
-              ('text (svg-text svg-labels
-                               (dom-attr node 'id)
-                               :x (dom-attr node 'x)
-                               :y (+ (dom-attr node 'y)
-                                     sketch-label-size)
-                               :font-size sketch-label-size
-                               :stroke "red"
-                               :fill "red"))
+              ('rect (sketch-label-text-node
+                      node
+                      (+ (dom-attr node 'x) 2)
+                      (+ (dom-attr node 'y)
+                         (- (dom-attr node 'height) 2))))
+              ('line (sketch-label-text-node
+                      node
+                      (dom-attr node 'x1)
+                      (dom-attr node 'y1)))
+              ((or 'circle 'ellipse)
+               (sketch-label-text-node
+                node
+                (dom-attr node 'cx)
+                (dom-attr node 'cy)))
+              ((or 'polyline 'polygon)
+               (let ((coords (split-string
+                              (car (split-string (dom-attr node 'points) ","))
+                              nil
+                              t)))
+                 (sketch-label-text-node
+                  node
+                  (string-to-number (car coords))
+                  (string-to-number (cadr coords)))))
+              ('text (sketch-label-text-node
+                      node
+                      (dom-attr node 'x)
+                      (+ (dom-attr node 'y)
+                         sketch-label-size)))
               ('g (let ((s (dom-attr node
                                      'transform)))
                     (string-match "translate\(\\([0-9]*\\)[, ]*\\([0-9]*\\)" s)
                     (let ((x (match-string 1 s))
                           (y (match-string 2 s)))
-                      (svg-text svg-labels
-                                (dom-attr node 'id)
-                                :x x
-                                :y y
-                                :font-size sketch-label-size
-                                :stroke "red"
-                                :fill "red"))))))
+                      (sketch-label-text-node
+                       node
+                       x
+                       y))))))
           nodes)
     svg-labels))
 
+
 (defun sketch-labels-list ()
-  (apply #'append (mapcar (lambda (l)
-                            (mapcar (lambda (node)
-                                      (dom-attr node 'id))
-                                    (dom-children (nth l sketch-layers-list))))
-                          show-layers)))
-
-;; (defun sketch-create-label (type)
-;;   (interactive)
-;;   (let* ((alphabet "abcdefghijklmnopqrstuvwxyz")
-;;          (labels-list (mapcar #'string (concat alphabet (upcase alphabet))))
-;;          (labels (sketch-labels-list)))
-;;     (while (member (car labels-list) labels)
-;;       (setq labels-list (cdr labels-list)))
-;;     (car labels-list)))
+  (apply #'append
+         (mapcar (lambda (l)
+                   (mapcar (lambda (node)
+                             (dom-attr node 'id))
+                           (dom-children (nth l sketch-layers-list))))
+                 show-layers)))
 
 (defun sketch-create-label (type)
   (interactive)
   (let* ((prefix (concat (when (/= sketch-active-layer 0)
                            (number-to-string sketch-active-layer))
                          (pcase type
-                           ("line" "l")
-                           ("rectangle" "r")
-                           ("circle" "c")
-                           ("ellipse" "e")
-                           ("polyline" "p")
-                           ("polygon" "g")
-                           ("freehand" "f")
-                           ("text" "t")
-                           ("group" "g"))))
+                           ('line "l")
+                           ('rectangle "r")
+                           ('circle "c")
+                           ('ellipse "e")
+                           ('polyline "p")
+                           ('polygon "g")
+                           ('freehand "f")
+                           ('text "t")
+                           ('group "g"))))
          (idx 0)
          (label (concat prefix (number-to-string idx)))
          (labels (sketch-labels-list)))
@@ -780,315 +282,618 @@ else return nil"
       (setq label (concat prefix (number-to-string idx))))
     label))
 
-(transient-define-infix sketch-layer ()
-  "Layer that is currently active when sketching."
-  :description "Active layer"
-  :class 'transient-lisp-variable
-  :variable 'sketch-active-layer)
-
-(defun sketch-list-layers ()
-  (mapcar #'number-to-string (number-sequence 0 (length sketch-layers-list))))
-;; (with-current-buffer (get-buffer "*sketch*")
-;;   (mapcar (lambda (layer) (alist-get 'id (cadr layer))) 
sketch-layers-list)))
-
-;; (defun sketch-translate-node-coords (node amount &rest args)
-;;   (dolist (coord args node)
-;;     (cl-decf (alist-get coord (cadr node)) amount)))
-
-(defun sketch--svg-translate (dx dy &optional object-def)
-  (interactive)
-  (let ((transform (sketch-parse-transform-value
-                    (dom-attr object-def
-                              'transform))))
-    (cl-decf (cl-first (alist-get 'translate transform)) dx)
-    (cl-decf (cl-second (alist-get 'translate transform)) dy)
-    (dom-set-attribute object-def
-                       'transform
-                       (sketch-format-transfrom-value transform))))
-
-;; (mapcar (lambda (node)
-;;           (pcase (dom-tag node)
-;;             ('line (sketch-translate-node-coords node dx 'x1 'x2)
-;;                    (sketch-translate-node-coords node dy 'y1 'y2))
-;;             ('rect (sketch-translate-node-coords node dx 'x)
-;;                    (sketch-translate-node-coords node dy 'y))
-;;             ((or 'circle 'ellipse)
-;;              (sketch-translate-node-coords node dx 'cx)
-;;              (sketch-translate-node-coords node dy 'cy))
-;;             ('text (sketch-translate-node-coords node dx 'x)
-;;                    (sketch-translate-node-coords node dy 'y))))
-;;         (cddr (nth sketch-active-layer sketch-layers-list))))
-;; (let ((node (car (dom-by-id svg-sketch label))))
-;;   (pcase (car node)
-;;     ('g (setf (alist-get 'transform (cadr node))
-;;               (format "translate(%s %s)" (- dx) (- dy))))
-;;     ;; ('line (sketch-translate-node-coords node dx 'x1 'x2)
-;;     ;;        (sketch-translate-node-coords node dy 'y1 'y2))
-;;     ;; ('rect (sketch-translate-node-coords node dx 'x)
-;;     ;;        (sketch-translate-node-coords node dy 'y))
-;;     ;; ((or 'circle 'ellipse)
-;;     ;;  (sketch-translate-node-coords node dx 'cx)
-;;     ;;  (sketch-translate-node-coords node dy 'cy))
-;;     ;; ('text (sketch-translate-node-coords node dx 'x)
-;;     ;;        (sketch-translate-node-coords node dy 'y)))
-
-;;     ) ;; TODO make it work for all types of elements
-;;   node))
-
-(defun sketch-redraw (&optional lisp lisp-buffer)
-  (unless sketch-mode
-    (user-error "Not in sketch-mode buffer"))
-  (save-current-buffer
-    (when lisp-buffer
-      (sketch-update-lisp-window lisp lisp-buffer))
-    ;; (let ((lisp-window (or (get-buffer-window "*sketch-root*")
-    ;;                        (get-buffer-window lisp-buffer))))
-    ;;   (unless (string= (buffer-name (window-buffer lisp-window)) "*sketch*")
-    ;;     (if-let (buf (get-buffer"*sketch-root*"))
-    ;;         (sketch-update-lisp-window sketch-root buf)
-    ;;       (sketch-update-lisp-window lisp lisp-buffer))))
-    (setq sketch-root (append (cl-subseq sketch-root 0 2) (list (nth (car 
show-layers) sketch-layers-list))))
-    (dolist (layer (cdr show-layers))
-      (setq sketch-root (append sketch-root (list (nth layer 
sketch-layers-list)))))
-    (setq sketch-svg (append svg-canvas
-                             (when sketch-show-grid (list sketch-grid))
-                             (when sketch-show-labels (list (sketch-labels)))
-                             (list sketch-root)))
-    (erase-buffer) ;; a (not exact) alternative is to use (kill-backward-chars 
1)
-    (sketch-insert-image sketch-svg
-                         (prin1-to-string sketch-root)
-                                :pointer 'arrow
-                                :grid-param sketch-grid-param
-                                :map `(((rect . ((0 . 0) . (,(dom-attr 
sketch-svg 'width) . ,(dom-attr sketch-svg 'height))))
-                                        ;; :map '(((rect . ((0 . 0) . (800 . 
600)))
-                                        sketch
-                                        (pointer arrow help-echo (lambda (_ _ 
pos)
-                                                                   (let (
-                                                                         ;; 
(message-log-max nil)
-                                                                         
(coords (cdr (mouse-pixel-position))))
-                                                                     (setq 
sketch-cursor-position (format "(%s, %s)"
-                                                                               
                           (- (car coords) sketch-im-x-offset)
-                                                                               
                           (+ (cdr coords) sketch-im-y-offset))))
-                                                                               
                                   (force-mode-line-update))))))
-    (beginning-of-line)))
-
-(defun sketch-update (&optional lisp lisp-buffer)
-  (unless sketch-mode
-    (user-error "Not in sketch-mode buffer"))
-  (save-current-buffer
-    (when lisp-buffer
-      (sketch-update-lisp-window lisp lisp-buffer))
-    ;; (let ((lisp-window (or (get-buffer-window "*sketch-root*")
-    ;;                        (get-buffer-window lisp-buffer))))
-    ;;   (unless (string= (buffer-name (window-buffer lisp-window)) "*sketch*")
-    ;;     (if-let (buf (get-buffer"*sketch-root*"))
-    ;;         (sketch-update-lisp-window sketch-root buf)
-    ;;       (sketch-update-lisp-window lisp lisp-buffer))))
-    (setq sketch-root (append (cl-subseq sketch-root 0 2) (list (nth (car 
show-layers) sketch-layers-list))))
-    (dolist (layer (cdr show-layers))
-      (setq sketch-root (append sketch-root (list (nth layer 
sketch-layers-list)))))
-    (setq sketch-svg (append svg-canvas
-                             (when sketch-show-grid (list sketch-grid))
-                             (when sketch-show-labels (list (sketch-labels)))
-                             (list sketch-root)))
-    (erase-buffer) ;; a (not exact) alternative is to use (kill-backward-chars 
1)
-    (sketch-insert-image sketch-svg
-                         nil
-                                :pointer 'arrow
-                                :grid-param sketch-grid-param
-                                :map `(((rect . ((0 . 0) . (,(dom-attr 
sketch-svg 'width) . ,(dom-attr sketch-svg 'height))))
-                                        ;; :map '(((rect . ((0 . 0) . (800 . 
600)))
-                                        sketch
-                                        (pointer arrow))))
-    (beginning-of-line)))
-
-
-(defun sketch-object-preview-update (object-type node start-coords end-coords)
+(defun sketch--create-canvas (width height)
+  (setq sketch-canvas (sketch-create width height nil nil nil :stroke 
sketch-default-stroke))
+  (apply #'svg-rectangle sketch-canvas 0 0 "100%" "100%"
+         :id "bg"
+         (when (or sketch-show-grid sketch-background)
+           (list :fill
+                 (if sketch-show-grid
+                     "url(#grid)"
+                   sketch-background)
+                 )))) ; sketch-background)
+
+(defun sketch-create-grid (&optional grid-param minor-grid-freq)
+  (setq sketch-grid-param (or grid-param sketch-grid-param))
+  (setq sketch-minor-grid-param (/ (float grid-param) (or minor-grid-freq 4)))
+  (setq sketch-grid (cons
+                     ;; major-grid
+                     (dom-node 'pattern
+                               `((id . "grid")
+                                 (width . ,grid-param)
+                                 (height . ,grid-param)
+                                 (patternUnits . "userSpaceOnUse"))
+                               (dom-node 'rect `((width . ,grid-param) (height 
. ,grid-param)
+                                                 (x . 0) (y . 0)
+                                                 (stroke-width . 0.8) (stroke 
. ,(car sketch-grid-colors))
+                                                 (fill . "url(#minorGrid)"))))
+                     ;; minor grid
+                     (dom-node 'pattern
+                               `((id . "minorGrid")
+                                 (width . ,sketch-minor-grid-param)
+                                 (height . ,sketch-minor-grid-param)
+                                 (patternUnits . "userSpaceOnUse"))
+                               (dom-node 'rect `((width . 
,sketch-minor-grid-param) (height . ,sketch-minor-grid-param)
+                                                 (x . 0) (y . 0)
+                                                 (stroke-width . 0.4) (stroke 
. ,(cdr sketch-grid-colors))
+                                                 ,(when sketch-background
+                                                    `(fill . 
,sketch-background))))))))
+
+(defun sketch-maybe-update-modeline ()
+  (when sketch-show-coords
+    (force-mode-line-update)))
+
+(defun sketch-draw-insert-image (width height)
+  (sketch-insert-image sketch-svg
+                       (prin1-to-string sketch-root)
+                       :map `(((rect . ((0 . 0) . (,(dom-attr sketch-svg 
'width) . ,(dom-attr sketch-svg 'height))))
+                               ;; :map '(((rect . ((0 . 0) . (800 . 600)))
+                               sketch
+                               ,(append '(pointer
+                                          arrow)
+                                        (when sketch-show-coords
+                                          (list 'help-echo (lambda (_ _ pos)
+                                                             (let ((coords 
(cdr (mouse-pixel-position))))
+                                                               (setq 
sketch-cursor-position
+                                                                     (format 
"(%s, %s)"
+                                                                             
(- (car coords) sketch-im-x-offset)
+                                                                             
(cdr coords))))
+                                                             ;; (+ (cdr 
coords) sketch-im-y-offset))))
+                                                             
(force-mode-line-update)))))))))
+
+(defun sketch-update-insert-image (width height)
+  (sketch-insert-image sketch-svg
+                       nil
+                       ;; :pointer 'arrow
+                       :map `(((rect . ((0 . 0) . (,(dom-attr sketch-svg 
'width) . ,(dom-attr sketch-svg 'height))))
+                               ;; :map '(((rect . ((0 . 0) . (800 . 600)))
+                               sketch
+                               (pointer arrow))))
+  (backward-char))
+
+(defun sketch-object-preview-update (object-type node start-coords end-coords 
&optional start-node)
   (pcase object-type
-    ("line"
+    ('line
      (setf (dom-attr node 'x2) (car end-coords))
      (setf (dom-attr node 'y2) (cdr end-coords)))
-    ("rectangle"
+    ('rectangle
      (setf (dom-attr node 'x) (car (sketch--rectangle-coords start-coords 
end-coords)))
      (setf (dom-attr node 'y) (cadr (sketch--rectangle-coords start-coords 
end-coords)))
      (setf (dom-attr node 'width) (caddr (sketch--rectangle-coords 
start-coords end-coords)))
      (setf (dom-attr node 'height) (cadddr (sketch--rectangle-coords 
start-coords end-coords))))
-    ("circle"
+    ('circle
      (setf (dom-attr node 'r) (sketch--circle-radius start-coords end-coords)))
-    ("ellipse"
+    ('ellipse
      (setf (dom-attr node 'cx) (car (sketch--ellipse-coords start-coords 
end-coords)))
      (setf (dom-attr node 'cy) (cadr (sketch--ellipse-coords start-coords 
end-coords)))
      (setf (dom-attr node 'rx) (caddr (sketch--ellipse-coords start-coords 
end-coords)))
-     (setf (dom-attr node 'ry) (cadddr (sketch--ellipse-coords start-coords 
end-coords))))))
+     (setf (dom-attr node 'ry) (cadddr (sketch--ellipse-coords start-coords 
end-coords))))
+    ('translate
+     (message "deze %s" start-node)
+     (let ((dx (- (car end-coords) (car start-coords)))
+           (dy (- (cdr end-coords) (cdr start-coords))))
+       (sketch--svg-move dx dy node start-node)))))
+
+(defun sketch-redraw (&optional lisp lisp-buffer update)
+  ;; (unless sketch-mode
+  ;;   (user-error "Not in sketch-mode buffer"))
+  ;; (save-current-buffer
+  (when lisp-buffer
+    (sketch-update-lisp-window lisp lisp-buffer))
+  ;; (let ((lisp-window (or (get-buffer-window "*sketch-root*")
+  ;;                        (get-buffer-window lisp-buffer))))
+  ;;   (unless (string= (buffer-name (window-buffer lisp-window)) "*sketch*")
+  ;;     (if-let (buf (get-buffer"*sketch-root*"))
+  ;;         (sketch-update-lisp-window sketch-root buf)
+  ;;       (sketch-update-lisp-window lisp lisp-buffer))))
+  (setq sketch-root (append (cl-subseq sketch-root 0 2) (list (nth (car 
show-layers) sketch-layers-list))))
+  (dolist (layer (cdr show-layers))
+    (setq sketch-root (append sketch-root (list (nth layer 
sketch-layers-list)))))
+  (setq sketch-svg (append sketch-canvas
+                           (list sketch-root)
+                           (when sketch-show-labels (list (sketch-labels)))))
+  (when sketch-show-grid
+    (svg--def sketch-svg (cdr sketch-grid))
+    (svg--def sketch-svg (car sketch-grid)))
+  (with-current-buffer "*sketch*"
+    (let ((inhibit-read-only t))
+      (erase-buffer) ;; a (not exact) alternative is to use 
(kill-backward-chars 1)
+      (let ((w (car sketch-size))
+            (h (cdr sketch-size)))
+        (if update
+            (sketch-update-insert-image w h)
+          (sketch-draw-insert-image w h))
+        (goto-char (point-min))))))
+
+(defun sketch--init (width height &optional grid-param minor-grid-freq)
+  (setq sketch-grid-param (or grid-param sketch-grid-param))
+  (setq sketch-minor-grid-freq (or minor-grid-freq sketch-minor-grid-freq))
+  (let* (svg)
+    ;; (when sketch-background
+    ;; (unless (memq "none" (list sketch-start-marker sketch-mid-marker 
sketch-end-marker))
+    ;;   (svg-marker sketch-canvas "arrow" 8 8 "black" t))
+    (sketch--create-canvas width height)
+    (setq sketch-svg (seq-copy sketch-canvas))
+    (when sketch-show-grid
+      (sketch-create-grid grid-param)
+      (svg--def sketch-svg (cdr sketch-grid))
+      (svg--def sketch-svg (car sketch-grid)))
+    (setq sketch-root (sketch-group "root"))
+    (setq sketch-layers-list (list (sketch-group "layer-0")))
+    (setq show-layers '(0))
+    (sketch-draw-insert-image width height)
+    (goto-char (point-min)) ; cursor over image looks better
+    (setq sketch-im-x-offset (car (window-absolute-pixel-position)))
+    (sketch-toggle-toolbar)
+    (when (featurep 'hydra) (hydra-sketch/body))
+    (add-hook 'kill-buffer-hook 'sketch-kill-toolbar nil t)
+    (special-mode)
+    (sketch-mode)))
+;; (evil-emacs-state)))
+
+(defun sketch (arg)
+  "Initialize or switch to (new) SVG image.
+With prefix ARG, create sketch using default (customizable)
+values"
+  (interactive "P")
+  (let ((buffer (get-buffer "*sketch*")))
+    (cond (buffer
+           (switch-to-buffer buffer)
+           (sketch-toggle-toolbar)
+           (when (featurep 'hydra) (hydra-sketch/body)))
+          (t
+           (let ((call-buffer (current-buffer))
+                 (width (if arg (car sketch-size) (read-number "Enter width: 
") ))
+                 (height (if arg (cdr sketch-size) (read-number "Enter height: 
"))))
+             (switch-to-buffer (get-buffer-create "*sketch*"))
+             (setq sketch-action 'line)
+             (setq sketch-grid-param (if arg 50 (read-number "Enter grid 
parameter (enter 0 for no grid): ")))
+             (sketch--init width height sketch-grid-param)
+             (when sketch-show-coords
+               (setq sketch-coordless-mode-line-format mode-line-format)
+               (add-to-list 'mode-line-format '(:eval sketch-cursor-position) 
t))
+             (setq sketch-call-buffer call-buffer)))))) ;; variable is buffer 
local))
+
+(define-key image-map "o" nil)
+
+(define-minor-mode sketch-mode
+  "Create svg images using the mouse.
+In sketch-mode buffer press \\[sketch-transient] to activate the
+transient."
+  :lighter "sketch-mode"
+  :keymap
+  `(
+    ([sketch down-mouse-1] . sketch-interactively)
+    ([sketch mouse-3] . sketch-text-interactively)
+    ([sketch C-S-drag-mouse-1] . sketch-crop)
+    ;; ([sketch S-down-mouse-1] . sketch-select)
+    ("a" . sketch-set-action)
+    ("c" . sketch-set-colors)
+    ("w" . sketch-set-width)
+    ("sd" . sketch-set-dasharray)
+    ("fw" . sketch-set-font-with-keyboard)
+    ("fs" . sketch-set-font-size)
+    ("v" . sketch-keyboard-select)
+    ("m" . sketch-modify-object)
+    ("d" . sketch-remove-object)
+    ("tg" . sketch-toggle-grid)
+    ("ts" . sketch-toggle-snap)
+    ("l" . sketch-cycle-labels)
+    ("D" . sketch-show-definition)
+    ("u" . sketch-undo)
+    ("U" . sketch-redo)
+    ("S" . image-save)
+    ("tt" . sketch-toggle-toolbar)
+    ("tc" . sketch-toggle-coords)
+    ("?" . sketch-help)
+    (,(kbd "C-c C-c") . sketch-quick-insert-image))
+  ;; (,(kbd "C-c C-s") . sketch-transient))
+  (when (boundp 'spacemacs-version)
+    (evil-emacs-state)
+    ;; (evil-local-set-key 'motion "g" 'sketch-toggle-grid)
+    ;; (evil-local-set-key 'motion "l" 'sketch-cycle-labels)
+    ;; (evil-local-set-key 'motion "?" 'sketch-help)
+    ;; (evil-local-set-key 'motion "tg" 'sketch-toggle-grid)
+    ;; (evil-local-set-key 'motion "ts" 'sketch-toggle-snap)
+    ;; (evil-local-set-key 'motion "tt" 'sketch-toggle-toolbar)
+    ;; (evil-local-set-key 'motion "tc" 'sketch-toggle-coords)
+    ;; (evil-local-set-key 'motion "fw" 'sketch-set-font-with-keyboard)
+    ;; (evil-local-set-key 'motion "fs" 'sketch-set-font-size)
+    )
+  (if (boundp 'undo-tree-mode)
+      (undo-tree-mode)
+    (buffer-enable-undo))
+  (setq-local global-hl-line-mode nil)
+  (blink-cursor-mode 0))
+
+(when (featurep 'hydra)
+    (defhydra hydra-sketch (:hint nil :foreign-keys run)
+   "
+^Stroke/Fill^     ^Font^            ^Edit^              ^Toggle^     
+^^^^^^^^-------------------------------------------------------------
+_a_ : action      _fw_: font        _v_  : select       _tg_: grid
+_c_ : color       _fs_: sont-size   _m_  : modify       _ts_: snap
+_w_ : width       ^ ^               _d_  : delete       _tt_: toolbar
+_sd_: dasharray   ^ ^               _u_/_U_: undo/redo    _tc_: coords
+"
+   ("a" sketch-set-action)
+   ("c" sketch-set-colors)
+   ("w" sketch-set-width)
+   ("sd" sketch-set-dasharray)
+   ("fw" sketch-set-font-with-keyboard)
+   ("fs" sketch-set-font-size)
+   ("v" sketch-keyboard-select)
+   ("m" sketch-modify-object)
+   ("d" sketch-remove-object)
+   ("tg" sketch-toggle-grid)
+   ("ts" sketch-toggle-snap)
+   ("l" sketch-cycle-labels)
+   ("D" sketch-show-definition)
+   ("u" sketch-undo)
+   ("U" sketch-redo)
+   ("S" image-save)
+   ("tt" sketch-toggle-toolbar)
+   ("tc" sketch-toggle-coords)
+   ("?" sketch-help "help" :color blue)
+   ("." nil  "exit hydra" :color blue :exit t)
+   ("q" sketch-quit-window "quit-restore" :exit t)
+   ("Q" sketch-quit "quit" :exit t)))
+
+
+(defun sketch-hydra ()
+  (interactive)
+  (if (featurep 'hydra)
+      (hydra-sketch/body)
+    (user-error "This feature requires the hydra package to be installed")))
+
+(define-key sketch-mode-map "." 'sketch-hydra)
+
+(defun sketch-quit-window ()
+  "Quit sketch window. The window can be restores with ‘M-x sketch'"
+  (interactive)
+  (when (get-buffer "*sketch-toolbar*")
+    (kill-buffer "*sketch-toolbar*"))
+  (quit-window))
+
+(defun sketch-quit ()
+  "Quit sketch-mode and kill buffers."
+  (interactive)
+  (when (get-buffer "*sketch-toolbar*")
+    (kill-buffer "*sketch-toolbar*"))
+  (kill-buffer "*sketch*"))
+
+;;; Actions
+(defvar sketch-action 'line)
+(defvar sketch-stroke-color "Black")
+(defvar sketch-fill-color "none")
+(defvar sketch-stroke-width 1)
+(defvar sketch-stroke-dasharray nil)
+
+(defvar sketch-font nil)
+
+
+(defun sketch-norm (vec)
+  "Return norm of a vector (list of numbers).
+VEC should be a cons or a list containing only number elements."
+  (let ((sum-of-squares (apply #'+
+                               (mapcar (lambda (x) (expt x 2))
+                                       vec))))
+    (expt sum-of-squares 0.5)))
+
+(defun sketch--circle-radius (start-coords end-coords)
+  (sketch-norm
+   (list (- (car end-coords) (car start-coords))
+         (- (cdr end-coords) (cdr start-coords)))))
+
+(defun sketch--rectangle-coords (start-coords end-coords)
+  (let ((base-coords (cons (apply #'min (list (car start-coords) (car 
end-coords)))
+                           (apply #'min (list (cdr start-coords) (cdr 
end-coords))))))
+    (list (car base-coords)
+          (cdr base-coords)
+          (abs (- (car end-coords) (car start-coords)))
+          (abs (- (cdr end-coords) (cdr start-coords))))))
+
+(defun sketch--ellipse-coords (start-coords end-coords)
+  (list (/ (+ (car start-coords) (car end-coords)) 2)
+        (/ (+ (cdr start-coords) (cdr end-coords)) 2)
+        (abs (/ (- (car end-coords) (car start-coords)) 2))
+        (abs (/ (- (cdr end-coords) (cdr start-coords)) 2))))
 
-(defun sketch-interactively-1 (event)
+(defun sketch--snap-to-grid (coord grid-param)
+  (cons (* (round (/ (float (car coord)) grid-param)) grid-param)
+        (* (round (/ (float (cdr coord)) grid-param)) grid-param)))
+
+(defun sketch-interactively (event)
+  "Draw objects interactively via a mouse drag EVENT. "
   (interactive "@e")
-  (let* ((args (when transient-current-prefix (transient-args 
'sketch-transient)))
-         (start (event-start event))
-         (grid-param (plist-get (cdr (posn-image start)) :grid-param))
-         (snap (transient-arg-value "--snap-to-grid=" args))
-         (start-coords (if (or (not snap) (string= snap "nil"))
-                           (posn-object-x-y start)
-                         (sketch--snap-to-grid (posn-object-x-y start) 
grid-param)))
+  (let* ((start (event-start event))
+         (start-coords (if sketch-snap-to-grid
+                           (sketch--snap-to-grid (posn-object-x-y start) 
sketch-minor-grid-param)
+                         (posn-object-x-y start)))
          (points (list (cons (car start-coords) (cdr start-coords)))) ;; list 
of point needed for polyline/gon
-         (object-props (list :stroke-width
-                             (transient-arg-value "--stroke-width=" args)
-                             :stroke
-                             (transient-arg-value "--stroke-color=" args)
-                             :fill
-                             (transient-arg-value "--fill-color=" args)
-                             :stroke-dasharray
-                             (transient-arg-value "--stroke-dasharray=" args)
-                             :marker-end (if args (pcase (transient-arg-value 
"--marker=" args)
-                                                    ("arrow" "url(#arrow)")
-                                                    ("dot" "url(#dot)")
-                                                    (_ "none"))
-                                           (if sketch-include-end-marker
-                                               "url(#arrow)"
-                                             "none"))))
-         (object-type (transient-arg-value "--object=" args))
-         (start-command-and-coords (pcase object-type
-                                     ("line" (list 'svg-line
-                                                   (car start-coords) (cdr 
start-coords)
-                                                   (car start-coords) (cdr 
start-coords)))
-                                     ("rectangle" `(svg-rectangle
-                                                    
,@(sketch--rectangle-coords start-coords start-coords)))
-                                     ("circle" (list 'svg-circle
-                                                     (car start-coords) (cdr 
start-coords)
-                                                     (sketch--circle-radius 
start-coords start-coords)))
-                                     ("ellipse" `(svg-ellipse 
,@(sketch--ellipse-coords start-coords start-coords)))
-                                     (var (list (pcase var 
-                                                  ((or "polyline" "freehand") 
'svg-polyline)
-                                                  ("polygon" 'svg-polygon))
+         (object-props (if (eq sketch-action 'text)
+                           (append (list :font-size sketch-font-size
+                                         :font-weight sketch-font-weight)
+                                   (when sketch-font
+                                     (list :font-family sketch-font))
+                                   (when sketch-stroke-color
+                                     (list :stroke sketch-stroke-color))
+                                   (when sketch-fill-color
+                                     (list :fill sketch-fill-color)))
+                         (list :stroke-width
+                               sketch-stroke-width
+                               :stroke
+                               sketch-stroke-color
+                               :fill
+                               sketch-fill-color
+                               :stroke-dasharray
+                               sketch-stroke-dasharray
+                               ;; :marker-end (if args (pcase 
(transient-arg-value "--marker=" args)
+                               ;;                        ("arrow" 
"url(#arrow)")
+                               ;;                        ("dot" "url(#dot)")
+                               ;;                        (_ "none"))
+                               ;;               (if sketch-include-end-marker
+                               ;;                   "url(#arrow)"
+                               ;;                 "none"))
+                               )))
+         (start-command-and-coords (pcase sketch-action
+                                     ('line (list 'svg-line
+                                                  (car start-coords) (cdr 
start-coords)
+                                                  (car start-coords) (cdr 
start-coords)))
+                                     ('rectangle `(svg-rectangle
+                                                   ,@(sketch--rectangle-coords 
start-coords start-coords)))
+                                     ('circle (list 'svg-circle
+                                                    (car start-coords) (cdr 
start-coords)
+                                                    (sketch--circle-radius 
start-coords start-coords)))
+                                     ('ellipse `(svg-ellipse 
,@(sketch--ellipse-coords start-coords start-coords)))
+                                     (var (list (pcase var
+                                                  ((or 'polyline 'freehand) 
'svg-polyline)
+                                                  ('polygon 'svg-polygon))
                                                 points))))
-         (label (sketch-create-label object-type)))
-    (apply (car start-command-and-coords)
-           (nth sketch-active-layer sketch-layers-list)
-           `(,@(cdr start-command-and-coords) ,@object-props :id ,label))
-    (let ((node (car (dom-by-id (nth sketch-active-layer sketch-layers-list) 
label))))
-      (cond ((member object-type '("line" "rectangle" "circle" "ellipse"))
+         (label (unless (memq sketch-action '(move translate))
+                  (sketch-create-label sketch-action))))
+    (if (eq sketch-action 'text)
+        (let ((text (read-string "Enter text: ")))
+          (apply #'svg-text
+                 (nth sketch-active-layer sketch-layers-list)
+                 text
+                 :x (car start-coords)  (cdr start-coords)
+                 :id label object-props))
+      (unless (memq sketch-action '(move translate))
+        (apply (car start-command-and-coords)
+               (nth sketch-active-layer sketch-layers-list)
+               `(,@(cdr start-command-and-coords) ,@object-props :id ,label)))
+      (let* ((node (car (dom-by-id (nth sketch-active-layer sketch-layers-list)
+                                   (if (memq sketch-action '(move translate))
+                                       (car sketch-selection)
+                                     label))))
+             (translate-start (dom-attr node 'transform)))
         (track-mouse
-          (while (not (eq (car event) 'drag-mouse-1))
-            (setq event (read-event))
-            (let* ((end (event-start event))
-                   (end-coords (if (or (not snap) (string= snap "nil"))
-                                   (posn-object-x-y end)
-                                 (sketch--snap-to-grid (posn-object-x-y end) 
grid-param))))
-              (sketch-object-preview-update object-type node start-coords 
end-coords)
-              (sketch-update)
-              (setq sketch-cursor-position (format "(%s, %s)"
-                                                   (car end-coords)
-                                                   (cdr end-coords)))
-                         (force-mode-line-update))))
-        ;; (sketch-possibly-update-image sketch-svg)))
-        (let* ((end (event-end event))
-               (end-coords (if (or (not snap) (string= snap "nil"))
-                               (posn-object-x-y end)
-                             (sketch--snap-to-grid (posn-object-x-y end) 
grid-param))))
-          (sketch-object-preview-update object-type node start-coords 
end-coords)))
-            ((member object-type '("polyline" "polygon"))
-             (track-mouse
-               (while (not (eq (car event) 'double-mouse-1))
-                 (setq event (read-event))
-                 (let* ((end (event-start event))
-                        (end-coords (if (or (not snap) (string= snap "nil"))
-                                        (posn-object-x-y end)
-                                      (sketch--snap-to-grid (posn-object-x-y 
end) grid-param))))
+          (cond ((member sketch-action '(line rectangle circle ellipse move 
translate))
+                 (let ((event (read-event)))
+                   (while (not (memq (car event) '(mouse-1 drag-mouse-1)))
+                     (let* ((end (event-start event))
+                            (end-coords (if sketch-snap-to-grid
+                                            (sketch--snap-to-grid 
(posn-object-x-y end) sketch-minor-grid-param)
+                                          (posn-object-x-y end))))
+                       (sketch-object-preview-update sketch-action
+                                                     node
+                                                     start-coords
+                                                     end-coords
+                                                     (when (eq sketch-action 
'translate)
+                                                       translate-start))
+                       (sketch-redraw nil nil t)
+                       (when (and sketch-lisp-buffer-name (buffer-live-p 
(get-buffer sketch-lisp-buffer-name)))
+                         (sketch-update-lisp-window node 
sketch-lisp-buffer-name))
+                       (setq event (read-event))
+                       (when sketch-show-coords
+                         (setq sketch-cursor-position (format "(%s, %s)"
+                                                              (car end-coords)
+                                                              (cdr 
end-coords))))
+                       (sketch-maybe-update-modeline)
+                       ))
+                   (let* ((end (event-end event))
+                          (end-coords (if sketch-snap-to-grid
+                                          (sketch--snap-to-grid 
(posn-object-x-y end) sketch-minor-grid-param)
+                                        (posn-object-x-y end))))
+                     (if (and (equal (car start-coords) (car end-coords))
+                              (equal (cdr start-coords) (cdr end-coords)))
+                         (dom-remove-node (nth sketch-active-layer 
sketch-layers-list) node)
+                       (sketch-object-preview-update sketch-action
+                                                     node
+                                                     start-coords
+                                                     end-coords
+                                                     (when (eq sketch-action 
'translate)
+                                                       translate-start))
+                       (sketch-redraw nil nil t)
+                       (when (and sketch-lisp-buffer-name (buffer-live-p 
(get-buffer sketch-lisp-buffer-name)))
+                         (sketch-update-lisp-window node 
sketch-lisp-buffer-name))
+                       ))))
+
+
+                ((member sketch-action '(polyline polygon))
+                 (while (not (eq (car event) 'double-mouse-1))
+                   (setq event (read-event))
+                   (let* ((end (event-start event))
+                          (end-coords (if sketch-snap-to-grid
+                                          (sketch--snap-to-grid 
(posn-object-x-y end) sketch-minor-grid-param)
+                                        (posn-object-x-y end))))
+                     (let (message-log-max)
+                       (message "Press double click finish by inserting a 
final node"))
+                     (setf (dom-attr node 'points) (mapconcat (lambda (pair)
+                                                                (format "%s 
%s" (car pair) (cdr pair)))
+                                                              (reverse
+                                                               (if (eq (car 
event) 'down-mouse-1)
+                                                                   (push 
end-coords points)
+                                                                 (cons 
end-coords points)))
+                                                              ", "))
+                     (sketch-redraw nil nil t)
+                     (setq sketch-cursor-position (format "(%s, %s)"
+                                                          (car end-coords)
+                                                          (cdr end-coords)))
+                     (sketch-maybe-update-modeline)))
+                 (let* ((end (event-end event))
+                        (end-coords (if sketch-snap-to-grid
+                                        (sketch--snap-to-grid (posn-object-x-y 
end) sketch-minor-grid-param)
+                                      (posn-object-x-y end))))
                    (setf (dom-attr node 'points) (mapconcat (lambda (pair)
-                                                                               
(format "%s %s" (car pair) (cdr pair)))
-                                                                             
(reverse 
+                                                              (format "%s %s" 
(car pair) (cdr pair)))
+                                                            (reverse
                                                              (if (eq (car 
event) 'down-mouse-1)
                                                                  (push 
end-coords points)
                                                                (cons 
end-coords points)))
-                                                                             
", "))
-                   (sketch-update)
-                   (setq sketch-cursor-position (format "(%s, %s)"
-                                                        (car end-coords)
-                                                        (cdr end-coords)))
-                              (force-mode-line-update)))
-               ;; (sketch-possibly-update-image sketch-svg)))
-               (let* ((end (event-end event))
-                      (end-coords (if (or (not snap) (string= snap "nil"))
-                                      (posn-object-x-y end)
-                                    (sketch--snap-to-grid (posn-object-x-y 
end) grid-param))))
-                 (setf (dom-attr node 'points) (mapconcat (lambda (pair)
-                                                                             
(format "%s %s" (car pair) (cdr pair)))
-                                                                           
(reverse 
-                                                           (if (eq (car event) 
'down-mouse-1)
-                                                               (push 
end-coords points)
-                                                             (cons end-coords 
points)))
-                                                                           ", 
")))))
-            ((string= object-type "freehand")
-             (track-mouse
-               (while (not (eq (car event) 'drag-mouse-1))
-                 (setq event (read-event))
-                 (let* ((end (if (eq (car event) 'drag-mouse-1)
-                                 (event-end event)
-                               (event-start event)))
-                        (end-coords (if (or (not snap) (string= snap "nil"))
-                                        (posn-object-x-y end)
-                                      (sketch--snap-to-grid (posn-object-x-y 
end) grid-param))))
-                   (setf (dom-attr node 'points) (mapconcat (lambda (pair)
-                                                                               
(format "%s %s" (car pair) (cdr pair)))
-                                                                             
(reverse (cl-pushnew end-coords points))
-                                                                             
", "))
-                   (sketch-update)
-                   (setq sketch-cursor-position (format "(%s, %s)"
-                                                        (car end-coords)
-                                                        (cdr end-coords)))
-                              (force-mode-line-update))))))
-               ;; (sketch-possibly-update-image sketch-svg)))
-        ;; (sketch-possibly-update-image sketch-svg
-        ;;                               :pointer 'arrow
-        ;;                               :map `(((rect . ((0 . 0) . 
(,(dom-attr sketch-svg 'width) . ,(dom-attr sketch-svg 'height))))
-        ;;                                       ;; :map '(((rect . ((0 . 0) . 
(800 . 600)))
-        ;;                                       sketch
-        ;;                                       (pointer arrow))))
-      (when-let (buf (get-buffer "*sketch-root*"))
-        (sketch-update-lisp-window sketch-root buf))
-        (sketch-redraw))))
-
-(transient-define-suffix sketch-remove-object ()
-  (interactive)
-  (svg-remove sketch-root (completing-read "Remove element with id: "
-                                           (sketch-labels-list)))
-  (sketch-redraw))
+                                                            ", "))))
+
+
+                ((string= sketch-action 'freehand)
+                 (while (not (memq (car event) '(mouse-1 drag-mouse-1)))
+                   (setq event (read-event))
+                   (let* ((end (if (eq (car event) 'drag-mouse-1)
+                                   (event-end event)
+                                 (event-start event)))
+                          (end-coords (if sketch-snap-to-grid
+                                          (sketch--snap-to-grid 
(posn-object-x-y end) sketch-minor-grid-param)
+                                        (posn-object-x-y end))))
+                     (setf (dom-attr node 'points) (mapconcat (lambda (pair)
+                                                                (format "%s 
%s" (car pair) (cdr pair)))
+                                                              (reverse 
(cl-pushnew end-coords points))
+                                                              ", "))
+                     (sketch-redraw nil nil t)
+                     (setq sketch-cursor-position (format "(%s, %s)"
+                                                          (car end-coords)
+                                                          (cdr end-coords)))
+                     (sketch-maybe-update-modeline))))))))
+    (when-let (buf (get-buffer "*sketch-root*"))
+      (sketch-update-lisp-window sketch-root buf))
+    (sketch-redraw)))
 
-(transient-define-suffix sketch-insert-snippet (event)
+(defvar sketch-font-size 20)
+(defvar sketch-font-weight "normal")
+
+(defun sketch-text-interactively (event)
   (interactive "@e")
-  (let ((coords (posn-object-x-y (event-start event)))
-        (node (oref transient-current-prefix value))
-        (label (sketch-create-label "group")))
-    (dom-set-attribute node
+  (let* ((start (event-start event))
+         (coords (if sketch-snap-to-grid
+                     (posn-object-x-y start)
+                   (sketch--snap-to-grid (posn-object-x-y start) 
sketch-grid-param)))
+         (text (read-string "Enter text: "))
+         (object-props (append (list :font-size sketch-font-size
+                                     :font-weight sketch-font-weight)
+                               (when sketch-font
+                                 (list :font-family sketch-font))
+                               (when sketch-stroke-color
+                                 (list :stroke sketch-stroke-color))
+                               (when sketch-fill-color
+                                 (list :fill sketch-fill-color)))))
+    ;; :fill
+    ;; (transient-arg-value "--fill-color=" sketch-args)
+    ;; :marker-end (if sketch-args (pcase (transient-arg-value "--marker=" 
sketch-args)
+    ;;                        ("arrow" "url(#arrow)")
+    ;;                        ("dot" "url(#dot)")
+    ;;                        (_ "none"))
+    ;;               (if sketch-include-end-marker
+    ;;                   "url(#arrow)"
+    ;;                 "none"))))
+    (apply #'svg-text
+           (nth sketch-active-layer sketch-layers-list)
+           text
+           :x (car coords)  (cdr coords)
+           :id (sketch-create-label 'text) object-props))
+  (sketch-redraw))
+
+;;; Modify object-label
+
+(defvar sketch-selection nil)
+
+(defun sketch-keyboard-select (&optional arg)
+  "Select labels to include in selection.
+Initial input shows current selection. With prefix ARG initial
+selection shows all object in sketch."
+  (interactive "P")
+  (setq sketch-selection (completing-read-multiple "Select labels for 
selection (separated by ,): "
+                                                   (sketch-labels-list)
+                                                   nil
+                                                   t
+                                                   (mapconcat #'identity
+                                                              (if all
+                                                                  
(sketch-labels-list)
+                                                                
sketch-selection)
+                                                              ","))))
+
+
+(defun sketch-move-object (buffer object-def props coords amount)
+  (dolist (coord coords)
+    (cl-incf (alist-get coord props) amount))
+  (sketch-redraw object-def buffer))
+
+(defun sketch-parse-transform-value (value)
+  (let ((transforms (mapcar (lambda (val)
+                              (split-string val "[(,)]" t))
+                            (split-string value))))
+    (mapcar (lambda (x)
+              (cons (intern (car x)) (mapcar (lambda (val)
+                                               (string-to-number val))
+                                             (cdr x))))
+            transforms)))
+
+(defun sketch-format-transfrom-value (value)
+  (string-join (mapcar (lambda (x) (concat (symbol-name (car x))
+                                           "("
+                                           (number-to-string (cadr x))
+                                           (if-let (y (caddr x))
+                                               (concat "," (number-to-string 
y)))
+                                           ")"))
+                       value)
+               " "))
+
+(defun sketch--svg-translate (dx dy &optional object-def)
+  (interactive)
+  (let ((transform (sketch-parse-transform-value
+                    (or (dom-attr object-def 'transform)
+                        "translate(0,0)"))))
+    (cl-decf (cl-first (alist-get 'translate transform)) dx)
+    (cl-decf (cl-second (alist-get 'translate transform)) dy)
+    (dom-set-attribute object-def
                        'transform
-                       (format "translate(%s,%s)" (car coords) (cdr coords)))
-    (dom-set-attribute node
-                       'id
-                       label)
-    (dom-append-child (nth sketch-active-layer sketch-layers-list) node)
-    (sketch-redraw)
-    (sketch-modify-object label)))
-
-(transient-define-prefix sketch-import (svg-file)
-  [([sketch mouse-1] "Insert snippet"  sketch-insert-snippet)]
-  (interactive (list (let ((default-directory (concat
-                                               (file-name-directory 
(locate-library "sketch-mode"))
-                                               "snippet-files/")))
-                       (read-file-name "Import (object) from file: "))))
-  (let* ((dom (sketch-snippet-get-dom svg-file))
-         (has-groups (dom-by-tag dom 'g)))
-    (when has-groups (sketch-snippets-add-labels dom))
-    (let* ((idx (when has-groups (read-number "Number of object for import: 
")))
-           (snippet (if (dom-by-tag dom 'g)
-                        (dom-elements dom 'id (number-to-string idx))
-                      (list (dom-append-child
-                             (sketch-group (sketch-create-label "group"))
-                             (car (dom-children dom)))))))
-      (sketch-redraw)
-      (transient-setup 'sketch-import nil nil :value (car snippet)))))
-
-;; (transient-define-suffix sketch-import-object (svg-file)
-;;   (interactive "fImport object from file: ")
+                       (sketch-format-transfrom-value transform))))
+
+(defun sketch--svg-move (dx dy &optional object-def start-node)
+  (interactive)
+  (let ((transform (sketch-parse-transform-value
+                    (if start-node 
+                        start-node
+                      "translate(0,0)"))))
+
+    ;;  (or (print (dom-attr start-node 'transform))
+    ;;      "translate(0,0)"))
+    (cond
+     ;; (end
+     ;;  (cl-incf (cl-first (alist-get 'translate transform))  dx)
+     ;;  (cl-incf (cl-second (alist-get 'translate transform)) dy))
+     (t
+      (cl-incf (cl-first (alist-get 'translate transform))  dx)
+      (cl-incf (cl-second (alist-get 'translate transform)) dy)))
+    (dom-set-attribute object-def
+                       'transform
+                       (sketch-format-transfrom-value transform))
+    start-node))
+
+(defun sketch-group-scale (buffer object-def direction &optional fast)
+  (let ((transform (sketch-parse-transform-value
+                    (dom-attr object-def
+                              'transform)))
+        (amount (if fast
+                    1
+                  0.1)))
+    (unless (alist-get 'scale transform)
+      (push '(scale 1) transform))
+    (pcase direction
+      ('up (cl-incf (car (alist-get 'scale transform)) amount))
+      ('down (cl-decf (car (alist-get 'scale transform)) amount)))
+    (dom-set-attribute object-def
+                       'transform
+                       (sketch-format-transfrom-value transform))
+    (sketch-redraw object-def buffer)))
 
 (define-minor-mode sketch-lisp-mode
   "Minor mode for svg lisp buffers."
@@ -1097,31 +902,29 @@ else return nil"
   `((,(kbd "C-c C-s") . sketch-transient)
     (,(kbd "C-c C-c") . sketch-load-definition)))
 
-(transient-define-suffix sketch-show-definition ()
+(defun sketch-show-definition ()
   ;; :transient 'transient--do-exit
   (interactive)
+  (when (get-buffer "*sketch-toolbar*")
+    (kill-buffer "*sketch-toolbar*"))
   (if-let (win (get-buffer-window "*sketch-root*"))
       (delete-window win)
     (let ((buffer (get-buffer-create "*sketch-root*"))
           (sketch sketch-root))
       (set-window-dedicated-p
-       (get-buffer-window
-        (pop-to-buffer buffer
-                       '(display-buffer-in-side-window . ((side . right) 
(window-width . 70)))))
+       (get-buffer-window (pop-to-buffer
+                           buffer
+                           `(display-buffer-in-side-window
+                             . ((side . right)
+                                (window-width . ,(funcall 
sketch-side-window-max-width))))))
        t)
+      (window-resize (get-buffer-window buffer) -3 t)
       (erase-buffer)
       (with-current-buffer buffer
         (dom-pp sketch)))
     (emacs-lisp-mode)
     (sketch-lisp-mode)))
 
-(transient-define-suffix sketch-copy-definition ()
-  (interactive)
-  (with-temp-buffer
-    (dom-pp sketch-svg)
-    (kill-new (buffer-string)))
-  (message "SVG definition added to kill-ring"))
-
 (defun sketch-load-definition ()
   (interactive)
   (let ((def (read (buffer-string))))
@@ -1130,233 +933,384 @@ else return nil"
       (setq sketch-layers-list (dom-by-id sketch-root "layer"))
       (sketch-redraw))))
 
-;; (defvar sketch-undo-redo nil)
-
-(transient-define-suffix sketch-undo (&optional count)
-  (interactive "*p")
-  (cond ((fboundp 'evil-undo)
-         (evil-undo count))
-        ((fboundp 'undo-tree-undo)
-         (undo-tree-undo))
-        (t (undo)))
-  (setq sketch-svg (read (buffer-string)))
-  (setq sketch-root (car (dom-by-id sketch-svg "root")))
-  (setq sketch-layers-list (dom-elements sketch-root 'id "layer"))
-  (unless sketch-layers-list (sketch-add-layer)))
-;; (let ((sketch-reverse (nreverse sketch-root)))
-;;   (push (pop sketch-reverse) sketch-undo-redo)
-;;   (setq sketch-root (nreverse sketch-reverse)))
-;; (sketch-redraw))
-
-(transient-define-suffix sketch-redo (count)
-  (interactive "*p")
-  (cond ((fboundp 'evil-undo)
-         (evil-redo count))
-        ((fboundp 'undo-tree-redo)
-         (undo-tree-redo))
-        (t (user-error "This command requires `undo-tree' or `evil' to be 
available")))
-  (setq sketch-root (read (buffer-string)))
-  (setq sketch-layers-list (dom-elements sketch-root 'id "layer"))
-  (unless sketch-layers-list (sketch-add-layer)))
-;; (let ((sketch-reverse (nreverse sketch-root)))
-;;   (push (pop sketch-undo-redo) sketch-reverse)
-;;   (setq sketch-root (nreverse sketch-reverse)))
-;; (sketch-redraw))
+(defun sketch-modify-object (&optional group)
+  (interactive)
+  (let ((show-labels sketch-show-labels))
+    (setq sketch-show-labels "all")
+    (sketch-toolbar-refresh)
+    (sketch-redraw)
+    (let* ((object-label (if group
+                             group
+                           (completing-read "Transform element with id: "
+                                            (sketch-labels-list))))
+           (buffer (get-buffer-create (format "*sketch-object-%s*" 
object-label))))
+      (setq sketch-selection (list object-label))
+      (display-buffer
+       buffer
+       `(display-buffer-in-side-window
+         . ((side . right)
+            (window-width . ,(funcall sketch-side-window-max-width)))))
+      (window-resize (get-buffer-window buffer) -3 t)
+      (pp (cadar (dom-by-id sketch-svg (format "^%s$" object-label))) buffer)
+      (setq sketch-action 'translate)
+      (with-current-buffer buffer
+        (emacs-lisp-mode))
+      (setq sketch-lisp-buffer-name buffer))
+    (setq sketch-show-labels sketch-show-labels)
+    (sketch-toolbar-refresh)
+    (sketch-redraw)))
 
-(transient-define-suffix sketch-text-interactively (event)
-  (interactive "@e")
-  (let* ((sketch-args (when transient-current-prefix (transient-args 
'sketch-transient)))
-         (start (event-start event))
-         (grid-param (plist-get (cdr (posn-image start)) :grid-param))
-         (snap (transient-arg-value "--snap-to-grid=" sketch-args))
-         (coords (if (or (not snap) (string= snap "nil"))
-                     (posn-object-x-y start)
-                   (sketch--snap-to-grid (posn-object-x-y start) grid-param)))
-         (text (read-string "Enter text: "))
-         (object-props (list :font-size
-                             (transient-arg-value "--font-size=" sketch-args)
-                             :font-weight
-                             (transient-arg-value "--font-weight=" sketch-args)
-                             )))
-    ;; :fill
-    ;; (transient-arg-value "--fill-color=" sketch-args)
-    ;; :marker-end (if sketch-args (pcase (transient-arg-value "--marker=" 
sketch-args)
-    ;;                        ("arrow" "url(#arrow)")
-    ;;                        ("dot" "url(#dot)")
-    ;;                        (_ "none"))
-    ;;               (if sketch-include-end-marker
-    ;;                   "url(#arrow)"
-    ;;                 "none"))))
-    (apply #'svg-text (nth sketch-active-layer sketch-layers-list) text :x 
(car coords) :y (cdr coords) :id (sketch-create-label "text") object-props))
-  (sketch-redraw))
+(defun sketch-update-lisp-window (lisp buffer)
+  ;; (let ((sketch sketch-root))
+  (with-current-buffer buffer
+    (erase-buffer)
+    (pp lisp (current-buffer))
+    (goto-char (point-max)))
+  (setq sketch-lisp-buffer-name buffer))
 
-(transient-define-infix sketch-select-font ()
-  :description "Option with list"
-  :class 'transient-option
-  :argument "--family="
-  :choices (font-family-list))
-
-(transient-define-infix sketch-font-size ()
-  :description "Option with list"
-  :class 'transient-option
-  :argument "--font-size="
-  :choices (mapcar (lambda (x)
-                     (number-to-string x))
-                   (number-sequence 1 100)))
-
-(transient-define-infix sketch-font-weight ()
-  :description "Option with list"
-  :class 'sketch-variable:choices
-  :argument "--font-weight="
-  :choices '("bold")
-  :default "normal")
-
-;; (defclass sketch-variable:layers (transient-variable)
-;;   ((fallback    :initarg :fallback    :initform nil)
-;;    (default     :initarg :default     :initform nil)))
-
-;; (cl-defmethod transient-infix-read ((obj sketch-variable:layers))
-;;   (let ((value (if-let (val (oref obj value))
-;;                    val
-;;                  (oref obj default)))
-;;         (layer (read-number "Type number of layer for toggle: ")))
-;;     (if (memq layer value)
-;;         (delq layer value)
-;;       (push layer value))))
-
-;; (cl-defmethod transient-infix-value ((obj sketch-variable:layers))
-;;   (let ((default (oref obj default)))
-;;     (if-let ((value (oref obj value)))
-;;         value)
-;;     (when default
-;;       default)))
-
-;; (cl-defmethod transient-format-value ((obj sketch-variable:layers))
-;;   (let ((value (oref obj value))
-;;         (default  (oref obj default)))
-;;     (format "%s" (if value
-;;                      (oref obj value)
-;;                    (oref obj default)))))
-;; (let ((value (oref obj value))
-;;       (default  (oref obj default)))
-;;   (if value
-;;       (format "%s (%s)"
-;;               (propertize value 'face (cons 'foreground-color value))
-;;               (propertize (apply 'color-rgb-to-hex (color-name-to-rgb 
value))
-;;                           'face 'transient-inactive-argument))
-;;     (if (string= default "none")
-;;         (propertize "none" 'face 'transient-inactive-argument)
-;;       (format "%s (%s)"
-;;               (propertize default 'face (cons 'foreground-color default))
-;;               (propertize (apply 'color-rgb-to-hex (color-name-to-rgb 
default))
-;;                           'face 'transient-inactive-argument))))))
-
-(transient-define-suffix sketch-add-layer ()
+(defun sketch-remove-object ()
   (interactive)
-  (let ((new-layer (length sketch-layers-list))
-        (active-layer-infix (object-assoc "Active layer" 'description 
transient-current-suffixes))
-        (show-layers-infix (object-assoc "Show layers" 'description 
transient-current-suffixes)))
-    (setq sketch-layers-list (append sketch-layers-list
-                                     (list (sketch-group (format "layer-%s" 
new-layer)))))
-    (setq sketch-active-layer new-layer)
-    (setq show-layers (append show-layers (list new-layer)))
-    (transient-infix-set active-layer-infix new-layer)
-    (transient-infix-set show-layers-infix show-layers))
-  (transient--redisplay)
-  (message "Existing layers (indices): %s" (mapconcat #'number-to-string
-                                                      (number-sequence 0 (1- 
(length sketch-layers-list)))
-                                                      ", ")))
-
-(transient-define-infix sketch-layers ()
-  "List with layers that should be added to the image.
-Should be a list of numbers containing the number of the layers
-that should be added to the image. Initial value: (0)"
-  :description "Show layers"
-  :class 'transient-lisp-variable
-  :variable 'show-layers)
-;; :argument "--layers="
-;; :default '(0))
-;; :default (number-sequence (length sketch-layers-list)))
-
-(transient-define-suffix sketch-crop (event)
+  (let ((show-labels sketch-show-labels))
+    (setq sketch-show-labels "all")
+    (sketch-toolbar-refresh)
+    (sketch-redraw)
+    (svg-remove sketch-root (completing-read "Remove element with id: "
+                                             (sketch-labels-list)))
+    (setq sketch-show-labels show-labels)
+    (sketch-toolbar-refresh)
+    (sketch-redraw)))
+
+;;; Web/SVG colors
+(defun sketch-colors-sort (colors-rgb-alist)
+  (let ((list-colors-sort 'hsv))
+    ;; color sort function in courtesy of facemenu.el
+    ;; (colors-sorted (mapcar (lambda (c) (cons c (color-name-to-rgb c))) 
(defined-colors)))
+    ;; Schwartzian transform with `(color key1 key2 key3 ...)'.
+    (mapcar
+     'car
+     (sort (delq nil (mapcar
+                      (lambda (c)
+                        (let ((key (list-colors-sort-key
+                                    (car c))))
+                          (when key
+                            (cons c (if (consp key)
+                                        key
+                                      (list key))))))
+                      colors-rgb-alist)) ;; HERE IS THE LIST
+           (lambda (a b)
+             (let* ((a-keys (cdr a))
+                    (b-keys (cdr b))
+                    (a-key (car a-keys))
+                    (b-key (car b-keys)))
+               ;; Skip common keys at the beginning of key lists.
+               (while (and a-key b-key (equal a-key b-key))
+                 (setq a-keys (cdr a-keys) a-key (car a-keys)
+                       b-keys (cdr b-keys) b-key (car b-keys)))
+               (cond
+                ((and (numberp a-key) (numberp b-key))
+                 (< a-key b-key))
+                ((and (stringp a-key) (stringp b-key))
+                 (string< a-key b-key)))))))))
+
+(defun sketch-crop (event)
+  "Crop the image to selection.
+Translate the svg-root via its transform attribute and resizes
+the canvas.
+
+Because the grid is implemented as a pattern on the background
+rectangle, the corners of the cropping area should coincide with
+major-grid nodes if the object should stay aligned with the
+grid (using snap to grid)."
   (interactive "@e")
-  (let* ((args (when transient-current-prefix (transient-args 
'sketch-transient)))
-         (start (event-start event))
-         (grid-param (plist-get (cdr (posn-image start)) :grid-param))
-         (snap (transient-arg-value "--snap-to-grid=" args))
+  (let* ((start (event-start event))
+         (snap sketch-snap-to-grid)
          (start-coords (if (or (not snap) (string= snap "nil"))
                            (posn-object-x-y start)
-                         (sketch--snap-to-grid (posn-object-x-y start) 
grid-param)))
+                         (sketch--snap-to-grid (posn-object-x-y start) 
sketch-minor-grid-param)))
          (end (event-end event))
          (end-coords (if (or (not snap) (string= snap "nil"))
                          (posn-object-x-y end)
-                       (sketch--snap-to-grid (posn-object-x-y end) 
grid-param)))
+                       (sketch--snap-to-grid (posn-object-x-y end) 
sketch-minor-grid-param)))
          (new-width (abs (- (car end-coords) (car start-coords))))
          (new-height (abs (- (cdr end-coords) (cdr start-coords)))))
-    (setq svg-canvas (svg-create new-width new-height :stroke "gray"))
-    (svg-marker svg-canvas "arrow" 8 8 "black" t)
-    (svg-rectangle svg-canvas 0 0 new-width new-height :fill "white")
+    ;; (dom-set-attribute sketch-svg 'viewBox (format "%s %s %s %s"
+    ;;                                                (car start-coords)
+    ;;                                                (cdr start-coords)
+    ;;                                                (car end-coords)
+    ;;                                                (cdr end-coords)))
+    (sketch--create-canvas new-width new-height)
+    ;; (svg-marker sketch-canvas "arrow" 8 8 "black" t)
+    ;; (svg-rectangle sketch-canvas 0 0 new-width new-height :fill "white")
     (sketch--svg-translate (car start-coords) (cdr start-coords) sketch-root)
     (sketch-redraw)))
 
-(defun sketch-image (svg &rest props)
-  "Return an image object from SVG.
-PROPS is passed on to `create-image' as its PROPS list."
-  (apply
-   #'create-image
-   (with-temp-buffer
-     (insert "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
-<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" 
\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\";>\n")
-     (svg-print svg)
-     (buffer-string))
-   'svg t props))
+(defun sketch-undo (&optional count)
+  (interactive)
+  ;; (let ((inhibit-read-only t))
+  (cond ((fboundp 'evil-undo)
+         (evil-undo count))
+        ((fboundp 'undo-tree-undo)
+         (undo-tree-undo))
+        (t (undo)))
+  ;; )
+  (setq sketch-svg (read (buffer-string)))
+  (setq sketch-root (car (dom-by-id sketch-svg "root")))
+  (setq sketch-layers-list (dom-elements sketch-root 'id "layer"))
+  (unless sketch-layers-list (call-interactively #'sketch-add-layer)))
 
-(defun sketch-insert-image (svg string &rest props)
-  "Insert SVG as an image at point.
-If the SVG is later changed, the image will also be updated."
-  (let ((image (apply #'sketch-image svg props))
-             (marker (point-marker)))
-    (insert-image image string)
-    (dom-set-attribute svg :image marker)))
+(defun sketch-redo (&optional count)
+  (interactive)
+  (let ((inhibit-read-only t))
+    (cond ((fboundp 'evil-undo)
+           (evil-redo count))
+          ((fboundp 'undo-tree-redo)
+           (undo-tree-redo))
+          (t (user-error "This command requires `undo-tree' or `evil' to be 
available"))))
+  (setq sketch-root (read (buffer-string)))
+  (setq sketch-layers-list (dom-elements sketch-root 'id "layer"))
+  (unless sketch-layers-list (call-interactively #'sketch-add-layer)))
+
+;; Adapted from `read-color'
+(defun read-color-web (&optional prompt convert-to-RGB)
+  "Read a color name or RGB triplet.
+Completion is available for color names, but not for RGB triplets.
+
+RGB triplets have the form \"#RRGGBB\".  Each of the R, G, and B
+components can have one to four digits, but all three components
+must have the same number of digits.  Each digit is a hex value
+between 0 and F; either upper case or lower case for A through F
+are acceptable.
+
+In addition to standard color names and RGB hex values, the
+following are available as color candidates.  In each case, the
+corresponding color is used.
+
+ * `foreground at point'   - foreground under the cursor
+ * `background at point'   - background under the cursor
+
+Optional arg PROMPT is the prompt; if nil, use a default prompt.
+
+Interactively, or with optional arg CONVERT-TO-RGB-P non-nil,
+convert an input color name to an RGB hex string.  Return the RGB
+hex string.
+
+Interactively, displays a list of colored completions.  If optional
+argument FOREGROUND is non-nil, shows them as foregrounds, otherwise
+as backgrounds."
+  (interactive "i\np")    ; Always convert to RGB interactively.
+  (let* ((completion-ignore-case t)
+         (colors (mapcar
+                  (lambda (color-name)
+                    (let ((color (copy-sequence color-name)))
+                      (propertize color 'face
+                                  (list :foreground (readable-foreground-color 
color-name)
+                                        :background color))))
+                  (mapcar #'car (sketch-colors-sort 
shr-color-html-colors-alist))))
+         (color (completing-read
+                 (or prompt "Color (name or #RGB triplet): ")
+                 ;; Completing function for reading colors, accepting
+                 ;; both color names and RGB triplets.
+                 (lambda (string pred flag)
+                   (cond
+                    ((null flag)        ; Try completion.
+                     (or (try-completion string colors pred)
+                         (if (color-defined-p string)
+                             string)))
+                    ((eq flag t)        ; List all completions.
+                     (or (all-completions string colors pred)
+                         (if (color-defined-p string)
+                             (list string))))
+                    ((eq flag 'lambda)  ; Test completion.
+                     (or (member string colors)
+                         (color-defined-p string)))))
+                 nil t)))
+
+    ;; Process named colors.
+    (when (member color colors)
+      (cond ((string-equal color "foreground at point")
+             (setq color (foreground-color-at-point)))
+            ((string-equal color "background at point")
+             (setq color (background-color-at-point))))
+      (when (and convert-to-RGB
+                 (not (string-equal color "")))
+        (let ((components (x-color-values color)))
+          (unless (string-match-p 
"^#\\(?:[[:xdigit:]][[:xdigit:]][[:xdigit:]]\\)+$" color)
+            (setq color (format "#%04X%04X%04X"
+                                (logand 65535 (nth 0 components))
+                                (logand 65535 (nth 1 components))
+                                (logand 65535 (nth 2 components))))))))
+    color))
 
-(defun sketch-possibly-update-image (svg)
-  (let ((marker (dom-attr svg :image)))
-    (when (and marker
-                    (buffer-live-p (marker-buffer marker)))
-      (with-current-buffer (marker-buffer marker)
-             (put-text-property marker (1+ marker) 'display (svg-image 
svg))))))
+(defvar sketch-colors-basic '("White" "Silver" "Gray" "Black"
+                              "Red" "Maroon" "Yellow" "Olive"
+                              "Lime" "Green" "Aqua" "Teal"
+                              "Blue" "Navy" "Fuchsia" "Purple"))
 
-(transient-define-suffix sketch-save ()
+
+;;; Configuration
+(defun sketch-set-action ()
   (interactive)
-  (let ((image (get-char-property (point) 'display))
-        (file (read-file-name "Save as: ")))
-    (with-temp-file file
-      (insert (plist-get (cdr image) :data)))))
-
-(transient-define-suffix sketch-insert-image-to-buffer (&optional 
insert-at-end-of-file)
-  "Insert image to buffer at point.
-When prefixed with a universal argument \\[universal-argument]
-then insert at end of file.
-
-Prompts for buffer for insert, then for the image file name for
-save. If the image is saved in a sub-directory of the buffer file
-then insert a relative link, otherwise insert an absolute link."
+  (setq sketch-action
+        (intern
+         (let ((read-answer-short t))
+           (read-answer "Select object: "
+                        '(("freehand"  ?f "draw freehand with mouse drag")
+                          ("line"      ?l "draw line with mouse drag")
+                          ("rectangle" ?r "draw rectangle with mouse drag")
+                          ("circle"    ?c "draw circle with mouse drag")
+                          ("ellipse"   ?e "draw-ellipse with mouse drag")
+                          ("polyline"  ?p "draw polyline by clicking. Double 
click to insert end.")
+                          ("polygon"   ?g "draw polygon by clicking. Double 
click to insert end.")
+                          ("select"    ?s "select objects")
+                          ("move"      ?m "move selected objects")
+                          ("translate" ?t "translate selected objects"))))))
+  (sketch-toolbar-refresh))
+
+(defun sketch-set-colors (&optional arg)
+  "Set stroke, fill or both colors simultaneously.
+With single prefix ARG, set fill color. With double prefix ARG,
+set stroke and fill color simultaneously. Otherwise set stroke
+color."
+  (interactive "p")
+  (print arg)
+  (let ((color (substring-no-properties (read-color-web "Select color: ")))
+        (fns (pcase arg
+               (1 '(sketch-stroke-color))
+               (4 '(sketch-fill-color))
+               (16 '(sketch-stroke-color
+                     sketch-fill-color)))))
+    (dolist (fn fns)
+      (set fn color)))
+  (sketch-toolbar-refresh))
+
+(defun sketch-set-font-with-keyboard (arg)
   (interactive "P")
-  (let* ((buffer (get-buffer (read-buffer "Add to buffer: ")))
-         (buffer-dir (file-name-directory (buffer-file-name buffer)))
-         (image (get-char-property (point) 'display))
-         (file (read-file-name "Save as: " buffer-dir)))
-    (kill-buffer "*sketch*")
-    (with-temp-file file
-      (insert (plist-get (cdr image) :data)))
-    (switch-to-buffer buffer)
-    (when insert-at-end-of-file
-      (goto-char (point-max)))
-    (insert (format "[[%s]]" (if (string-match buffer-dir file)
-                                 (concat "./" (file-name-nondirectory file))
-                               file)))))
+  (if arg
+      (sketch-set-font)
+    (completing-read "Select font: " (font-family-list))))
+
+(defun sketch-set-font-size ()
+  (interactive)
+  (setq sketch-font-size (string-to-number
+                          (completing-read "Select font size: " 
(number-sequence 8 60)))))
+
+(defun sketch-set-width ()
+  (interactive)
+  (setq sketch-stroke-width (string-to-number
+                             (completing-read "Enter width (floats allowed): "
+                                              (number-sequence 1 10)))))
+
+(defun sketch-set-dasharray ()
+  (interactive)
+  (setq sketch-stroke-dasharray (completing-read "Enter dasharry (custom 
values allowed): "
+                                                 '("8" "8,4"))))
+
+(defun sketch-set-font ()
+  (interactive)
+  (pop-to-buffer "*sketch-fonts*")
+  (let ((button-width (* 4 5 (default-font-width)))
+        (button-height (* 2 (default-font-height)))
+        (counter 0))
+    (dolist (x (sort (seq-uniq (font-family-list)) #'string-lessp))
+      (insert-text-button x
+                          'action
+                          (lambda (button) (interactive)
+                            (setq sketch-font (button-label button))
+                            (kill-buffer)
+                            (sketch-toolbar-refresh))
+                          'display (svg-image (let ((svg (svg-create 
button-width button-height)))
+                                                (svg-rectangle svg 0 0 
button-width button-height
+                                                               :fill "white")
+                                                (svg-text svg "ABC abc"
+                                                          :font-size 
button-height
+                                                          :font-family x
+                                                          :stroke "black"
+                                                          :fill "black"
+                                                          :x 4
+                                                          (- button-height 4))
+                                                svg)))
+      (insert " ")
+      (insert x)
+      (setq counter (1+ counter))
+      (if (/= counter 2)
+          (insert (make-string
+                   (- 30 (length x)) (string-to-char " ")))
+        (insert "\n\n")
+        (setq counter 0)))
+    (goto-char (point-min))
+    (special-mode)))
+
+
 
-(transient-define-suffix sketch-quick-insert-image (&optional 
insert-at-end-of-file)
+(defun sketch-toggle-grid ()
+  (interactive)
+  (setq sketch-show-grid (if sketch-show-grid nil t))
+  (if (not sketch-show-grid)
+      (dom-set-attribute (car (dom-by-id sketch-canvas "bg")) 'fill 
sketch-background)
+    (unless sketch-grid
+      (sketch-create-grid))
+    (dom-set-attribute (car (dom-by-id sketch-canvas "bg")) 'fill 
"url(#grid)"))
+  ;;        (svg--def sketch-svg (cdr sketch-grid))
+  ;;        (svg--def sketch-svg (car sketch-grid)))
+  ;;       (t
+  ;;        (dom-remove-node sketch-svg (car (dom-by-id sketch-svg "^grid$")))
+  (sketch-redraw)
+  (sketch-toolbar-refresh))
+
+(defun sketch-toggle-snap ()
+  (interactive)
+  (setq sketch-snap-to-grid (if sketch-snap-to-grid nil t))
+  (sketch-toolbar-refresh)
+  (message "Snap-to-grid %s" (if sketch-snap-to-grid "on" "off")))
+
+(defun sketch-cycle-labels ()
+  (interactive)
+  (setq sketch-show-labels (pcase sketch-show-labels
+                             ("layer" "all")
+                             ("all" nil)
+                             (_ "layer")))
+  (sketch-redraw)
+  (sketch-toolbar-refresh))
+
+(defun sketch-toggle-coords ()
+  (interactive)
+  (setq sketch-show-coords (if sketch-show-coords nil t))
+  (if (not sketch-show-coords)
+      (setq mode-line-format sketch-coordless-mode-line-format)
+    (setq sketch-coordless-mode-line-format mode-line-format)
+    (add-to-list 'mode-line-format '(:eval sketch-cursor-position) t)))
+
+
+(defvar-local sketch-call-buffer nil)
+
+(add-hook 'org-ctrl-c-ctrl-c-final-hook 'sketch-org-toggle-image)
+
+(defun sketch-org-toggle-image ()
+  (let* ((context (org-element-lineage
+                   (org-element-context)
+                   ;; Limit to supported contexts.
+                   '(babel-call clock dynamic-block footnote-definition
+                                footnote-reference inline-babel-call 
inline-src-block
+                                inlinetask item keyword node-property paragraph
+                                plain-list planning property-drawer 
radio-target
+                                src-block statistics-cookie table table-cell 
table-row
+                                timestamp)
+                   t))
+         (type (org-element-type context)))
+    (when (eq type 'paragraph)
+      (let ((parent (org-element-property :parent context)))
+        (when (eq (org-element-type parent) 'special-block)
+          (let* ((props (cadr parent))
+                 (beg (plist-get props :contents-begin))
+                 (end (plist-get props :contents-end)))
+            (if (get-char-property (point) 'display)
+                (remove-text-properties beg end '(display nil))
+              (let* ((xml (buffer-substring-no-properties beg end))
+                     (image (create-image xml 'svg t)))
+                (put-text-property beg (1- end) 'display image)
+                (goto-char beg)))))))))
+
+(defun sketch-quick-insert-image (&optional insert-at-end-of-file)
   "Insert image at point as overlay wrapped in org image block.
 The image overlay is created over the inserted xml
 definition and is wrapped inside an image block (not yet
@@ -1381,248 +1335,318 @@ then insert the image at the end"
                             (buffer-string)))
       (insert "\n#+END_IMAGE"))))
 
-;;; Modify object
-
-(defun sketch-translate-object (buffer object-def props coords amount)
-  (dolist (coord coords)
-    (cl-incf (alist-get coord props) amount))
-  (sketch-redraw object-def buffer))
-
-(defun sketch-parse-transform-value (value)
-  (let ((transforms (mapcar (lambda (val)
-                              (split-string val "[(,)]" t))
-                            (split-string value))))
-    (mapcar (lambda (x)
-              (cons (intern (car x)) (mapcar (lambda (val)
-                                               (string-to-number val))
-                                             (cdr x))))
-            transforms)))
-
-(defun sketch-format-transfrom-value (value)
-  (string-join (mapcar (lambda (x) (concat (symbol-name (car x))
-                                           "("
-                                           (number-to-string (cadr x))
-                                           (if-let (y (caddr x))
-                                               (concat "," (number-to-string 
y)))
-                                           ")"))
-                       value)
-               " "))
-
-(defun sketch-group-translate (buffer object-def direction &optional fast)
-  (let ((transform (sketch-parse-transform-value
-                    (dom-attr object-def
-                              'transform)))
-        (amount (if fast
-                    10
-                  1)))
-    (pcase direction
-      ('up (cl-decf (second (alist-get 'translate transform)) amount))
-      ('down (cl-incf (second (alist-get 'translate transform)) amount)))
-    (dom-set-attribute object-def
-                       'transform
-                       (sketch-format-transfrom-value transform))
-    (sketch-redraw object-def buffer)))
+(defun sketch-help ()
+  (interactive)
+  (if (> emacs-major-version 27)
+      (describe-keymap 'sketch-mode-map)
+    (let ((help-window-select t))
+      (describe-bindings)
+      (search-forward "sketch-mode"))))
 
-(defun sketch-group-scale (buffer object-def direction &optional fast)
-  (let ((transform (sketch-parse-transform-value
-                    (dom-attr object-def
-                              'transform)))
-        (amount (if fast
-                    1
-                  0.1)))
-    (unless (alist-get 'scale transform)
-      (push '(scale 1) transform))
-    (pcase direction
-      ('up (cl-incf (car (alist-get 'scale transform)) amount))
-      ('down (cl-decf (car (alist-get 'scale transform)) amount)))
-    (dom-set-attribute object-def
-                       'transform
-                       (sketch-format-transfrom-value transform))
-    (sketch-redraw object-def buffer)))
 
-(transient-define-suffix sketch-group-scale-up (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object))))
-    (sketch-group-scale buffer (car object-def) 'up)))
-
-(transient-define-suffix sketch-group-scale-up-fast (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object))))
-    (sketch-group-scale buffer (car object-def) 'up t)))
-
-(transient-define-suffix sketch-group-scale-down (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object))))
-    (sketch-group-scale buffer (car object-def) 'down)))
-
-(transient-define-suffix sketch-group-scale-down-fast (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object))))
-    (sketch-group-scale buffer (car object-def) 'down t)))
-
-;; TODO 'refactor' subsequent suffixes (e.g. create general function/macro)
-(transient-define-suffix sketch-translate-down (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object)))
-         (props (cadar object-def)))
-    (if (eq (caar object-def) 'g)
-        (sketch-group-translate buffer (car object-def) 'down)
-      (sketch-translate-object buffer
-                               object-def
-                               props
-                               (pcase (caar object-def)
-                                 ('line '(y1 y2))
-                                 ('text '(y)))
-                               1))))
-
-(transient-define-suffix sketch-translate-fast-down (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object)))
-         (props (cadar object-def)))
-    (if (eq (caar object-def) 'g)
-        (sketch-group-translate buffer (car object-def) 'down t)
-      (sketch-translate-object buffer
-                               object-def
-                               props
-                               (pcase (caar object-def)
-                                 ('line '(y1 y2))
-                                 ('text '(y)))
-                               10))))
-
-(transient-define-suffix sketch-translate-up (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object)))
-         (props (cadar object-def)))
-    (if (eq (caar object-def) 'g)
-        (sketch-group-translate buffer (car object-def) 'up)
-      (sketch-translate-object buffer
-                               object-def
-                               props
-                               (pcase (caar object-def)
-                                 ('line '(y1 y2))
-                                 ('text '(y)))
-                               -1))))
-
-(transient-define-suffix sketch-translate-fast-up (args)
-  (interactive (list (oref transient-current-prefix value)))
-  (let* ((object (transient-arg-value "--object=" args))
-         (buffer (transient-arg-value "--buffer=" args))
-         (object-def (dom-by-id sketch-svg (format "^%s$" object)))
-         (props (cadar object-def)))
-    (if (eq (caar object-def) 'g)
-        (sketch-group-translate buffer (car object-def) 'up t)
-      (sketch-translate-object buffer
-                               object-def
-                               props
-                               (pcase (caar object-def)
-                                 ('line '(y1 y2))
-                                 ('text '(y)))
-                               -10))))
-
-(transient-define-prefix sketch-modify-object (&optional group)
-  "Set object properties."
-  :transient-suffix 'transient--do-call
-  ["Properties"
-   [("o" "object" sketch-modify-object 'transient--do-exit)]]
-  [[("<down>" "down" sketch-translate-down)
-    ("<up>" "up" sketch-translate-up)]
-   [("S-<down>" "fast down" sketch-translate-fast-down)
-    ("S-<up>" "fast up" sketch-translate-fast-up)]
-   [("u" "scale up" sketch-group-scale-up)
-    ("d" "scale up" sketch-group-scale-down)]]
-  [("l" sketch-cycle-labels)
-   ("q" "Quit" transient-quit-one)]
+;;; Toolbar
+(defun sketch-toolbar-refresh ()
+  (with-current-buffer (get-buffer "*sketch-toolbar*")
+    (let ((inhibit-read-only t))
+      (erase-buffer)
+      (insert (propertize "Press . for hydra or press ? for help\n\n" 'face 
'bold))
+      (sketch-toolbar-colors)
+      (insert "\n")
+      (sketch-toolbar-widths)
+      (insert "\n")
+      (sketch-toolbar-objects)
+      (insert "\n\n")
+      (sketch-toolbar-toggles)
+      (insert "\n\n")
+      (sketch-toolbar-font)
+      (goto-char (point-min)))))
+
+(defun sketch-toggle-toolbar ()
   (interactive)
-  (let* ((object (if group
-                     group
-                   (completing-read "Transform element with id: "
-                                    (sketch-labels-list))))
-         (buffer (get-buffer-create (format "*sketch-object-%s*" object))))
-    (display-buffer buffer '(display-buffer-in-side-window . ((side . right) 
(window-width . 70))))
-    (pp (cadar (dom-by-id sketch-svg (format "^%s$" object))) buffer)
-    (with-current-buffer buffer
-      (emacs-lisp-mode))
-    (transient-setup 'sketch-modify-object
-                     nil
-                     nil
-                     :value (list (format "--object=%s" object)
-                                  (format "--buffer=%s" buffer)))))
+  (let ((win (get-buffer-window "*sketch-toolbar*")))
+    (if win
+        (delete-window win)
+      (let ((buffer (get-buffer-create "*sketch-toolbar*")))
+        (set-window-dedicated-p
+         (display-buffer-in-side-window (get-buffer-create "*sketch-toolbar*")
+                                        `((side . right)
+                                          (window-width . ,(funcall 
sketch-side-window-max-width))))
+         t)
+        (window-resize (get-buffer-window buffer) -3 t)
+        (with-current-buffer buffer
+          (setq cursor-type nil)
+          (special-mode))
+        (sketch-toolbar-refresh)))))
+
+(defun sketch-toolbar-colors ()
+  ;; STROKE COLOR
+  (insert (propertize "STROKE COLOR: "))
+  (insert-text-button "   "
+                      'action
+                      (lambda (button) (interactive)
+                        (setq sketch-stroke-color (plist-get (button-get 
button 'face) :background)))
+                      'face (list :background
+                                  (alist-get sketch-stroke-color
+                                             shr-color-html-colors-alist
+                                             nil nil 'string=)))
+  (insert " ")
+  (insert (if (string= sketch-stroke-color "none")
+              "none"
+            sketch-stroke-color))
+  (insert "\n")
+  (insert-text-button "none"
+                      'action (lambda (button) (interactive)
+                                (setq sketch-stroke-color "none")
+                                (sketch-toolbar-refresh)))
+  (insert "\n\n")
+  (let ((counter 0))
+    (dolist (color sketch-colors-basic)
+      (insert-text-button "   "
+                          'action
+                          (lambda (button) (interactive)
+                            (setq sketch-stroke-color
+                                  (car (rassoc (plist-get (button-get button 
'face) :background)
+                                               shr-color-html-colors-alist)))
+                            (sketch-toolbar-refresh)
+                            ;; (transient-quit-all)
+                            ;; (call-interactively #'sketch-transient)
+                            )
+                          'face (list
+                                 :background (alist-get color
+                                                        
shr-color-html-colors-alist
+                                                        nil nil 'string=)))
+      (setq counter (1+ counter))
+      (if (not (= counter 8))
+          (insert " ")
+        (insert "\n\n")
+        ;; (when (= counter 8)
+        ;;   (insert "\n")
+        (setq counter 0))))
+
+  (insert "\n")
+
+  ;; FILL COLOR
+  (insert (propertize "FILL COLOR: "))
+  (apply #'insert-text-button "   "
+         'action
+         (lambda (button) (interactive)
+           (print sketch-fill-color))
+         (pcase sketch-fill-color
+           ("none" nil)
+           (_ (list 'face (when sketch-fill-color
+                            (list :background (alist-get sketch-fill-color
+                                                         
shr-color-html-colors-alist
+                                                         nil nil 
'string=)))))))
+  (insert " ")
+  (insert (if (string= sketch-fill-color "none")
+              "none"
+            sketch-fill-color))
+  (insert "\n")
+  (insert-text-button "none"
+                      'action (lambda (_) (interactive)
+                                (setq sketch-fill-color "none")
+                                (sketch-toolbar-refresh)))
+  (insert "\n\n")
+  (let ((counter 0))
+    (dolist (color sketch-colors-basic)
+      (insert-text-button "   "
+                          'action
+                          (lambda (button) (interactive)
+                            (setq sketch-fill-color
+                                  (car (rassoc
+                                        (plist-get (button-get button 'face) 
:background)
+                                        shr-color-html-colors-alist)))
+                            (sketch-toolbar-refresh)
+                            ;; (transient-quit-all)
+                            ;; (call-interactively #'sketch-transient)
+                            )
+                          'face (list
+                                 :background (alist-get color
+                                                        
shr-color-html-colors-alist
+                                                        nil nil 'string=)))
+      (setq counter (1+ counter))
+      (if (not (= counter 8))
+          (insert " ")
+        (insert "\n\n")
+        (setq counter 0)))))
+
+(defun sketch-toolbar-widths ()
+  (insert "STROKE WIDTH: ")
+  (insert (number-to-string sketch-stroke-width))
+  (insert "\n")
+  (let* ((widths 12)
+         (button-width (+ (* 4 (default-font-width)) 3))
+         (button-height (default-font-height))
+         (stroke-height (/ button-height 2)))
+    (let ((counter 0))
+      (dotimes (w widths)
+        (insert-text-button (format "%s" (1+ w))
+                            'action
+                            (lambda (button) (interactive)
+                              (setq sketch-stroke-width (string-to-number 
(button-label button)))
+                              (sketch-toolbar-refresh)
+                              ;; (transient-quit-all)
+                              ;; (call-interactively #'sketch-transient)
+                              )
+                            'display (svg-image (let ((svg (svg-create 
button-width button-height)))
+                                                  (svg-rectangle svg 0 0 
button-width button-height
+                                                                 :fill "white")
+                                                  (svg-line svg 5 stroke-height
+                                                            (- button-width 5) 
stroke-height
+                                                            :stroke "black" 
:stroke-width (1+ w))
+                                                  svg)))
+        (setq counter (1+ counter))
+        (if (not (= counter 6))
+            (insert " ")
+          (insert "\n\n")
+          (setq counter 0))))))
+
+(defun sketch-toolbar-objects ()
+  (insert "MOUSE ACTION\n")
+  (insert "draw\n")
+  (let ((objects '(line polyline circle ellipse rectangle polygon)))
+    (let ((counter 0))
+      (while objects
+        (let ((o (car objects)))
+          (apply #'insert-text-button
+                 (symbol-name o)
+                 'action (lambda (button) (interactive)
+                           (setq sketch-action (intern (button-label button)))
+                           (sketch-toolbar-refresh))
+                 (when (eq o sketch-action)
+                   (list 'face 'link-visited)))
+          (setq counter (1+ counter))
+          (cond ((/= counter 4)
+                 (dotimes (_ (- 10 (length (symbol-name o))))
+                   (insert " ")))
+                ;; (let ((o (cadr objects)))
+                ;;   (apply #'insert-text-button
+                ;;          (symbol-name o)
+                ;;          'action (lambda (button) (interactive)
+                ;;                    (setq sketch-action (intern 
(button-label button)))
+                ;;                    (sketch-toolbar-refresh))
+                ;;          (when (eq o sketch-action)
+                ;;            (list 'face 'link-visited))))
+                ;; ;; (list 'face (if (eq o sketch-action)
+                ;; ;;                 'widget-button-pressed
+                ;; ;;               'widget-button)))
+                (t
+                 (insert "\n")
+                 (setq counter 0)))
+          (setq objects (cdr objects))))))
+  (apply #'insert-text-button
+         "freehand"
+         'action (lambda (button) (interactive)
+                   (setq sketch-action (intern (button-label button)))
+                   (sketch-toolbar-refresh))
+         (when (eq 'freehand sketch-action)
+           (list 'face 'link-visited)))
+  (insert "  ")
+  (apply #'insert-text-button
+         "text"
+         'action (lambda (button) (interactive)
+                   (setq sketch-action (intern (button-label button)))
+                   (sketch-toolbar-refresh))
+         (when (eq 'text sketch-action)
+           (list 'face 'link-visited)))
+  (insert "\n\n")
+  (insert "edit\n")
+  (dolist (e '(select move translate))
+    (apply #'insert-text-button
+           (symbol-name e)
+           'action (lambda (button) (interactive)
+                     ;; (setq sketch-action (intern (button-label button)))
+                     (pcase (intern (button-label button))
+                       ('select (user-error "Feature not yet implemented, 
instead press `v' to select with keyboard"))
+                       ((or 'move 'translate) (user-error "Feature not yet 
implemented, instead press `m' to select and translate")))
+                     (sketch-toolbar-refresh))
+           (when (eq e sketch-action)
+             (list 'face 'link-visited)))
+    (insert " ")
+    ))
+
+(defun sketch-toolbar-toggles ()
+  (insert "TOGGLES\n")
+  (insert "Grid: ")
+  (apply #'insert-text-button (if sketch-show-grid "show" "hide")
+         'action
+         (lambda (button) (interactive)
+           (sketch-toggle-grid)
+           (sketch-toolbar-refresh))
+         (when sketch-show-grid
+           (list 'face 'link-visited)))
+  ;; (list 'face (if sketch-grid
+  ;;                 'widget-button-pressed
+  ;;               'widget-button)))
+  (insert "   ")
+  (insert "Snap: ")
+  (apply #'insert-text-button (if sketch-snap-to-grid "on" "off")
+         'action
+         (lambda (button) (interactive)
+           (sketch-toggle-snap)
+           (sketch-toolbar-refresh))
+         (when sketch-snap-to-grid
+           (list 'face 'link-visited)))
+  (insert "   ")
+  (insert "Labels: ")
+  (apply #'insert-text-button (or sketch-show-labels "hide")
+         'action
+         (lambda (button) (interactive)
+           (sketch-cycle-labels)
+           (sketch-toolbar-refresh))
+         (when sketch-show-labels
+           (list 'face 'link-visited))))
+;; (list 'face (if sketch-snap-to-grid
+;;                 'widget-button-pressed
+;;               'widget-button))))
+
+(defun sketch-toolbar-font ()
+  (interactive)
+  (insert "FONT\n")
+  (insert "family: ")
+  (if sketch-font
+      (let ((button-width (* 2 5 (default-font-width)))
+            (button-height (default-font-height))
+            (counter 0))
+        (insert-text-button sketch-font
+                            'action
+                            (lambda (_) (interactive)
+                              (sketch-set-font)
+                              ;; (transient-quit-all)
+                              ;; (call-interactively #'sketch-transient)
+                              )
+                            'display (svg-image (let ((svg (svg-create 
button-width button-height)))
+                                                  (svg-rectangle svg 0 0 
button-width button-height
+                                                                 :fill "white")
+                                                  (svg-text svg "ABC abc"
+                                                            :font-size 
button-height
+                                                            :font-family 
sketch-font
+                                                            :stroke "black"
+                                                            :fill "black"
+                                                            :x 3
+                                                            (- button-height 
3))
+                                                  svg))))
+    (insert-text-button "none"
+                        'action
+                        (lambda (_) (interactive)
+                          (sketch-set-font))))
+  (insert"   ")
+  (insert "Size: ")
+  (insert-text-button (number-to-string sketch-font-size)
+                      'action
+                      (lambda (_) (interactive)
+                        (setq sketch-font-size (string-to-number
+                                                (completing-read "Select font 
size: "
+                                                                 
(number-sequence 8 40 2))))
+                        ;; (transient-quit-all)
+                        ;; (call-interactively #'sketch-transient)
+                        )))
+
+(defun sketch-kill-toolbar ()
+  (let ((toolbar (get-buffer "*sketch-toolbar*")))
+    (when toolbar
+      (kill-buffer toolbar))))
 
-(defun sketch-update-lisp-window (lisp buffer)
-  ;; (let ((sketch sketch-root))
-  (with-current-buffer buffer
-    (erase-buffer)
-    (pp lisp (current-buffer))
-    (goto-char (point-max))))
-
-;;; import/snippets
-
-(defun sketch-snippet-get-dom (svg-file)
-  (interactive "fCreate dom from file: ")
-  (with-temp-buffer "svg"
-                    (insert-file-contents-literally svg-file)
-                    (xml-remove-comments (point-min) (point-max))
-                    (libxml-parse-xml-region (point-min) (point-max))))
-
-(defun sketch-snippets-add-ids (dom)
-  (let ((idx 0))
-    (dolist (n (dom-by-tag dom 'g))
-      (dom-set-attribute n 'id (number-to-string idx))
-      (setq idx (1+ idx)))))
-
-(defun sketch-snippets-add-labels (dom)
-  (interactive "f")
-  (sketch-snippets-add-ids dom)
-  (mapc (lambda (n)
-          (let* ((s (dom-attr n 'transform))
-                 (coords (when s
-                           (split-string
-                            (string-trim
-                             s
-                             "translate(" ")")
-                            ","))))
-            (svg-text dom
-                      (dom-attr n 'id)
-                      :x (car coords)
-                      :y (cadr coords)
-                      :font-size 10
-                      :stroke "red"
-                      :fill "red")))
-        (cdr (dom-by-tag dom 'g)))
-  (unless sketch-mode
-    (user-error "Not in sketch-mode buffer"))
-  ;; (save-current-buffer
-  ;; (when lisp-buffer
-  ;;   (sketch-update-lisp-window lisp lisp-buffer))
-  ;; (let ((lisp-window (or (get-buffer-window "*sketch-root*")
-  ;;                        (get-buffer-window lisp-buffer))))
-  ;;   (unless (string= (buffer-name (window-buffer lisp-window)) "*sketch*")
-  ;;     (if-let (buf (get-buffer"*sketch-root*"))
-  ;;         (sketch-update-lisp-window sketch-root buf)
-  ;;       (sketch-update-lisp-window lisp lisp-buffer))))
-  ;; (setq sketch-root (append (cl-subseq sketch-root 0 2) (list (nth (car 
show-layers) svg-layers))))
-  ;; (dolist (layer (cdr show-layers))
-  ;;   (setq sketch-root (append sketch-root (list (nth layer svg-layers)))))
-  ;; (setq sketch-svg (append svg-canvas
-  ;;                          (when sketch-show-grid (list sketch-grid))
-  ;;                          (when sketch-show-labels (list (sketch-labels)))
-  ;;                          (list sketch-root)))
-  (erase-buffer) ;; a (not exact) alternative is to use (kill-backward-chars 1)
-  (insert-image (svg-image dom)))
 
 (provide 'sketch-mode)
 ;;; sketch-mode.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]