diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,11 @@
 # Changelog
 
-## [Unreleased]
+## [unreleased]
+
+## [0.1.0.5]
+- various internal improvements to how previews are staged and committed
+- fix crash issue with undoing drags that contained folders
+- fix several other crashes
 
 ## [0.1.0.4]
 ### Changed
diff --git a/src/Potato/Data/Text/Zipper2.hs b/src/Potato/Data/Text/Zipper2.hs
--- a/src/Potato/Data/Text/Zipper2.hs
+++ b/src/Potato/Data/Text/Zipper2.hs
@@ -4,7 +4,7 @@
 import           Prelude
 
 import Control.Exception (assert)
-import Control.Monad.State (evalState, forM, get, put)
+import Control.Monad.State (evalState, get, put)
 import Data.Char (isSpace)
 import Data.Map (Map)
 import Data.Maybe (fromMaybe)
diff --git a/src/Potato/Flow.hs b/src/Potato/Flow.hs
--- a/src/Potato/Flow.hs
+++ b/src/Potato/Flow.hs
@@ -1,7 +1,7 @@
 module Potato.Flow (
-  module Potato.Flow.SElts
+  module Potato.Flow.Serialization.Snake
   , module Potato.Flow.Math
-  , module Potato.Flow.SEltMethods
+  , module Potato.Flow.Methods.SEltMethods
   , module Potato.Flow.Types
   , module Potato.Flow.Methods.Types
   , module Potato.Flow.Render
@@ -21,9 +21,9 @@
 import           Potato.Flow.Controller
 import           Potato.Flow.Math
 import           Potato.Flow.Render
-import           Potato.Flow.SEltMethods
+import           Potato.Flow.Methods.SEltMethods
 import           Potato.Flow.Methods.Types
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import Potato.Flow.OwlHelpers
 import Potato.Flow.OwlWorkspace
diff --git a/src/Potato/Flow/Attachments.hs b/src/Potato/Flow/Attachments.hs
--- a/src/Potato/Flow/Attachments.hs
+++ b/src/Potato/Flow/Attachments.hs
@@ -18,7 +18,7 @@
 
 import           Potato.Flow.Math
 import           Potato.Flow.OwlItem
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 import Potato.Flow.Methods.LineTypes
 
 import Data.List (minimumBy)
diff --git a/src/Potato/Flow/BroadPhase.hs b/src/Potato/Flow/BroadPhase.hs
--- a/src/Potato/Flow/BroadPhase.hs
+++ b/src/Potato/Flow/BroadPhase.hs
@@ -23,8 +23,8 @@
 import           Potato.Flow.Owl
 import           Potato.Flow.OwlItem
 import           Potato.Flow.OwlState
-import           Potato.Flow.SEltMethods
-import           Potato.Flow.SElts
+import           Potato.Flow.Methods.SEltMethods
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 
 import qualified Data.IntMap.Strict        as IM
diff --git a/src/Potato/Flow/Controller/Goat.hs b/src/Potato/Flow/Controller/Goat.hs
--- a/src/Potato/Flow/Controller/Goat.hs
+++ b/src/Potato/Flow/Controller/Goat.hs
@@ -48,8 +48,8 @@
 import           Potato.Flow.OwlState
 import           Potato.Flow.OwlWorkspace
 import           Potato.Flow.Render
-import           Potato.Flow.SEltMethods
-import           Potato.Flow.SElts
+import           Potato.Flow.Methods.SEltMethods
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import  Potato.Flow.Preview 
 import Potato.Flow.Methods.LlamaWorks
@@ -282,9 +282,11 @@
   gs_1 = gs {
       _goatState_screenRegion = x
     }
-  r = goat_setPan (_goatState_pan gs_1) gs_1
+  r = goat_rerenderAfterMove gs_1
+  
 
 
+
 endoGoatCmdWSEvent :: WSEvent -> GoatState -> GoatState
 endoGoatCmdWSEvent wsev gs = goat_applyWSEvent WSEventType_Local_Refresh wsev gs
 
@@ -303,23 +305,28 @@
       -- NOTE _goatState_layersHandler gets set by goat_applyWSEvent during refresh
     }
 
-endoGoatCmdSetFocusedArea :: GoatFocusedArea -> GoatState -> GoatState
-endoGoatCmdSetFocusedArea gfa goatState = r where
+goat_setFocusedArea :: GoatFocusedArea -> GoatState -> GoatState
+goat_setFocusedArea gfa goatState = r where
   didchange = gfa /= _goatState_focusedArea goatState
   goatstatewithnewfocus = goatState { _goatState_focusedArea = gfa }
   noactionneeded = goatstatewithnewfocus
   potatoHandlerInput = potatoHandlerInputFromGoatState goatState
   r = if didchange && pHandlerName (_goatState_layersHandler goatState) == handlerName_layersRename
+    -- special case to force confirmation on layersRename handler which is special because it does not generate a Preview
     then let
         goatState_afterAction = case pHandleKeyboard (_goatState_layersHandler goatState) potatoHandlerInput (KeyboardData KeyboardKey_Return []) of
           Nothing -> noactionneeded
-          Just pho -> goat_processLayersHandlerOutput pho goatstatewithnewfocus
-      in assert (_goatState_focusedArea goatState == GoatFocusedArea_Layers) $ goatState_afterAction  
+          Just pho -> traceShow "press enter" $ goat_processLayersHandlerOutput pho goatstatewithnewfocus
+      in assert (_goatState_focusedArea goatState == GoatFocusedArea_Layers) $ goatState_afterAction
     else noactionneeded
 
 
+endoGoatCmdSetFocusedArea :: GoatFocusedArea -> GoatState -> GoatState
+endoGoatCmdSetFocusedArea = goat_setFocusedArea
+
+
 endoGoatCmdMouse :: LMouseData -> GoatState -> GoatState
-endoGoatCmdMouse mouseData goatState = trace ("endomouse: " <> show mouseData) $ r where
+endoGoatCmdMouse mouseData goatState = r where
   sameSource = _mouseDrag_isLayerMouse (_goatState_mouseDrag goatState) == _lMouseData_isLayerMouse mouseData
   mouseSourceFailure = _mouseDrag_state (_goatState_mouseDrag goatState) /= MouseDragState_Up && not sameSource
   mouseDrag = case _mouseDrag_state (_goatState_mouseDrag goatState) of
@@ -328,9 +335,9 @@
 
     _                        ->  continueDrag mouseData (_goatState_mouseDrag goatState)
   canvasDrag = toRelMouseDrag last_pFState (_goatState_pan goatState) mouseDrag
-  goatState_withNewMouse = goatState {
+  goatState_withFocusedArea = goat_setFocusedArea (if isLayerMouse then GoatFocusedArea_Layers else GoatFocusedArea_Canvas) goatState
+  goatState_withNewMouse = goatState_withFocusedArea {
       _goatState_mouseDrag = mouseDrag
-      , _goatState_focusedArea = if isLayerMouse then GoatFocusedArea_Layers else GoatFocusedArea_Canvas
     }
   noChangeOutput = goatState_withNewMouse
   -- TODO maybe split this case out to endoGoatCmdLayerMouse
@@ -539,25 +546,32 @@
     , _renderContext_renderedCanvasRegion = _goatState_renderedCanvas goatState
   }
 
-goat_setPan :: XY -> GoatState -> GoatState
-goat_setPan (V2 dx dy) goatState = r where
+goat_addPan :: XY -> GoatState -> GoatState
+goat_addPan (V2 dx dy) goatState = r where
   -- set the pan
   -- render move
   -- render selection
   V2 cx0 cy0 = _goatState_pan goatState
   next_pan = V2 (cx0+dx) (cy0 + dy) 
 
+  gs_1 = goatState {
+      _goatState_pan = next_pan
+    }
+  
+  r = goat_rerenderAfterMove gs_1
+
+goat_rerenderAfterMove :: GoatState -> GoatState
+goat_rerenderAfterMove goatState = r where
+  -- render move
+  -- render selection
   rc = renderContextFromGoatState goatState
-  (rc_aftermove, _) = goat_renderCanvas_move rc next_pan (_goatState_screenRegion goatState)
+  (rc_aftermove, _) = goat_renderCanvas_move rc (_goatState_pan goatState) (_goatState_screenRegion goatState)
   rc_afterselection = goat_renderCanvas_selection rc_aftermove (_goatState_selection goatState)
   r = goatState {
-      _goatState_pan = next_pan
-      , _goatState_renderedCanvas = _renderContext_renderedCanvasRegion rc_aftermove
+      _goatState_renderedCanvas = _renderContext_renderedCanvasRegion rc_aftermove
       , _goatState_renderedSelection = _renderContext_renderedCanvasRegion rc_afterselection
     }
 
-
-
 computeCanvasSelection :: (HasCallStack) => GoatState -> CanvasSelection
 computeCanvasSelection goatState = r where
   pfs = goatState_pFState goatState
@@ -633,11 +647,11 @@
 
 
 goat_processHandlerOutput_noSetHandler :: PotatoHandlerOutput -> GoatState -> GoatState
-goat_processHandlerOutput_noSetHandler pho goatState = trace ("goat_processHandlerOutput_noSetHandler HANDLER OUTPUT: " <> show pho <> " CURRENT HANDLER: " <> show (_goatState_handler goatState)) $ r where
+goat_processHandlerOutput_noSetHandler pho goatState = r where
 
   r = case _potatoHandlerOutput_action pho of
     HOA_Select x y -> goat_setSelection x y goatState
-    HOA_Pan x -> goat_setPan x goatState
+    HOA_Pan x -> goat_addPan x goatState
     HOA_Layers x y -> goat_setLayersStateWithChangesFromToggleHide x y goatState
     HOA_Preview p -> goat_applyWSEvent WSEventType_Local_NoRefresh (WSEApplyPreview dummyShepard dummyShift p) goatState
     HOA_Nothing -> goatState
@@ -650,16 +664,11 @@
 goat_processCanvasHandlerOutput pho goatState = r where
   canvasSelection = computeCanvasSelection goatState
 
-  wasNoAction = handlerOutputAction_isNothing $ _potatoHandlerOutput_action pho
-  -- TODO you can DELETE workspace stuff here once all handlers properly return commit actions when they are done
-  -- you need this right now otherwise, for example, drag creating a box via BoxHandler will not commit after mouseup as it returns (handler: Nothing, action: HOA_Nothing)
-  (next_handler, next_workspace) = case _potatoHandlerOutput_nextHandler pho of
-    -- if we replaced the handler and there was HOA_Nothing, commit its local preview if there was one
-    Nothing -> (makeHandlerFromSelection canvasSelection, if wasNoAction then maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState else _goatState_workspace goatState)
-    Just x -> (x, _goatState_workspace goatState)
+  next_handler = case _potatoHandlerOutput_nextHandler pho of
+    Nothing -> makeHandlerFromSelection canvasSelection
+    Just x -> x
   goatState' = goatState { 
       _goatState_handler = next_handler 
-      , _goatState_workspace = next_workspace
     }
   r = goat_processHandlerOutput_noSetHandler pho $ goatState'
 
@@ -761,7 +770,7 @@
   -- NO that's not true because in those cases you can return an explicit commit action or you return HOA_Nothing which should also commit when the handler is replaced
   (next_handler, next_workspace) = if (not . handlerActiveState_isActive) (pIsHandlerActive (_goatState_handler goatState_afterSetLayersState)) && not (goatState_hasLocalPreview goatState_afterSetLayersState)
     -- if we replaced the handler, commit its local preview if there was one
-    then trace "COMMIT: " $ (makeHandlerFromSelection next_canvasSelection, maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState_afterSetLayersState)
+    then (makeHandlerFromSelection next_canvasSelection, maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState_afterSetLayersState)
     else (_goatState_handler goatState_afterSetLayersState, _goatState_workspace goatState_afterSetLayersState)
   goatState_afterSetHandler = goatState_afterSetLayersState {
       _goatState_handler = next_handler
diff --git a/src/Potato/Flow/Controller/Handler.hs b/src/Potato/Flow/Controller/Handler.hs
--- a/src/Potato/Flow/Controller/Handler.hs
+++ b/src/Potato/Flow/Controller/Handler.hs
@@ -14,7 +14,7 @@
 import           Potato.Flow.Render
 import           Potato.Flow.OwlState
 import           Potato.Flow.OwlWorkspace
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import qualified Potato.Flow.Preview as Preview
 
 import qualified Potato.Data.Text.Zipper          as TZ
@@ -51,8 +51,6 @@
 
     , _potatoHandlerOutput_action                  :: HandlerOutputAction
   } deriving (Show)
-
-
 
 instance Default PotatoHandlerOutput where
   def = PotatoHandlerOutput {
diff --git a/src/Potato/Flow/Controller/Manipulator/Box.hs b/src/Potato/Flow/Controller/Manipulator/Box.hs
--- a/src/Potato/Flow/Controller/Manipulator/Box.hs
+++ b/src/Potato/Flow/Controller/Manipulator/Box.hs
@@ -7,14 +7,7 @@
 {-# OPTIONS_GHC -fno-warn-unused-record-wildcards #-}
 
 
-module Potato.Flow.Controller.Manipulator.Box (
-  BoxHandleType(..)
-  , BoxHandler(..)
-  , BoxCreationType(..)
-  , makeHandleBox
-  , makeDeltaBox
-  --, MouseManipulator(..)
-) where
+module Potato.Flow.Controller.Manipulator.Box where
 
 import           Relude
 
@@ -25,8 +18,8 @@
 import           Potato.Flow.Controller.Manipulator.Common
 import           Potato.Flow.Controller.Types
 import           Potato.Flow.Math
-import           Potato.Flow.SEltMethods
-import           Potato.Flow.SElts
+import           Potato.Flow.Methods.SEltMethods
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import           Potato.Flow.OwlItem
 import Potato.Flow.Owl
@@ -156,6 +149,9 @@
 data BoxHandler = BoxHandler {
 
     _boxHandler_handle      :: BoxHandleType -- the current handle we are dragging
+
+    -- TODO this is wrong as makeDragOperation does not always return a Llama
+    -- rename this to mouseActive or something
     , _boxHandler_undoFirst :: Bool
 
     -- with this you can use same code for both create and manipulate (create the handler and immediately pass input to it)
@@ -164,6 +160,9 @@
 
     , _boxHandler_downOnLabel :: Bool
 
+
+    , _boxHandler_prevDeltaLBox :: Maybe DeltaLBox
+
   } deriving (Show)
 
 makeDragDeltaBox :: BoxHandleType -> RelMouseDrag -> DeltaLBox
@@ -178,18 +177,68 @@
 
   r = makeDeltaBox bht boxRestrictedDelta
 
+-- reduces the DeltaLBox such that the LBox does not invert
+-- assumes LBox is canonical and that LBox is not already smaller than the desired constrained size
+constrainDeltaLBox :: Int -> DeltaLBox -> LBox -> DeltaLBox
+constrainDeltaLBox minsize d1@(DeltaLBox (V2 dx dy) (V2 dw dh)) d2@((LBox (V2 x y) (V2 w h))) = r where
+  optuple e = (e, -e)
+
+  (ndx, ndw) = if dx /= 0 
+    then optuple (min (w-minsize) dx)
+    else (dx, (max minsize (w+dw)) - w)
+  
+  (ndy, ndh) = if dy /= 0
+    then optuple (min (h-minsize) dy)
+    else (dy, (max minsize (h+dh)) - h)
+
+  istranslateonly = dw == 0 && dh == 0
+  
+  r = if istranslateonly 
+    then d1 
+    else DeltaLBox (V2 ndx ndy) (V2 ndw ndh)
+
+-- OR you remove the delta portion that already modified the box in preview
+makeDragOperationNew :: PotatoHandlerInput -> DeltaLBox -> Maybe Llama
+makeDragOperationNew phi dbox = op where
+  selection = transformableSelection phi
+  selectionl = toList $ transformableSelection phi
+  pfs = _potatoHandlerInput_pFState phi
+  lboxes = fmap (\sowl -> _sEltDrawer_box (getDrawer . hasOwlItem_toOwlSubItem $ sowl) pfs) selectionl
+
+  -- go through each element in selection and ensure that dbox does not invert that element
+  -- DANGER you need to make sure you have sensible bounding box functions or you might put things in a non-resizeable state
+  constraineddbox = foldl' (constrainDeltaLBox 1) dbox lboxes
+
+  fmapfn sowl = makeSetLlama (rid, newselt) where
+    rid = _superOwl_id sowl
+    oldselt = superOwl_toSElt_hack sowl
+    -- TODO don't use the CBoundingBox version of that funciton, it's deprecated, write a new one.
+    newselt = modify_sElt_with_cBoundingBox True oldselt (CBoundingBox constraineddbox)
+
+  op = if Seq.null selection
+    then Nothing
+    else Just $ makeCompositionLlama . toList $ (fmap fmapfn selectionl)
+
+
 makeDragOperation :: PotatoHandlerInput -> DeltaLBox -> Maybe Llama
 makeDragOperation phi dbox = op where
   selection = transformableSelection phi
+  selectionl = toList $ transformableSelection phi
+  pfs = _potatoHandlerInput_pFState phi
+  lboxes = fmap (\sowl -> _sEltDrawer_box (getDrawer . hasOwlItem_toOwlSubItem $ sowl) pfs) selectionl
 
+  -- go through each element in selection and ensure that dbox does not invert that element
+  -- DANGER you need to make sure you have sensible bounding box functions or you might put things in a non-resizeable state
+  constraineddbox = foldl' (constrainDeltaLBox 0) dbox lboxes
+
   makeController _ = cmd where
     cmd = CTagBoundingBox :=> (Identity $ CBoundingBox {
-      _cBoundingBox_deltaBox = dbox
+      _cBoundingBox_deltaBox = dbox -- constraineddbox
     })
 
   op = if Seq.null selection
     then Nothing
-    else Just $ makePFCLlama . OwlPFCManipulate $ IM.fromList (fmap (\s -> (_superOwl_id s, makeController s)) (toList selection))
+    else Just $ makePFCLlama . OwlPFCManipulate $ IM.fromList (fmap (\s -> (_superOwl_id s, makeController s)) selectionl)
 
 -- TODO split this handler in two handlers
 -- one for resizing selection (including boxes)
@@ -201,6 +250,7 @@
       , _boxHandler_creation = BoxCreationType_None
       , _boxHandler_active = False
       , _boxHandler_downOnLabel = False
+      , _boxHandler_prevDeltaLBox = Nothing
       -- TODO whatever
       --, _boxHandler_wasDragged = False
     }
@@ -223,6 +273,8 @@
     then True
     else False
 
+minusDeltaLBox :: DeltaLBox -> DeltaLBox -> DeltaLBox
+minusDeltaLBox (DeltaLBox (V2 dx1 dy1) (V2 dw1 dh1)) (DeltaLBox (V2 dx2 dy2) (V2 dw2 dh2)) = DeltaLBox (V2 (dx1-dx2) (dy1-dy2)) (V2 (dw1-dw2) (dh1-dh2))
 
 instance PotatoHandler BoxHandler where
   pHandlerName _ = handlerName_box
@@ -295,16 +347,18 @@
         BoxCreationType_TextArea -> "<textarea>"
         _ -> error "invalid BoxCreationType"
 
+      mdd = makeDragDeltaBox _boxHandler_handle rmd
 
       mop = case _boxHandler_creation of
         x | x == BoxCreationType_Box || x == BoxCreationType_Text -> Just $ makeAddEltLlama _potatoHandlerInput_pFState newEltPos (OwlItem (OwlInfo nameToAdd) (OwlSubItemBox boxToAdd))
         BoxCreationType_TextArea -> Just $ makeAddEltLlama _potatoHandlerInput_pFState newEltPos (OwlItem (OwlInfo nameToAdd) (OwlSubItemTextArea textAreaToAdd))
-        _ -> makeDragOperation phi (makeDragDeltaBox _boxHandler_handle rmd)
+        _ -> makeDragOperationNew phi (minusDeltaLBox mdd (fromMaybe (DeltaLBox 0 0) _boxHandler_prevDeltaLBox))
 
       newbh = bh {
           _boxHandler_undoFirst = True
           -- if we drag, we are no longer in label case
           , _boxHandler_downOnLabel = False
+          , _boxHandler_prevDeltaLBox = Just mdd
         }
 
       -- NOTE, that if we did create a new box, it wil get auto selected and a new BoxHandler will be created for it
@@ -357,7 +411,11 @@
 
           -- This clears the handler and causes selection to regenerate a new handler.
           -- Why do we do it this way instead of returning a handler? Not sure, doesn't matter.
-          else Just def
+          else Just def {
+              _potatoHandlerOutput_action = HOA_Preview Preview_MaybeCommit
+              -- doesn't work, see comments where _boxHandler_undoFirst is defined
+              --_potatoHandlerOutput_action = if _boxHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing
+            }
 
         -- TODO if this was a text box creation case, consider entering text edit mode
 
@@ -388,7 +446,7 @@
       else case mmove of
         Nothing -> Nothing
         Just move -> Just r2 where
-          mop = makeDragOperation phi move
+          mop = makeDragOperationNew phi move
           r2 = def {
               _potatoHandlerOutput_nextHandler = Just $ SomePotatoHandler bh
               , _potatoHandlerOutput_action = case mop of
diff --git a/src/Potato/Flow/Controller/Manipulator/BoxText.hs b/src/Potato/Flow/Controller/Manipulator/BoxText.hs
--- a/src/Potato/Flow/Controller/Manipulator/BoxText.hs
+++ b/src/Potato/Flow/Controller/Manipulator/BoxText.hs
@@ -24,7 +24,7 @@
 import           Potato.Flow.Controller.Input
 import           Potato.Flow.Controller.Manipulator.Common
 import           Potato.Flow.Math
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import Potato.Flow.Owl
 import Potato.Flow.OwlWorkspace
diff --git a/src/Potato/Flow/Controller/Manipulator/Common.hs b/src/Potato/Flow/Controller/Manipulator/Common.hs
--- a/src/Potato/Flow/Controller/Manipulator/Common.hs
+++ b/src/Potato/Flow/Controller/Manipulator/Common.hs
@@ -16,7 +16,7 @@
 
 import           Potato.Flow.Controller.Types
 import           Potato.Flow.Math
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.OwlItem
 import Potato.Flow.Owl
 import Potato.Flow.DebugHelpers
diff --git a/src/Potato/Flow/Controller/Manipulator/Layers.hs b/src/Potato/Flow/Controller/Manipulator/Layers.hs
--- a/src/Potato/Flow/Controller/Manipulator/Layers.hs
+++ b/src/Potato/Flow/Controller/Manipulator/Layers.hs
@@ -18,7 +18,7 @@
 import           Potato.Flow.OwlItem
 import           Potato.Flow.OwlState
 import           Potato.Flow.OwlWorkspace
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import           Potato.Flow.Preview
 
diff --git a/src/Potato/Flow/Controller/Manipulator/Line.hs b/src/Potato/Flow/Controller/Manipulator/Line.hs
--- a/src/Potato/Flow/Controller/Manipulator/Line.hs
+++ b/src/Potato/Flow/Controller/Manipulator/Line.hs
@@ -24,7 +24,7 @@
 import           Potato.Flow.OwlItem
 import           Potato.Flow.OwlState
 import           Potato.Flow.OwlWorkspace
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import Potato.Flow.Methods.LlamaWorks
 import Potato.Flow.Preview
 
@@ -182,7 +182,7 @@
 
 -- |
 -- see indexing information in 'whichSubSegmentDidClick'
-getAnchorPosition :: Bool -> OwlPFState -> SAutoLine -> Int -> XY
+getAnchorPosition :: (HasCallStack) => Bool -> OwlPFState -> SAutoLine -> Int -> XY
 getAnchorPosition offsetAttach pfs sline@SAutoLine {..} anchorindex = r where
   mps = _sAutoLine_midpoints
   endindex = length mps + 1
@@ -191,7 +191,7 @@
     else if anchorindex == endindex
       then getEndpointPosition offsetAttach pfs sline False
       else if anchorindex > 0 && anchorindex < endindex
-        then case mps L.!! (anchorindex-1) of
+        then case mps `debugBangBang` (anchorindex-1) of
           SAutoLineConstraintFixed xy -> xy
         else error $ "out of bounds anchor index " <> show anchorindex
 
@@ -440,7 +440,9 @@
             , _potatoHandlerOutput_action = HOA_Preview $ Preview (previewOperation_fromUndoFirst _autoLineEndPointHandler_undoFirst) op
           }
       -- no need to return AutoLineHandler, it will be recreated from selection by goat
-      MouseDragState_Up -> Just def
+      MouseDragState_Up -> Just def {
+          _potatoHandlerOutput_action = if _autoLineEndPointHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing
+        }
       MouseDragState_Cancelled -> if _autoLineEndPointHandler_undoFirst then Just def { _potatoHandlerOutput_action = HOA_Preview Preview_Cancel } else Just def
 
   pHandleKeyboard _ PotatoHandlerInput {..} _ = Nothing
@@ -591,7 +593,9 @@
           , _potatoHandlerOutput_action = HOA_Preview $ Preview (previewOperation_fromUndoFirst _autoLineMidPointHandler_undoFirst) event
         }
     -- no need to return AutoLineHandler, it will be recreated from selection by goat
-    MouseDragState_Up -> Just def
+    MouseDragState_Up -> Just def {
+        _potatoHandlerOutput_action = if _autoLineMidPointHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing
+      }
     MouseDragState_Cancelled -> if _autoLineMidPointHandler_undoFirst then Just def { _potatoHandlerOutput_action = HOA_Preview Preview_Cancel } else Just def
   pRenderHandler AutoLineMidPointHandler {..} phi@PotatoHandlerInput {..} = r where
     boxes = maybeRenderPoints (False, False) _autoLineMidPointHandler_offsetAttach _autoLineMidPointHandler_midPointIndex phi
@@ -640,9 +644,6 @@
           }
 
       MouseDragState_Up -> Just def {
-
-          -- TODO Preview_Commit 
-
           -- go back to AutoLineLabelHandler on completion
           _potatoHandlerOutput_nextHandler = if not _autoLineLabelMoverHandler_undoFirst
             -- if _autoLineLabelMoverHandler_undoFirst is false, this means we didn't drag at all, in which case go to label edit handler
@@ -650,6 +651,8 @@
               makeAutoLineLabelHandler_from_labelIndex _autoLineLabelMoverHandler_labelIndex _autoLineLabelMoverHandler_prevHandler phi rmd
             -- TODO consider also going into edit handler after dragging an endpoint, but for now, just go back to the previous handler (which will be AutoLineHandler)
             else Just (_autoLineLabelMoverHandler_prevHandler)
+
+          , _potatoHandlerOutput_action = if _autoLineLabelMoverHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing
         }
 
       MouseDragState_Cancelled -> Just def {
diff --git a/src/Potato/Flow/Controller/Manipulator/Select.hs b/src/Potato/Flow/Controller/Manipulator/Select.hs
--- a/src/Potato/Flow/Controller/Manipulator/Select.hs
+++ b/src/Potato/Flow/Controller/Manipulator/Select.hs
@@ -21,7 +21,7 @@
 import           Potato.Flow.OwlItem
 import           Potato.Flow.OwlState
 import Potato.Flow.RenderCache
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 
 import           Control.Exception                      (assert)
 import           Data.Default
diff --git a/src/Potato/Flow/Controller/Manipulator/TextArea.hs b/src/Potato/Flow/Controller/Manipulator/TextArea.hs
--- a/src/Potato/Flow/Controller/Manipulator/TextArea.hs
+++ b/src/Potato/Flow/Controller/Manipulator/TextArea.hs
@@ -17,7 +17,7 @@
 import           Potato.Flow.Math
 import           Potato.Flow.Owl
 import           Potato.Flow.OwlWorkspace
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import Potato.Flow.Preview
 
diff --git a/src/Potato/Flow/Controller/Manipulator/TextInputState.hs b/src/Potato/Flow/Controller/Manipulator/TextInputState.hs
--- a/src/Potato/Flow/Controller/Manipulator/TextInputState.hs
+++ b/src/Potato/Flow/Controller/Manipulator/TextInputState.hs
@@ -5,7 +5,7 @@
 import Relude
 
 import           Potato.Flow.Math
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Controller.Input
 import           Potato.Flow.Controller.Handler
 
diff --git a/src/Potato/Flow/Controller/OwlLayers.hs b/src/Potato/Flow/Controller/OwlLayers.hs
--- a/src/Potato/Flow/Controller/OwlLayers.hs
+++ b/src/Potato/Flow/Controller/OwlLayers.hs
@@ -9,7 +9,7 @@
 
 import           Potato.Flow.Controller.Types
 import           Potato.Flow.Types
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 import           Potato.Flow.OwlItem
 import Potato.Flow.Owl
 import Potato.Flow.OwlState
@@ -136,8 +136,12 @@
     r = "LayersState: "
       <> show _layersState_meta
       <> "\nLayerEntries:\n"
-      <> showFoldable _layersState_entries
+      <> layerEntriesToPrettyText _layersState_entries
 
+--instance Show LayersState where
+--  show ls = T.unpack (potatoShow ls)
+
+
 data LockHideCollapseOp = LHCO_ToggleLock | LHCO_ToggleHide | LHCO_ToggleCollapse deriving (Show)
 
 alterWithDefault :: (Eq a, Default a) => (a -> a) -> REltId -> REltIdMap a -> REltIdMap a
@@ -199,7 +203,7 @@
   le = Seq.index _layersState_entries lepos
   lerid = layerEntry_rEltId le
   ledepth = layerEntry_depth le
-  childFrom nextLayerEntry = layerEntry_depth nextLayerEntry /= ledepth
+  childFrom nextLayerEntry = layerEntry_depth nextLayerEntry > ledepth
   -- visible children of le
   childles = Seq.takeWhileL childFrom . Seq.drop (lepos+1) $ _layersState_entries
   -- everything before le
diff --git a/src/Potato/Flow/Controller/Types.hs b/src/Potato/Flow/Controller/Types.hs
--- a/src/Potato/Flow/Controller/Types.hs
+++ b/src/Potato/Flow/Controller/Types.hs
@@ -21,7 +21,7 @@
 
 import           Potato.Flow.Math
 import           Potato.Flow.Owl
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 
 import           Data.Aeson
diff --git a/src/Potato/Flow/DebugHelpers.hs b/src/Potato/Flow/DebugHelpers.hs
--- a/src/Potato/Flow/DebugHelpers.hs
+++ b/src/Potato/Flow/DebugHelpers.hs
@@ -25,8 +25,8 @@
   then b
   else error $ "assert failed:\n" <> potatoShow a
 
-traceWith :: (a -> String) -> a -> a
-traceWith f x = trace (f x) x
+traceIdWith :: (a -> String) -> a -> a
+traceIdWith f x = trace (f x) x
 
 traceShowIdWithLabel :: (Show a) => String -> a -> a
 traceShowIdWithLabel label x = trace (label <> ": " <> show x) x
diff --git a/src/Potato/Flow/Deprecated/State.hs b/src/Potato/Flow/Deprecated/State.hs
--- a/src/Potato/Flow/Deprecated/State.hs
+++ b/src/Potato/Flow/Deprecated/State.hs
@@ -40,8 +40,8 @@
 
 import           Potato.Flow.Deprecated.Layers
 import           Potato.Flow.Math
-import           Potato.Flow.SEltMethods
-import           Potato.Flow.SElts
+import           Potato.Flow.Methods.SEltMethods
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 
 import           Control.Exception       (assert)
diff --git a/src/Potato/Flow/Llama.hs b/src/Potato/Flow/Llama.hs
--- a/src/Potato/Flow/Llama.hs
+++ b/src/Potato/Flow/Llama.hs
@@ -10,7 +10,7 @@
 import           Potato.Flow.Owl
 import           Potato.Flow.OwlItem
 import           Potato.Flow.OwlState
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 
 
diff --git a/src/Potato/Flow/Methods/LineDrawer.hs b/src/Potato/Flow/Methods/LineDrawer.hs
--- a/src/Potato/Flow/Methods/LineDrawer.hs
+++ b/src/Potato/Flow/Methods/LineDrawer.hs
@@ -38,7 +38,8 @@
 import           Potato.Flow.Methods.Types
 import           Potato.Flow.Owl
 import           Potato.Flow.OwlItem
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
+import Potato.Flow.DebugHelpers
 
 import qualified Data.List                      as L
 import qualified Data.List.Index                as L
@@ -691,13 +692,13 @@
 -- the SAutoLineLabel does not have to be one of labels contained in the SAutoLine _sAutoLine_labels
 -- which is useful for positioning SAutoLineLabel before adding them to SAutoLine
 -- however the midpoint index in SAutoLineLabel is expected to map correctly to the SAutoLine
-getSAutoLineLabelPosition :: (HasOwlTree a) => a -> SAutoLine -> SAutoLineLabel -> XY
+getSAutoLineLabelPosition :: (HasCallStack, HasOwlTree a) => a -> SAutoLine -> SAutoLineLabel -> XY
 getSAutoLineLabelPosition ot sal sall = getSAutoLineLabelPositionFromLineAnchorsForRender lar sal sall where
-  lar = sAutoLine_to_lineAnchorsForRenderList ot sal L.!! (_sAutoLineLabel_index sall)
+  lar = sAutoLine_to_lineAnchorsForRenderList ot sal `debugBangBang` (_sAutoLineLabel_index sall)
 
 -- get SAutoLineLabel positions in visual order (which may not be the same as logical order)
 -- return includes SAutoLineLabel and its original logical index for convenience
-getSortedSAutoLineLabelPositions :: (HasOwlTree a) => a -> SAutoLine -> [(XY, Int, SAutoLineLabel)]
+getSortedSAutoLineLabelPositions :: (HasCallStack, HasOwlTree a) => a -> SAutoLine -> [(XY, Int, SAutoLineLabel)]
 getSortedSAutoLineLabelPositions ot sal@SAutoLine {..} = r where
   sortfn (_,a) (_,b) = case compare (_sAutoLineLabel_index a) (_sAutoLineLabel_index b) of
     EQ -> case _sAutoLineLabel_position a of
@@ -708,7 +709,7 @@
 
   larlist = sAutoLine_to_lineAnchorsForRenderList ot sal
 
-  r = fmap (\(i, sall) -> (internal_getSAutoLineLabelPosition (larlist L.!! _sAutoLineLabel_index sall) sal sall, i, sall)) sortedlls
+  r = fmap (\(i, sall) -> (internal_getSAutoLineLabelPosition (larlist `debugBangBang` _sAutoLineLabel_index sall) sal sall, i, sall)) sortedlls
 
 
 -- takes a list of line anchors as returned by sAutoLine_to_lineAnchorsForRenderList and a position
diff --git a/src/Potato/Flow/Methods/LineTypes.hs b/src/Potato/Flow/Methods/LineTypes.hs
--- a/src/Potato/Flow/Methods/LineTypes.hs
+++ b/src/Potato/Flow/Methods/LineTypes.hs
@@ -5,7 +5,7 @@
 import           Relude
 
 import           Potato.Flow.Math
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 
 import Linear.Vector ((^*))
 import Linear.Matrix (M22, (!*))
diff --git a/src/Potato/Flow/Methods/LlamaWorks.hs b/src/Potato/Flow/Methods/LlamaWorks.hs
--- a/src/Potato/Flow/Methods/LlamaWorks.hs
+++ b/src/Potato/Flow/Methods/LlamaWorks.hs
@@ -14,7 +14,7 @@
 import Potato.Flow.OwlWorkspace
 import Potato.Flow.OwlState
 import Potato.Flow.Llama
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 
 import           Control.Exception (assert)
diff --git a/src/Potato/Flow/Methods/SEltMethods.hs b/src/Potato/Flow/Methods/SEltMethods.hs
new file mode 100644
--- /dev/null
+++ b/src/Potato/Flow/Methods/SEltMethods.hs
@@ -0,0 +1,384 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Potato.Flow.Methods.SEltMethods where
+
+import           Relude
+
+import           Potato.Flow.Math
+import           Potato.Flow.Methods.LineDrawer
+import           Potato.Flow.Methods.TextCommon
+import           Potato.Flow.Methods.Types
+import           Potato.Flow.Owl
+import Potato.Flow.RenderCache
+import           Potato.Flow.OwlItem
+import           Potato.Flow.Serialization.Snake
+import           Potato.Flow.Types
+
+import           Data.Dependent.Sum             (DSum ((:=>)))
+import qualified Data.Map                       as Map
+import           Data.Maybe                     (fromJust)
+import qualified Data.Text                      as T
+import qualified Potato.Data.Text.Zipper        as TZ
+import Control.Exception (assert)
+
+
+-- DisplayLines tag is Int, 0 for no cursor 1 for cursor
+noTrailngCursorDisplayLines :: Int -> TextAlign -> T.Text -> TZ.DisplayLines Int
+noTrailngCursorDisplayLines width alignment text = r where
+  -- force TZ to top so that displayLinesWithAlignment doesn't create trailing space for cursor
+  tz = TZ.top (TZ.fromText text)
+
+  -- hack to get rid of trailing cursor if text is ""
+  r = if T.null text
+    then TZ.DisplayLines {
+        _displayLines_spans = []
+        , _displayLines_offsetMap = Map.empty
+        , _displayLines_cursorPos   = (0,0)
+      }
+    else TZ.displayLinesWithAlignment (convertTextAlignToTextZipperTextAlignment alignment) width 0 1 tz
+
+makeDisplayLinesFromSBox :: SBox -> TZ.DisplayLines Int
+makeDisplayLinesFromSBox sbox = r where
+  alignment = _textStyle_alignment . _sBoxText_style . _sBox_text $ sbox
+  text = _sBoxText_text . _sBox_text $ sbox
+  LBox _ (V2 width' _) = _sBox_box sbox
+  width = case _sBox_boxType sbox of
+    SBoxType_BoxText   -> max 0 (width'-2)
+    SBoxType_NoBoxText -> width'
+    _                  -> error "wrong type"
+  r = noTrailngCursorDisplayLines width alignment text
+
+
+
+-- TODO DELETE use doesOwlSubItemIntersectBox instead
+doesSEltIntersectBox_DEPRECATED :: LBox -> SElt -> Bool
+doesSEltIntersectBox_DEPRECATED lbox selt = case selt of
+  SEltNone                     -> False
+  SEltFolderStart              -> False
+  SEltFolderEnd                -> False
+  SEltBox x                    -> does_lBox_intersect_include_zero_area lbox (_sBox_box x)
+  SEltTextArea x                   -> does_lBox_intersect_include_zero_area lbox (_sTextArea_box x)
+  -- TODO this is wrong, do it correctly...
+  -- we use does_lBox_intersect since it's impossible for a SAutoLine to have zero sized box
+  SEltLine sline -> does_lBox_intersect lbox (fromJust $ getSEltBox_naive (SEltLine sline))
+
+doesSEltIntersectPoint :: XY -> SElt -> Bool
+doesSEltIntersectPoint pos selt = doesSEltIntersectBox_DEPRECATED (LBox pos (V2 1 1)) selt
+
+getSEltSuperStyle :: SElt -> Maybe SuperStyle
+getSEltSuperStyle selt = case selt of
+  SEltBox SBox {..}       -> Just _sBox_superStyle
+  SEltLine SAutoLine {..} -> Just _sAutoLine_superStyle
+  _                       -> Nothing
+
+getSEltLabelSuperStyle :: SEltLabel -> Maybe SuperStyle
+getSEltLabelSuperStyle (SEltLabel _ x) = getSEltSuperStyle x
+
+getSEltLineStyle :: SElt -> Maybe LineStyle
+getSEltLineStyle selt = case selt of
+  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyle
+  _                       -> Nothing
+
+getSEltLineStyleEnd :: SElt -> Maybe LineStyle
+getSEltLineStyleEnd selt = case selt of
+  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyleEnd
+  _                       -> Nothing
+
+getSEltLabelLineStyle :: SEltLabel -> Maybe LineStyle
+getSEltLabelLineStyle (SEltLabel _ x) = getSEltLineStyle x
+
+getSEltLabelLineStyleEnd :: SEltLabel -> Maybe LineStyle
+getSEltLabelLineStyleEnd (SEltLabel _ x) = getSEltLineStyleEnd x
+
+
+getSEltBoxTextStyle :: SElt -> Maybe TextStyle
+getSEltBoxTextStyle = \case
+  SEltBox SBox {..}         -> Just . _sBoxText_style $ _sBox_text
+  _ -> Nothing
+
+getSEltLabelBoxTextStyle :: SEltLabel -> Maybe TextStyle
+getSEltLabelBoxTextStyle (SEltLabel _ x) = getSEltBoxTextStyle x
+
+getSEltBoxType :: SElt -> Maybe SBoxType
+getSEltBoxType = \case
+  SEltBox SBox {..} -> Just _sBox_boxType
+  _ -> Nothing
+
+getSEltLabelBoxType :: SEltLabel -> Maybe SBoxType
+getSEltLabelBoxType (SEltLabel _ x) = getSEltBoxType x
+
+sBox_drawer :: SBox -> SEltDrawer
+sBox_drawer sbox@SBox {..} = r where
+  CanonicalLBox _ _ lbox@(LBox (V2 x y) (V2 w h)) = canonicalLBox_from_lBox _sBox_box
+
+  titlewidth = max 0 (w-2)
+
+  fillfn _ = case _superStyle_fill _sBox_superStyle of
+    FillStyle_Simple c -> Just c
+    FillStyle_Blank    -> Nothing
+
+  rfntext (V2 x' y') = case _sBox_boxType of
+    SBoxType_Box -> Nothing
+    SBoxType_NoBox -> Nothing
+    _ -> outputChar where
+
+      -- 😰😰😰 for now we just do the below for every cell
+      dl = makeDisplayLinesFromSBox sbox
+
+      offs = case _sBox_boxType of
+        SBoxType_NoBoxText -> (0,0)
+        _                  -> (1,1)
+
+      outputChar = displayLinesToChar (x, y) dl (x', y') offs
+
+  -- TODO test
+  rfnlabel (V2 x' y') = case _sBoxTitle_title _sBox_title of
+    Nothing -> Nothing
+    Just title -> outputChar where
+      -- TODO we want to crop instead of wrap here
+      -- however using infinite width trick will break AlignRight :(
+      dl = noTrailngCursorDisplayLines titlewidth (_sBoxTitle_align _sBox_title) title
+      -- note that y' will ultimately resolve to a yindex of 0 inside of displayLinesToChar
+      outputChar = displayLinesToChar (x, y) dl (x', y') (1,0)
+
+  rfnnoborder pt
+    | not (does_lBox_contains_XY lbox pt) = Nothing
+    | otherwise = case rfntext pt of
+      -- 'Just Nothing' means don't use fill char (this happens when there are wide chars)
+      Just mx -> mx
+      Nothing -> fillfn pt
+
+  rfnborder pt@(V2 x' y')
+    | not (does_lBox_contains_XY lbox pt) = Nothing
+    | w == 1 && h == 1 = _superStyle_point _sBox_superStyle
+    | w == 1 = _superStyle_vertical _sBox_superStyle
+    | h == 1 = _superStyle_horizontal _sBox_superStyle
+    | x' == x && y' == y = _superStyle_tl _sBox_superStyle
+    | x' == x && y' == y+h-1 = _superStyle_bl _sBox_superStyle
+    | x' == x+w-1 && y' == y = _superStyle_tr _sBox_superStyle
+    | x' == x+w-1 && y' == y+h-1 = _superStyle_br _sBox_superStyle
+    | x' == x || x' == x+w-1 = _superStyle_vertical _sBox_superStyle
+    -- label shows up at top horizontal portion
+    | y' == y = case rfnlabel pt of
+      Nothing    -> _superStyle_horizontal _sBox_superStyle
+      Just pchar -> pchar
+    | y' == y+h-1 = _superStyle_horizontal _sBox_superStyle
+    | otherwise = rfnnoborder pt
+
+  r = SEltDrawer {
+      _sEltDrawer_box = const lbox
+      , _sEltDrawer_renderFn = \_ -> case _sBox_boxType of
+        SBoxType_NoBoxText -> rfnnoborder
+        SBoxType_NoBox     -> rfnnoborder
+        _                  -> rfnborder
+      
+      -- TODO 
+      , _sEltDrawer_maxCharWidth = 1
+    }
+
+sTextArea_drawer :: STextArea -> SEltDrawer
+sTextArea_drawer STextArea {..} = r where
+
+  lbox@(LBox p _) = _sTextArea_box
+
+  renderfn p' = outputChar where
+    inbounds = does_lBox_contains_XY lbox p'
+    outputChar = if inbounds
+      then case Map.lookup (p' - p) _sTextArea_text of
+        Nothing -> if _sTextArea_transparent
+          then Nothing
+          else Just ' '
+        Just c -> Just c
+      else Nothing
+
+  r = SEltDrawer {
+      _sEltDrawer_box = const lbox
+      , _sEltDrawer_renderFn = \_ -> renderfn
+
+      -- TODO
+      , _sEltDrawer_maxCharWidth = 1
+    }
+
+-- NOTE that there is not a 1-1 mapping between `OwlSubItem` and `OwlItemCache` as the `OwlItemCache` is dependent on the OwlTree
+-- this function assumes that you are requesting the drawer with the intent of passing in an OwlTree
+-- TODO it would have been better for SEltDrawer to be created based on an OwlTree rather than return a function that takes an OwlTree
+getDrawerWithCache :: OwlSubItem -> Maybe OwlItemCache -> SEltDrawer
+getDrawerWithCache osubitem mcache = case osubitem of 
+  OwlSubItemNone        -> nilDrawer
+  OwlSubItemFolder _ -> nilDrawer
+  OwlSubItemBox sbox    -> sBox_drawer sbox
+  OwlSubItemLine sline -> case mcache of 
+    Just (OwlItemCache_Line lars _) -> sSimpleLineNewRenderFn sline (Just lars)
+    Nothing -> sSimpleLineNewRenderFn sline Nothing
+    _ -> assert False (sSimpleLineNewRenderFn sline Nothing)
+  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea
+
+-- TODO pass in cache here
+getDrawer :: OwlSubItem -> SEltDrawer
+getDrawer = \case
+  OwlSubItemNone        -> nilDrawer
+  OwlSubItemFolder _ -> nilDrawer
+  OwlSubItemBox sbox    -> sBox_drawer sbox
+  OwlSubItemLine sline -> sSimpleLineNewRenderFn sline Nothing
+  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea
+  {-
+  where
+    potatoDrawer = SEltDrawer {
+        _sEltDrawer_box = const $ fromJust (getSEltBox_naive selt)
+        , _sEltDrawer_renderFn =  makePotatoRenderer $ fromJust (getSEltBox_naive selt)
+      }
+  -}
+
+getDrawerFromSEltForTest :: SElt -> SEltDrawer
+getDrawerFromSEltForTest = getDrawer . sElt_to_owlSubItem
+
+updateOwlSubItemCache :: (HasOwlTree a) => a -> OwlSubItem -> Maybe OwlItemCache
+updateOwlSubItemCache ot x = r where
+  r = case x of
+    -- TODO use sAutoLine_to_lineAnchorsForRenderList here instead
+    (OwlSubItemLine sline) -> cache where
+      cache = Just $ OwlItemCache_Line (sSimpleLineNewRenderFnComputeCache ot sline) prerender
+      seltdrawer = getDrawerWithCache x cache
+      prerender = makePreRender ot seltdrawer 
+    _ -> Just $ OwlItemCache_Generic prerender where
+      seltdrawer = getDrawerWithCache x Nothing
+      prerender = makePreRender ot seltdrawer 
+
+
+-- TODO move modify methods to another file
+
+modify_sAutoLineConstraint_with_cBoundingBox :: Bool -> SAutoLineConstraint -> CBoundingBox -> SAutoLineConstraint
+modify_sAutoLineConstraint_with_cBoundingBox isDo constraint CBoundingBox {..} = case constraint of
+  SAutoLineConstraintFixed xy -> SAutoLineConstraintFixed $ modifyDelta isDo xy (_deltaLBox_translate _cBoundingBox_deltaBox)
+
+modify_sElt_with_cBoundingBox :: Bool -> SElt -> CBoundingBox -> SElt
+modify_sElt_with_cBoundingBox isDo selt cbb@CBoundingBox {..} = case selt of
+  SEltBox sbox  -> SEltBox $ sbox {
+      _sBox_box = modifyDelta isDo (_sBox_box sbox) _cBoundingBox_deltaBox
+    }
+  -- TODO handle resize parameter
+  SEltLine sline@SAutoLine {..} -> SEltLine $ sline {
+      _sAutoLine_start = modifyDelta isDo _sAutoLine_start
+        (_deltaLBox_translate _cBoundingBox_deltaBox)
+      , _sAutoLine_end = modifyDelta isDo _sAutoLine_end
+        (_deltaLBox_translate _cBoundingBox_deltaBox)
+      , _sAutoLine_midpoints = fmap (\slc -> modify_sAutoLineConstraint_with_cBoundingBox isDo slc cbb) _sAutoLine_midpoints
+    }
+  SEltTextArea stext -> SEltTextArea $ stext {
+      _sTextArea_box     = modifyDelta isDo (_sTextArea_box stext) _cBoundingBox_deltaBox
+    }
+  x          -> x
+
+modify_sElt_with_cSuperStyle :: Bool -> SElt -> CSuperStyle -> SElt
+modify_sElt_with_cSuperStyle isDo selt (CSuperStyle style) = case selt of
+  SEltBox sbox -> SEltBox $ sbox {
+      _sBox_superStyle = modifyDelta isDo (_sBox_superStyle sbox) style
+    }
+  -- TODO handle resize parameter
+  SEltLine sline -> SEltLine $ sline {
+      _sAutoLine_superStyle = modifyDelta isDo (_sAutoLine_superStyle sline) style
+    }
+  _ -> error $ "Controller - SElt type mismatch: CTagSuperStyle - " <> show selt
+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
+  --x -> x
+
+-- TODO DELETE use llama instead
+modify_sElt_with_cLineStyle :: Bool -> SElt -> CLineStyle -> SElt
+modify_sElt_with_cLineStyle isDo selt (CLineStyle style) = case selt of
+  SEltLine sline -> SEltLine $ sline {
+      _sAutoLine_lineStyle = modifyDelta isDo (_sAutoLine_lineStyle sline) style
+    }
+  _ -> error $ "Controller - SElt type mismatch: CTagLineStyle - " <> show selt
+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
+  --x -> x
+
+modify_sElt_with_cTextStyle :: Bool -> SElt -> CTextStyle -> SElt
+modify_sElt_with_cTextStyle isDo selt (CTextStyle style) = case selt of
+  SEltBox sbox -> SEltBox $ sbox {
+      _sBox_text = (_sBox_text sbox) {
+          _sBoxText_style = modifyDelta isDo (_sBoxText_style . _sBox_text $ sbox) style
+        }
+    }
+  _ -> error $ "Controller - SElt type mismatch: CTagBoxTextStyle - " <> show selt
+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
+  --x -> x
+
+modify_sEltBox_label_with_cTextAlign :: Bool -> SElt -> CTextAlign -> SElt
+modify_sEltBox_label_with_cTextAlign isDo selt (CTextAlign align) = case selt of
+  SEltBox sbox -> SEltBox $ sbox {
+      _sBox_title = sboxtitle {
+          _sBoxTitle_align = modifyDelta isDo (_sBoxTitle_align sboxtitle) align
+        }
+    } where
+      sboxtitle = _sBox_title sbox
+  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt
+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
+  --x -> x
+
+modify_sEltBox_label_with_cMaybeText :: Bool -> SElt -> CMaybeText -> SElt
+modify_sEltBox_label_with_cMaybeText isDo selt (CMaybeText text) = case selt of
+  SEltBox sbox -> SEltBox $ sbox {
+      _sBox_title = sboxtitle {
+          _sBoxTitle_title = modifyDelta isDo (_sBoxTitle_title sboxtitle) text
+        }
+    } where
+      sboxtitle = _sBox_title sbox
+  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt
+
+modify_sEltTextArea_with_cTextArea :: Bool -> SElt -> CTextArea -> SElt
+modify_sEltTextArea_with_cTextArea isDo selt (CTextArea dt) = case selt of
+  SEltTextArea stextarea -> SEltTextArea $ stextarea {
+      _sTextArea_text = modifyDelta isDo (_sTextArea_text stextarea) dt
+    }
+  _ -> error $ "Controller - SElt type mismatch: CTagTextArea - " <> show selt
+
+modify_sEltTextArea_with_cTextAreaToggle :: Bool -> SElt -> CTextAreaToggle -> SElt
+modify_sEltTextArea_with_cTextAreaToggle isDo selt (CTextAreaToggle toggle) = case selt of
+  -- double toggle is idempotent but we disallow it for now
+  SEltTextArea _ -> error $ "Controller - SElt type mismatch: CTagTextAreaToggle - " <> show selt
+  x -> modifyDelta isDo x toggle
+
+
+modifyDelta :: (Delta x dx) => Bool -> x ->  dx -> x
+modifyDelta isDo x dx = if isDo
+  then plusDelta x dx
+  else minusDelta x dx
+
+updateFnFromController :: Bool -> Controller -> (SEltLabel -> SEltLabel)
+updateFnFromController isDo = \case
+  (CTagRename :=> Identity d) -> \seltl -> modifyDelta isDo seltl d
+  (CTagLine :=> Identity d) -> \(SEltLabel sname selt) -> case selt of
+    SEltLine s -> SEltLabel sname (SEltLine $ modifyDelta isDo s d)
+    _ -> error $ "Controller - SElt type mismatch: CTagLine - " <> show selt
+  (CTagBoxText :=> Identity d) -> \(SEltLabel sname selt) -> case selt of
+    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)
+    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt
+  (CTagBoxType :=> Identity d) -> \(SEltLabel sname selt) -> case selt of
+    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)
+    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt
+  (CTagBoundingBox :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sElt_with_cBoundingBox isDo selt d)
+  (CTagSuperStyle :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sElt_with_cSuperStyle isDo selt d)
+  (CTagLineStyle :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sElt_with_cLineStyle isDo selt d)
+  (CTagBoxTextStyle :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sElt_with_cTextStyle isDo selt d)
+
+  (CTagBoxLabelAlignment :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sEltBox_label_with_cTextAlign isDo selt d)
+  (CTagBoxLabelText :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sEltBox_label_with_cMaybeText isDo selt d)
+
+  (CTagTextArea :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sEltTextArea_with_cTextArea isDo selt d)
+  (CTagTextAreaToggle :=> Identity d) -> \(SEltLabel sname selt) ->
+    SEltLabel sname (modify_sEltTextArea_with_cTextAreaToggle isDo selt d)
+
+
+-- | helper method used in copy pasta
+offsetSEltTree :: XY -> SEltTree -> SEltTree
+offsetSEltTree offset stree = r where
+  op = CBoundingBox (DeltaLBox offset 0)
+  offsetfn (rid, seltl) = (rid, updateFnFromController True (CTagBoundingBox :=> Identity op) seltl)
+  r = fmap offsetfn stree
diff --git a/src/Potato/Flow/Methods/TextCommon.hs b/src/Potato/Flow/Methods/TextCommon.hs
--- a/src/Potato/Flow/Methods/TextCommon.hs
+++ b/src/Potato/Flow/Methods/TextCommon.hs
@@ -6,7 +6,7 @@
 
 import           Relude
 
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 
 import qualified Data.Map as Map
 import qualified Data.Text                      as T
diff --git a/src/Potato/Flow/Methods/Types.hs b/src/Potato/Flow/Methods/Types.hs
--- a/src/Potato/Flow/Methods/Types.hs
+++ b/src/Potato/Flow/Methods/Types.hs
@@ -11,7 +11,7 @@
 
 import           Potato.Flow.Math
 import           Potato.Flow.Owl
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 
 import qualified Data.Text         as T
 
diff --git a/src/Potato/Flow/Owl.hs b/src/Potato/Flow/Owl.hs
--- a/src/Potato/Flow/Owl.hs
+++ b/src/Potato/Flow/Owl.hs
@@ -16,7 +16,7 @@
 import qualified Data.IntSet as IS
 import qualified Data.Text as T
 import Potato.Flow.OwlItem
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 import Potato.Flow.Types
 import Potato.Flow.DebugHelpers
 
@@ -611,6 +611,7 @@
               (rpass, rmsg) = (rpass1 && rpass2, rmsg2)
         r = Seq.foldlWithIndex foldfn (True, msg0) kiddos
 
+-- oops, this should have been -1 but it's 0 so we start indexing stuff at 1 🤷🏼‍♀️
 owlTree_maxId :: OwlTree -> REltId
 owlTree_maxId s = maybe 0 fst (IM.lookupMax (_owlTree_mapping s))
 
@@ -781,21 +782,25 @@
   where
     r = owlParliament_toSuperOwlParliament od . OwlParliament $ _owlTree_topOwls
 
-owlTree_removeREltId :: REltId -> OwlTree -> OwlTree
-owlTree_removeREltId rid od = owlTree_removeSuperOwl (owlTree_mustFindSuperOwl od rid) od
+owlTree_removeREltId :: Bool -> REltId -> OwlTree -> OwlTree
+owlTree_removeREltId keepChildren rid od = owlTree_removeSuperOwl keepChildren (owlTree_mustFindSuperOwl od rid) od
 
-owlTree_removeSuperOwl :: SuperOwl -> OwlTree -> OwlTree
-owlTree_removeSuperOwl sowl OwlTree {..} = r
+owlTree_removeSuperOwl :: Bool -> SuperOwl -> OwlTree -> OwlTree
+owlTree_removeSuperOwl keepChildren sowl OwlTree {..} = r
   where
     -- remove the element itself
     newMapping'' = IM.delete (_superOwl_id sowl) _owlTree_mapping
 
-    -- remove all children recursively
+
+    -- remove all children recursively if desired
+    -- NOTE if keepChildren is true, this will put the OwlTree in an invalid state (presumably so that you can fix it later)
     removeEltWithoutAdjustMommyFn rid mapping = case IM.lookup rid mapping of
       Nothing -> error $ errorMsg_owlMapping_lookupFail mapping rid
       Just (_, OwlItem _ (OwlSubItemFolder kiddos)) -> foldr removeEltWithoutAdjustMommyFn (IM.delete rid mapping) kiddos
       Just _ -> IM.delete rid mapping
-    newMapping' = case _superOwl_elt sowl of
+    newMapping' = if keepChildren
+      then newMapping''
+      else case _superOwl_elt sowl of
       OwlItem _ (OwlSubItemFolder kiddos) -> foldr removeEltWithoutAdjustMommyFn newMapping'' kiddos
       _ -> newMapping''
 
@@ -841,7 +846,7 @@
     -- NOTE, that _owlItemMeta_position in sowls may be incorrect in the middle of this fold
     -- this forces us to do linear search in the owlTree_removeSuperOwl call rather than use sibling position as index into children D:
     -- TODO fix by always sort from right to left to avoid this
-    removedOd = foldl (\acc sowl -> owlTree_removeSuperOwl sowl acc) od sowls
+    removedOd = foldl (\acc sowl -> owlTree_removeSuperOwl False sowl acc) od sowls
 
     -- WIP start
     -- ??? I can't remember what this is anymore, did I aready fix this or no? Pretty sure I can just delet all of this
@@ -872,10 +877,12 @@
       . filter ((== _owlSpot_parent) . _owlItemMeta_parent . _superOwl_meta)
       . toList
       $ sowls
+
     -- list of all siblings on the spot we are dragging to
     origSiblings = fromMaybe (error "expected siblings") $ if _owlSpot_parent == noOwl
       then mommyOwl_kiddos $ od
       else mommyOwl_kiddos $ owlTree_mustFindSuperOwl od _owlSpot_parent
+
     -- now we will walk from right to left picking out the first elt that is on or after the target spot we are dragging to (_owlSpot_leftSibling) and isn't in the removed list
     findPos ::
       REltId -- ^ original _owlSpot_leftSibling
@@ -896,9 +903,11 @@
       else if x == y
         then findPos targetrid xs ys past
         else findPos targetrid (x:xs) ys past
+
     newLeftSibling = case _owlSpot_leftSibling of
       Nothing -> Nothing
       Just target -> findPos target (reverse $ toList removed) (reverse $ toList origSiblings) False
+
     correctedSpot = spot { _owlSpot_leftSibling = newLeftSibling}
 
     selttree = superOwlParliament_toSEltTree od sop
@@ -961,16 +970,28 @@
       x -> x
 
   -- go from left to right such that parents/left siblings are added first
-  r = mapAccumL mapaccumlfn od0 $ toList $ fmap (\sowl -> (owlTree_owlItemMeta_toOwlSpot miniot (_superOwl_meta sowl), sowl)) (owliterateall miniot)
+  (newot, addedSowls) = mapAccumL mapaccumlfn od0 $ toList $ fmap (\sowl -> (owlTree_owlItemMeta_toOwlSpot miniot (_superOwl_meta sowl), sowl)) (owliterateall miniot)
+  r = (newot, addedSowls)
 
--- parents NOT allowed :O
-internal_owlTree_addOwlItem :: OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, SuperOwl)
-internal_owlTree_addOwlItem OwlSpot {..} rid oitem OwlTree {..} = assert nochildrenifaddingfolder r
+
+internal_owlTree_recalculateChildrenDepths_recursive :: OwlTree -> REltId -> OwlTree
+internal_owlTree_recalculateChildrenDepths_recursive ot rid = r_0 where
+  sowl = owlTree_mustFindSuperOwl ot rid
+  adjustdepthfn (oem, oitem) = (,oitem) $ oem { _owlItemMeta_depth = _owlItemMeta_depth (_superOwl_meta sowl) + 1 }
+  r_0 = case _superOwl_elt sowl of
+    OwlItem oinfo (OwlSubItemFolder kiddos) -> r_1 where
+      -- update the depth of all its kiddos
+      omwithadjusteddepth = foldr (\kid om -> IM.adjust adjustdepthfn kid om) (_owlTree_mapping ot) kiddos
+      newot_0 = ot {_owlTree_mapping = omwithadjusteddepth}
+      -- recurse
+      r_1 = foldl (internal_owlTree_recalculateChildrenDepths_recursive) newot_0 kiddos
+    x -> ot
+
+-- parents allowed ONLY if all the children already exist in the tree (as orphans) or it will crash
+-- returns the added element for convenience but NOT its children 
+internal_owlTree_addOwlItem :: (HasCallStack) => OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, SuperOwl)
+internal_owlTree_addOwlItem OwlSpot {..} rid oitem OwlTree {..} = r
   where
-    -- if we're adding a folder (in the normal case), ensure it has no children
-    nochildrenifaddingfolder = case oitem of
-      OwlItem _ (OwlSubItemFolder kiddos) -> Seq.null kiddos
-      _ -> True
 
     -- first add the OwlItem to the mapping
     meta =
@@ -987,8 +1008,8 @@
 
     newMapping' = IM.insertWithKey (\k _ ov -> error ("key " <> show k <> " already exists with value " <> show ov)) rid (meta, oitem) _owlTree_mapping
 
-    -- modify kiddos of the parent we are adding to
-    modifyKiddos kiddos = Seq.insertAt position rid kiddos
+    -- update siblings in the parent we are adding to, leaving the tree in an invalid state (siblings have wrong position index in their OwlItemMeta)
+    updateSiblings kiddos = Seq.insertAt position rid kiddos
       where
         position = case _owlSpot_leftSibling of
           Nothing -> 0
@@ -996,32 +1017,42 @@
             Nothing -> error $ "expected to find leftmost sibling " <> show leftsibrid <> " in " <> show kiddos
             Just x -> x + 1
     adjustfn (oem, oitem') = case oitem' of
-      OwlItem oinfo (OwlSubItemFolder kiddos) -> (oem, OwlItem oinfo (OwlSubItemFolder (modifyKiddos kiddos)))
+      OwlItem oinfo (OwlSubItemFolder kiddos) -> (oem, OwlItem oinfo (OwlSubItemFolder (updateSiblings kiddos)))
       _ -> error $ "expected OwlItemFolder"
     newMapping = case _owlSpot_parent of
       x | x == noOwl -> newMapping'
-      _ -> IM.adjust adjustfn _owlSpot_parent newMapping'
+      _ -> assert (IM.member _owlSpot_parent newMapping') $ IM.adjust adjustfn _owlSpot_parent newMapping'
     -- or top owls if there is no parent
     newTopOwls = case _owlSpot_parent of
-      x | x == noOwl -> modifyKiddos _owlTree_topOwls
+      x | x == noOwl -> updateSiblings _owlTree_topOwls
       _ -> _owlTree_topOwls
 
-    r' =
+    tree_1 =
       OwlTree
         { _owlTree_mapping = newMapping,
           _owlTree_topOwls = newTopOwls
         }
 
-    newtree = internal_owlTree_reorgKiddos r' _owlSpot_parent
+    -- correct the sibling indices
+    tree_2 = internal_owlTree_reorgKiddos tree_1 _owlSpot_parent
 
-    newsowl = owlTree_mustFindSuperOwl newtree rid
+    -- correct the children depths
+    tree_3 = internal_owlTree_recalculateChildrenDepths_recursive tree_2 rid
 
-    r = (newtree, newsowl)
+    newsowl = owlTree_mustFindSuperOwl tree_3 rid
 
--- OwlItem must not be a parent
-owlTree_addOwlItem :: OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, SuperOwl)
-owlTree_addOwlItem = internal_owlTree_addOwlItem
+    r = (tree_3, newsowl)
 
+-- NOTE parents are allowed here IF all the children already exist in the tree
+-- returns the added element and all its children
+owlTree_addOwlItem :: OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, [SuperOwl])
+owlTree_addOwlItem ospot rid oitem ot = r where
+  (newot, addedSowls) = internal_owlTree_addOwlItem ospot rid oitem ot
+  addedSowlsWithChildren = toList . owliterateat newot . _superOwl_id $ addedSowls
+  r = (newot, addedSowlsWithChildren)
+
+  
+
 -- this method works for parents IF all children are included in the list and sorted from left to right
 owlTree_addOwlItemList :: [(REltId, OwlSpot, OwlItem)] -> OwlTree -> (OwlTree, [SuperOwl])
 owlTree_addOwlItemList seltls od0 = r where
@@ -1031,14 +1062,13 @@
   mapaccumlfn od (rid,ospot,oitem) = internal_owlTree_addOwlItem ospot rid oitemmodded od where
     osubitemmodded = case _owlItem_subItem oitem of
       -- temp remove kiddos from parent as needed by internal_owlTree_addOwlItem
+      -- the kiddos will be set correctly when the children get added further down the seltls list
       OwlSubItemFolder _ -> OwlSubItemFolder Seq.empty
       x -> x
     oitemmodded = OwlItem (_owlItem_info oitem) osubitemmodded
 
   -- go from left to right such that parents are added first
-  (newot, changes) = mapAccumL mapaccumlfn od0 seltls
-
-  r = (newot, changes)
+  r = mapAccumL mapaccumlfn od0 seltls
 
 
 -- TODO TEST
diff --git a/src/Potato/Flow/OwlHelpers.hs b/src/Potato/Flow/OwlHelpers.hs
--- a/src/Potato/Flow/OwlHelpers.hs
+++ b/src/Potato/Flow/OwlHelpers.hs
@@ -3,11 +3,11 @@
 
 import Relude
 
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 import Potato.Flow.Owl
 import Potato.Flow.OwlItem
 import Potato.Flow.Llama
-import Potato.Flow.SEltMethods
+import Potato.Flow.Methods.SEltMethods
 
 superOwl_mustGetSLine :: SuperOwl -> SAutoLine
 superOwl_mustGetSLine sowl = case _owlItem_subItem$ _superOwl_elt sowl of
diff --git a/src/Potato/Flow/OwlItem.hs b/src/Potato/Flow/OwlItem.hs
--- a/src/Potato/Flow/OwlItem.hs
+++ b/src/Potato/Flow/OwlItem.hs
@@ -4,7 +4,7 @@
 
 import Relude
 
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 import Potato.Flow.DebugHelpers
 
 data OwlInfo = OwlInfo {
diff --git a/src/Potato/Flow/OwlState.hs b/src/Potato/Flow/OwlState.hs
--- a/src/Potato/Flow/OwlState.hs
+++ b/src/Potato/Flow/OwlState.hs
@@ -8,8 +8,8 @@
 import Potato.Flow.Attachments
 import Potato.Flow.OwlItem
 import           Potato.Flow.Math
-import           Potato.Flow.SElts
-import           Potato.Flow.SEltMethods
+import           Potato.Flow.Serialization.Snake
+import           Potato.Flow.Methods.SEltMethods
 import           Potato.Flow.Types
 import Potato.Flow.DebugHelpers
 
@@ -18,6 +18,7 @@
 import           Data.List.Ordered       (isSortedBy)
 import           Data.Maybe
 import qualified Data.Text as T
+import qualified Data.Sequence as Seq
 
 
 
@@ -114,10 +115,10 @@
 owlPFState_to_SuperOwlParliament :: OwlPFState -> SuperOwlParliament
 owlPFState_to_SuperOwlParliament OwlPFState {..} = owlParliament_toSuperOwlParliament _owlPFState_owlTree $ OwlParliament $ _owlTree_topOwls _owlPFState_owlTree
 
-do_newElts :: [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges)
+do_newElts :: (HasCallStack) => [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges)
 do_newElts seltls pfs@OwlPFState {..} = r where
 
-  -- parents are allowed, but seltls must be sortefd from left -> right such that leftmost sibling/parent of OwlSpot exists (assuming elts are added to the tree from left to right)
+  -- notably, passing in a list here rather than SuperOwlParialment allows us to add parents with children so long as everything is sorted from left to right (such that the parent/sibling exist when the child gets added)
   (newot, changes') = owlTree_addOwlItemList seltls _owlPFState_owlTree
 
   changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) changes'
@@ -125,7 +126,7 @@
 
 undo_newElts :: [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges)
 undo_newElts seltls pfs@OwlPFState {..} = r where
-  foldfn (rid,_,_) od = owlTree_removeREltId rid od
+  foldfn (rid,_,_) od = owlTree_removeREltId False rid od
   -- assumes seltls sorted from left to right so that no parent is deleted before its child
   newot = foldr foldfn _owlPFState_owlTree seltls
   changes = IM.fromList $ fmap (\(rid,_,_) -> (rid, Nothing)) seltls
@@ -145,7 +146,7 @@
 
 undo_newMiniOwlTree :: (MiniOwlTree, OwlSpot) -> OwlPFState -> (OwlPFState, SuperOwlChanges)
 undo_newMiniOwlTree (mot, _) pfs@OwlPFState {..} = r where
-  foldfn rid od = owlTree_removeREltId rid od
+  foldfn rid od = owlTree_removeREltId False rid od
   newot = foldr foldfn _owlPFState_owlTree (_owlTree_topOwls mot)
   changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Nothing)) $ toList $ owliterateall mot
   r = (pfs { _owlPFState_owlTree = newot}, changes)
@@ -174,9 +175,10 @@
   op = superOwlParliament_toOwlParliament sop
   (newot, changes') = owlTree_moveOwlParliament op os _owlPFState_owlTree
   changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) changes'
+
   r = (pfs { _owlPFState_owlTree = newot}, changes)
 
-undo_move :: (OwlSpot, SuperOwlParliament) -> OwlPFState -> (OwlPFState, SuperOwlChanges)
+undo_move :: (HasCallStack) => (OwlSpot, SuperOwlParliament) -> OwlPFState -> (OwlPFState, SuperOwlChanges)
 undo_move (_, sop) pfs@OwlPFState {..} = assert isUndoFriendly r where
 
   -- NOTE that sop is likely invalid in pfs at this point
@@ -184,17 +186,17 @@
   -- make sure SuperOwlParliament is ordered in an undo-friendly way
   isUndoFriendly = isSuperOwlParliamentUndoFriendly sop
 
-  -- first remove all elements we moved
-  removefoldfn tree' so = owlTree_removeREltId (_superOwl_id so) tree'
+  -- first remove all elements we moved, but keep their children putting the tree in an invalid state
+  removefoldfn tree' so = owlTree_removeREltId True (_superOwl_id so) tree'
   removedTree = foldl' removefoldfn _owlPFState_owlTree (unSuperOwlParliament sop)
 
-  -- then add them back in in order
+  -- then add them back in in order (any children are still in the tree with missing parent)
   addmapaccumlfn tree' so = owlTree_addOwlItem ospot (_superOwl_id so) (_superOwl_elt so) tree' where
-    -- NOTE that because we are ordered from left to right, _superOwl_meta so is valid in tree'
+    -- NOTE that because we are ordered from left to right, `_superOwl_meta so` points to a vlaid spot in the tree' (parents comes before children)
     ospot = owlTree_owlItemMeta_toOwlSpot tree' $ _superOwl_meta so
   (addedTree, changes') = mapAccumL addmapaccumlfn removedTree (unSuperOwlParliament sop)
-
-  changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) (toList changes')
+  changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) (join . toList $ changes')
+  
   r = (pfs { _owlPFState_owlTree = addedTree}, changes)
 
 
diff --git a/src/Potato/Flow/OwlWorkspace.hs b/src/Potato/Flow/OwlWorkspace.hs
--- a/src/Potato/Flow/OwlWorkspace.hs
+++ b/src/Potato/Flow/OwlWorkspace.hs
@@ -22,7 +22,7 @@
 import           Potato.Flow.Owl
 import           Potato.Flow.OwlItem
 import           Potato.Flow.OwlState
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import           Potato.Flow.Types
 import Potato.Flow.Preview
 
@@ -278,6 +278,7 @@
         
 
       Preview_Commit -> assert (owlPFWorkspace_hasLocalPreview ws) $ (ws_afterCommit, IM.empty)
+      Preview_MaybeCommit -> (ws_afterCommit, IM.empty)
       Preview_Cancel -> case _owlPFWorkspace_localPreview ws of 
         Nothing -> error "expected local preview"
         Just (_, _, undollama) -> clearLocalPreview $ doLlamaWorkspace' False undollama ws
diff --git a/src/Potato/Flow/Preview.hs b/src/Potato/Flow/Preview.hs
--- a/src/Potato/Flow/Preview.hs
+++ b/src/Potato/Flow/Preview.hs
@@ -41,6 +41,8 @@
   Preview PreviewOperation Llama 
   -- commit the last operation
   | Preview_Commit
+  -- same as above but does not assert that there is a local preview
+  | Preview_MaybeCommit 
   -- cancel the preview 
   | Preview_Cancel 
   deriving (Show, Generic)
diff --git a/src/Potato/Flow/Reflex/GoatWidget.hs b/src/Potato/Flow/Reflex/GoatWidget.hs
--- a/src/Potato/Flow/Reflex/GoatWidget.hs
+++ b/src/Potato/Flow/Reflex/GoatWidget.hs
@@ -124,11 +124,8 @@
 
     -- new Endo folding
     endoStyle = [ 
-        -- these be run before _goatWidgetConfig_mouse because sometimes we want to set params/change focus and input a mouse at the same time (i.e. clicking away from params widget to canvas widget causing params to send an update)
-        fmap endoGoatCmdSetFocusedArea _goatWidgetConfig_setFocusedArea
-
-        -- the order of the rest doesn't matter
-        , fmap endoGoatCmdSetDefaultParams _goatWidgetConfig_setPotatoDefaultParameters
+        -- the order of these ones don't matter
+        fmap endoGoatCmdSetDefaultParams _goatWidgetConfig_setPotatoDefaultParameters
         , fmap endoGoatCmdMarkSaved _goatWidgetConfig_markSaved
         , fmap endoGoatCmdSetTool _goatWidgetConfig_selectTool
         , fmap endoGoatCmdSetDebugLabel _goatWidgetConfig_setDebugLabel
@@ -136,10 +133,13 @@
         , fmap endoGoatCmdLoad _goatWidgetConfig_load
         , fmap (\_ -> endoGoatCmdNewFolder "folder") _goatWidgetConfig_newFolder
         , fmap endoGoatCmdWSEvent _goatWidgetConfig_bypassEvent
-        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_paramsEvent $ \llama -> WSEApplyLlama (False, llama)
-        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_canvasSize $ \xy -> WSEApplyLlama (False, makePFCLlama $ OwlPFCResizeCanvas (DeltaLBox 0 xy))
         , fmap endoGoatCmdMouse _goatWidgetConfig_mouse
         , fmap endoGoatCmdKeyboard _goatWidgetConfig_keyboard
+
+        -- these be run before _goatWidgetConfig_mouse because sometimes we want to set params/change focus and input a mouse at the same time (i.e. clicking away from params widget to canvas widget causing params to send an update)
+        , fmap endoGoatCmdSetFocusedArea _goatWidgetConfig_setFocusedArea
+        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_paramsEvent $ \llama -> WSEApplyLlama (False, llama)
+        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_canvasSize $ \xy -> WSEApplyLlama (False, makePFCLlama $ OwlPFCResizeCanvas (DeltaLBox 0 xy))
       ]
 
   -- DELETE
diff --git a/src/Potato/Flow/Render.hs b/src/Potato/Flow/Render.hs
--- a/src/Potato/Flow/Render.hs
+++ b/src/Potato/Flow/Render.hs
@@ -34,9 +34,9 @@
 import           Potato.Flow.RenderCache
 import           Potato.Flow.BroadPhase
 import           Potato.Flow.Math
-import           Potato.Flow.SEltMethods
+import           Potato.Flow.Methods.SEltMethods
 import           Potato.Flow.Methods.Types
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import Potato.Flow.Types
 import Potato.Flow.OwlState
 import           Potato.Flow.OwlItem
diff --git a/src/Potato/Flow/RenderCache.hs b/src/Potato/Flow/RenderCache.hs
--- a/src/Potato/Flow/RenderCache.hs
+++ b/src/Potato/Flow/RenderCache.hs
@@ -6,7 +6,7 @@
 
 import           Potato.Flow.Math
 import           Potato.Flow.Methods.Types
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 import Potato.Flow.Types
 import Potato.Flow.Owl
 import           Potato.Flow.Methods.LineTypes
diff --git a/src/Potato/Flow/SEltMethods.hs b/src/Potato/Flow/SEltMethods.hs
deleted file mode 100644
--- a/src/Potato/Flow/SEltMethods.hs
+++ /dev/null
@@ -1,384 +0,0 @@
-{-# LANGUAGE RecordWildCards #-}
-
-module Potato.Flow.SEltMethods where
-
-import           Relude
-
-import           Potato.Flow.Math
-import           Potato.Flow.Methods.LineDrawer
-import           Potato.Flow.Methods.TextCommon
-import           Potato.Flow.Methods.Types
-import           Potato.Flow.Owl
-import Potato.Flow.RenderCache
-import           Potato.Flow.OwlItem
-import           Potato.Flow.SElts
-import           Potato.Flow.Types
-
-import           Data.Dependent.Sum             (DSum ((:=>)))
-import qualified Data.Map                       as Map
-import           Data.Maybe                     (fromJust)
-import qualified Data.Text                      as T
-import qualified Potato.Data.Text.Zipper        as TZ
-import Control.Exception (assert)
-
-
--- DisplayLines tag is Int, 0 for no cursor 1 for cursor
-noTrailngCursorDisplayLines :: Int -> TextAlign -> T.Text -> TZ.DisplayLines Int
-noTrailngCursorDisplayLines width alignment text = r where
-  -- force TZ to top so that displayLinesWithAlignment doesn't create trailing space for cursor
-  tz = TZ.top (TZ.fromText text)
-
-  -- hack to get rid of trailing cursor if text is ""
-  r = if T.null text
-    then TZ.DisplayLines {
-        _displayLines_spans = []
-        , _displayLines_offsetMap = Map.empty
-        , _displayLines_cursorPos   = (0,0)
-      }
-    else TZ.displayLinesWithAlignment (convertTextAlignToTextZipperTextAlignment alignment) width 0 1 tz
-
-makeDisplayLinesFromSBox :: SBox -> TZ.DisplayLines Int
-makeDisplayLinesFromSBox sbox = r where
-  alignment = _textStyle_alignment . _sBoxText_style . _sBox_text $ sbox
-  text = _sBoxText_text . _sBox_text $ sbox
-  LBox _ (V2 width' _) = _sBox_box sbox
-  width = case _sBox_boxType sbox of
-    SBoxType_BoxText   -> max 0 (width'-2)
-    SBoxType_NoBoxText -> width'
-    _                  -> error "wrong type"
-  r = noTrailngCursorDisplayLines width alignment text
-
-
-
--- TODO DELETE use doesOwlSubItemIntersectBox instead
-doesSEltIntersectBox_DEPRECATED :: LBox -> SElt -> Bool
-doesSEltIntersectBox_DEPRECATED lbox selt = case selt of
-  SEltNone                     -> False
-  SEltFolderStart              -> False
-  SEltFolderEnd                -> False
-  SEltBox x                    -> does_lBox_intersect_include_zero_area lbox (_sBox_box x)
-  SEltTextArea x                   -> does_lBox_intersect_include_zero_area lbox (_sTextArea_box x)
-  -- TODO this is wrong, do it correctly...
-  -- we use does_lBox_intersect since it's impossible for a SAutoLine to have zero sized box
-  SEltLine sline -> does_lBox_intersect lbox (fromJust $ getSEltBox_naive (SEltLine sline))
-
-doesSEltIntersectPoint :: XY -> SElt -> Bool
-doesSEltIntersectPoint pos selt = doesSEltIntersectBox_DEPRECATED (LBox pos (V2 1 1)) selt
-
-getSEltSuperStyle :: SElt -> Maybe SuperStyle
-getSEltSuperStyle selt = case selt of
-  SEltBox SBox {..}       -> Just _sBox_superStyle
-  SEltLine SAutoLine {..} -> Just _sAutoLine_superStyle
-  _                       -> Nothing
-
-getSEltLabelSuperStyle :: SEltLabel -> Maybe SuperStyle
-getSEltLabelSuperStyle (SEltLabel _ x) = getSEltSuperStyle x
-
-getSEltLineStyle :: SElt -> Maybe LineStyle
-getSEltLineStyle selt = case selt of
-  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyle
-  _                       -> Nothing
-
-getSEltLineStyleEnd :: SElt -> Maybe LineStyle
-getSEltLineStyleEnd selt = case selt of
-  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyleEnd
-  _                       -> Nothing
-
-getSEltLabelLineStyle :: SEltLabel -> Maybe LineStyle
-getSEltLabelLineStyle (SEltLabel _ x) = getSEltLineStyle x
-
-getSEltLabelLineStyleEnd :: SEltLabel -> Maybe LineStyle
-getSEltLabelLineStyleEnd (SEltLabel _ x) = getSEltLineStyleEnd x
-
-
-getSEltBoxTextStyle :: SElt -> Maybe TextStyle
-getSEltBoxTextStyle = \case
-  SEltBox SBox {..}         -> Just . _sBoxText_style $ _sBox_text
-  _ -> Nothing
-
-getSEltLabelBoxTextStyle :: SEltLabel -> Maybe TextStyle
-getSEltLabelBoxTextStyle (SEltLabel _ x) = getSEltBoxTextStyle x
-
-getSEltBoxType :: SElt -> Maybe SBoxType
-getSEltBoxType = \case
-  SEltBox SBox {..} -> Just _sBox_boxType
-  _ -> Nothing
-
-getSEltLabelBoxType :: SEltLabel -> Maybe SBoxType
-getSEltLabelBoxType (SEltLabel _ x) = getSEltBoxType x
-
-sBox_drawer :: SBox -> SEltDrawer
-sBox_drawer sbox@SBox {..} = r where
-  CanonicalLBox _ _ lbox@(LBox (V2 x y) (V2 w h)) = canonicalLBox_from_lBox _sBox_box
-
-  titlewidth = max 0 (w-2)
-
-  fillfn _ = case _superStyle_fill _sBox_superStyle of
-    FillStyle_Simple c -> Just c
-    FillStyle_Blank    -> Nothing
-
-  rfntext (V2 x' y') = case _sBox_boxType of
-    SBoxType_Box -> Nothing
-    SBoxType_NoBox -> Nothing
-    _ -> outputChar where
-
-      -- 😰😰😰 for now we just do the below for every cell
-      dl = makeDisplayLinesFromSBox sbox
-
-      offs = case _sBox_boxType of
-        SBoxType_NoBoxText -> (0,0)
-        _                  -> (1,1)
-
-      outputChar = displayLinesToChar (x, y) dl (x', y') offs
-
-  -- TODO test
-  rfnlabel (V2 x' y') = case _sBoxTitle_title _sBox_title of
-    Nothing -> Nothing
-    Just title -> outputChar where
-      -- TODO we want to crop instead of wrap here
-      -- however using infinite width trick will break AlignRight :(
-      dl = noTrailngCursorDisplayLines titlewidth (_sBoxTitle_align _sBox_title) title
-      -- note that y' will ultimately resolve to a yindex of 0 inside of displayLinesToChar
-      outputChar = displayLinesToChar (x, y) dl (x', y') (1,0)
-
-  rfnnoborder pt
-    | not (does_lBox_contains_XY lbox pt) = Nothing
-    | otherwise = case rfntext pt of
-      -- 'Just Nothing' means don't use fill char (this happens when there are wide chars)
-      Just mx -> mx
-      Nothing -> fillfn pt
-
-  rfnborder pt@(V2 x' y')
-    | not (does_lBox_contains_XY lbox pt) = Nothing
-    | w == 1 && h == 1 = _superStyle_point _sBox_superStyle
-    | w == 1 = _superStyle_vertical _sBox_superStyle
-    | h == 1 = _superStyle_horizontal _sBox_superStyle
-    | x' == x && y' == y = _superStyle_tl _sBox_superStyle
-    | x' == x && y' == y+h-1 = _superStyle_bl _sBox_superStyle
-    | x' == x+w-1 && y' == y = _superStyle_tr _sBox_superStyle
-    | x' == x+w-1 && y' == y+h-1 = _superStyle_br _sBox_superStyle
-    | x' == x || x' == x+w-1 = _superStyle_vertical _sBox_superStyle
-    -- label shows up at top horizontal portion
-    | y' == y = case rfnlabel pt of
-      Nothing    -> _superStyle_horizontal _sBox_superStyle
-      Just pchar -> pchar
-    | y' == y+h-1 = _superStyle_horizontal _sBox_superStyle
-    | otherwise = rfnnoborder pt
-
-  r = SEltDrawer {
-      _sEltDrawer_box = const lbox
-      , _sEltDrawer_renderFn = \_ -> case _sBox_boxType of
-        SBoxType_NoBoxText -> rfnnoborder
-        SBoxType_NoBox     -> rfnnoborder
-        _                  -> rfnborder
-      
-      -- TODO 
-      , _sEltDrawer_maxCharWidth = 1
-    }
-
-sTextArea_drawer :: STextArea -> SEltDrawer
-sTextArea_drawer STextArea {..} = r where
-
-  lbox@(LBox p _) = _sTextArea_box
-
-  renderfn p' = outputChar where
-    inbounds = does_lBox_contains_XY lbox p'
-    outputChar = if inbounds
-      then case Map.lookup (p' - p) _sTextArea_text of
-        Nothing -> if _sTextArea_transparent
-          then Nothing
-          else Just ' '
-        Just c -> Just c
-      else Nothing
-
-  r = SEltDrawer {
-      _sEltDrawer_box = const lbox
-      , _sEltDrawer_renderFn = \_ -> renderfn
-
-      -- TODO
-      , _sEltDrawer_maxCharWidth = 1
-    }
-
--- NOTE that there is not a 1-1 mapping between `OwlSubItem` and `OwlItemCache` as the `OwlItemCache` is dependent on the OwlTree
--- this function assumes that you are requesting the drawer with the intent of passing in an OwlTree
--- TODO it would have been better for SEltDrawer to be created based on an OwlTree rather than return a function that takes an OwlTree
-getDrawerWithCache :: OwlSubItem -> Maybe OwlItemCache -> SEltDrawer
-getDrawerWithCache osubitem mcache = case osubitem of 
-  OwlSubItemNone        -> nilDrawer
-  OwlSubItemFolder _ -> nilDrawer
-  OwlSubItemBox sbox    -> sBox_drawer sbox
-  OwlSubItemLine sline -> case mcache of 
-    Just (OwlItemCache_Line lars _) -> sSimpleLineNewRenderFn sline (Just lars)
-    Nothing -> sSimpleLineNewRenderFn sline Nothing
-    _ -> assert False (sSimpleLineNewRenderFn sline Nothing)
-  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea
-
--- TODO pass in cache here
-getDrawer :: OwlSubItem -> SEltDrawer
-getDrawer = \case
-  OwlSubItemNone        -> nilDrawer
-  OwlSubItemFolder _ -> nilDrawer
-  OwlSubItemBox sbox    -> sBox_drawer sbox
-  OwlSubItemLine sline -> sSimpleLineNewRenderFn sline Nothing
-  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea
-  {-
-  where
-    potatoDrawer = SEltDrawer {
-        _sEltDrawer_box = const $ fromJust (getSEltBox_naive selt)
-        , _sEltDrawer_renderFn =  makePotatoRenderer $ fromJust (getSEltBox_naive selt)
-      }
-  -}
-
-getDrawerFromSEltForTest :: SElt -> SEltDrawer
-getDrawerFromSEltForTest = getDrawer . sElt_to_owlSubItem
-
-updateOwlSubItemCache :: (HasOwlTree a) => a -> OwlSubItem -> Maybe OwlItemCache
-updateOwlSubItemCache ot x = r where
-  r = case x of
-    -- TODO use sAutoLine_to_lineAnchorsForRenderList here instead
-    (OwlSubItemLine sline) -> cache where
-      cache = Just $ OwlItemCache_Line (sSimpleLineNewRenderFnComputeCache ot sline) prerender
-      seltdrawer = getDrawerWithCache x cache
-      prerender = makePreRender ot seltdrawer 
-    _ -> Just $ OwlItemCache_Generic prerender where
-      seltdrawer = getDrawerWithCache x Nothing
-      prerender = makePreRender ot seltdrawer 
-
-
--- TODO move modify methods to another file
-
-modify_sAutoLineConstraint_with_cBoundingBox :: Bool -> SAutoLineConstraint -> CBoundingBox -> SAutoLineConstraint
-modify_sAutoLineConstraint_with_cBoundingBox isDo constraint CBoundingBox {..} = case constraint of
-  SAutoLineConstraintFixed xy -> SAutoLineConstraintFixed $ modifyDelta isDo xy (_deltaLBox_translate _cBoundingBox_deltaBox)
-
-modify_sElt_with_cBoundingBox :: Bool -> SElt -> CBoundingBox -> SElt
-modify_sElt_with_cBoundingBox isDo selt cbb@CBoundingBox {..} = case selt of
-  SEltBox sbox  -> SEltBox $ sbox {
-      _sBox_box = modifyDelta isDo (_sBox_box sbox) _cBoundingBox_deltaBox
-    }
-  -- TODO handle resize parameter
-  SEltLine sline@SAutoLine {..} -> SEltLine $ sline {
-      _sAutoLine_start = modifyDelta isDo _sAutoLine_start
-        (_deltaLBox_translate _cBoundingBox_deltaBox)
-      , _sAutoLine_end = modifyDelta isDo _sAutoLine_end
-        (_deltaLBox_translate _cBoundingBox_deltaBox)
-      , _sAutoLine_midpoints = fmap (\slc -> modify_sAutoLineConstraint_with_cBoundingBox isDo slc cbb) _sAutoLine_midpoints
-    }
-  SEltTextArea stext -> SEltTextArea $ stext {
-      _sTextArea_box     = modifyDelta isDo (_sTextArea_box stext) _cBoundingBox_deltaBox
-    }
-  x          -> x
-
-modify_sElt_with_cSuperStyle :: Bool -> SElt -> CSuperStyle -> SElt
-modify_sElt_with_cSuperStyle isDo selt (CSuperStyle style) = case selt of
-  SEltBox sbox -> SEltBox $ sbox {
-      _sBox_superStyle = modifyDelta isDo (_sBox_superStyle sbox) style
-    }
-  -- TODO handle resize parameter
-  SEltLine sline -> SEltLine $ sline {
-      _sAutoLine_superStyle = modifyDelta isDo (_sAutoLine_superStyle sline) style
-    }
-  _ -> error $ "Controller - SElt type mismatch: CTagSuperStyle - " <> show selt
-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
-  --x -> x
-
--- TODO DELETE use llama instead
-modify_sElt_with_cLineStyle :: Bool -> SElt -> CLineStyle -> SElt
-modify_sElt_with_cLineStyle isDo selt (CLineStyle style) = case selt of
-  SEltLine sline -> SEltLine $ sline {
-      _sAutoLine_lineStyle = modifyDelta isDo (_sAutoLine_lineStyle sline) style
-    }
-  _ -> error $ "Controller - SElt type mismatch: CTagLineStyle - " <> show selt
-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
-  --x -> x
-
-modify_sElt_with_cTextStyle :: Bool -> SElt -> CTextStyle -> SElt
-modify_sElt_with_cTextStyle isDo selt (CTextStyle style) = case selt of
-  SEltBox sbox -> SEltBox $ sbox {
-      _sBox_text = (_sBox_text sbox) {
-          _sBoxText_style = modifyDelta isDo (_sBoxText_style . _sBox_text $ sbox) style
-        }
-    }
-  _ -> error $ "Controller - SElt type mismatch: CTagBoxTextStyle - " <> show selt
-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
-  --x -> x
-
-modify_sEltBox_label_with_cTextAlign :: Bool -> SElt -> CTextAlign -> SElt
-modify_sEltBox_label_with_cTextAlign isDo selt (CTextAlign align) = case selt of
-  SEltBox sbox -> SEltBox $ sbox {
-      _sBox_title = sboxtitle {
-          _sBoxTitle_align = modifyDelta isDo (_sBoxTitle_align sboxtitle) align
-        }
-    } where
-      sboxtitle = _sBox_title sbox
-  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt
-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk
-  --x -> x
-
-modify_sEltBox_label_with_cMaybeText :: Bool -> SElt -> CMaybeText -> SElt
-modify_sEltBox_label_with_cMaybeText isDo selt (CMaybeText text) = case selt of
-  SEltBox sbox -> SEltBox $ sbox {
-      _sBox_title = sboxtitle {
-          _sBoxTitle_title = modifyDelta isDo (_sBoxTitle_title sboxtitle) text
-        }
-    } where
-      sboxtitle = _sBox_title sbox
-  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt
-
-modify_sEltTextArea_with_cTextArea :: Bool -> SElt -> CTextArea -> SElt
-modify_sEltTextArea_with_cTextArea isDo selt (CTextArea dt) = case selt of
-  SEltTextArea stextarea -> SEltTextArea $ stextarea {
-      _sTextArea_text = modifyDelta isDo (_sTextArea_text stextarea) dt
-    }
-  _ -> error $ "Controller - SElt type mismatch: CTagTextArea - " <> show selt
-
-modify_sEltTextArea_with_cTextAreaToggle :: Bool -> SElt -> CTextAreaToggle -> SElt
-modify_sEltTextArea_with_cTextAreaToggle isDo selt (CTextAreaToggle toggle) = case selt of
-  -- double toggle is idempotent but we disallow it for now
-  SEltTextArea _ -> error $ "Controller - SElt type mismatch: CTagTextAreaToggle - " <> show selt
-  x -> modifyDelta isDo x toggle
-
-
-modifyDelta :: (Delta x dx) => Bool -> x ->  dx -> x
-modifyDelta isDo x dx = if isDo
-  then plusDelta x dx
-  else minusDelta x dx
-
-updateFnFromController :: Bool -> Controller -> (SEltLabel -> SEltLabel)
-updateFnFromController isDo = \case
-  (CTagRename :=> Identity d) -> \seltl -> modifyDelta isDo seltl d
-  (CTagLine :=> Identity d) -> \(SEltLabel sname selt) -> case selt of
-    SEltLine s -> SEltLabel sname (SEltLine $ modifyDelta isDo s d)
-    _ -> error $ "Controller - SElt type mismatch: CTagLine - " <> show selt
-  (CTagBoxText :=> Identity d) -> \(SEltLabel sname selt) -> case selt of
-    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)
-    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt
-  (CTagBoxType :=> Identity d) -> \(SEltLabel sname selt) -> case selt of
-    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)
-    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt
-  (CTagBoundingBox :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sElt_with_cBoundingBox isDo selt d)
-  (CTagSuperStyle :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sElt_with_cSuperStyle isDo selt d)
-  (CTagLineStyle :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sElt_with_cLineStyle isDo selt d)
-  (CTagBoxTextStyle :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sElt_with_cTextStyle isDo selt d)
-
-  (CTagBoxLabelAlignment :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sEltBox_label_with_cTextAlign isDo selt d)
-  (CTagBoxLabelText :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sEltBox_label_with_cMaybeText isDo selt d)
-
-  (CTagTextArea :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sEltTextArea_with_cTextArea isDo selt d)
-  (CTagTextAreaToggle :=> Identity d) -> \(SEltLabel sname selt) ->
-    SEltLabel sname (modify_sEltTextArea_with_cTextAreaToggle isDo selt d)
-
-
--- | helper method used in copy pasta
-offsetSEltTree :: XY -> SEltTree -> SEltTree
-offsetSEltTree offset stree = r where
-  op = CBoundingBox (DeltaLBox offset 0)
-  offsetfn (rid, seltl) = (rid, updateFnFromController True (CTagBoundingBox :=> Identity op) seltl)
-  r = fmap offsetfn stree
diff --git a/src/Potato/Flow/SElts.hs b/src/Potato/Flow/SElts.hs
deleted file mode 100644
--- a/src/Potato/Flow/SElts.hs
+++ /dev/null
@@ -1,458 +0,0 @@
-{-# LANGUAGE RecordWildCards #-}
-
-module Potato.Flow.SElts where
-
-import           Relude
-
-import           Potato.Flow.Math
-import Potato.Data.Text.Unicode
-
-import           Control.Exception (assert)
-import           Data.Aeson
-import           Data.Binary
-import           Data.Default
-import qualified Text.Show
-import qualified Data.Text as T
-import qualified Data.List         as L
-import qualified Data.Map as Map
-import qualified Potato.Data.Text.Zipper as TZ
-import Data.Ratio
-
-
-type REltId = Int
-type PChar = Char
-type MPChar = Maybe PChar
-
-getPCharWidth :: Char -> Int8
-getPCharWidth = getCharWidth
-
-
-data FillStyle = FillStyle_Blank | FillStyle_Simple PChar deriving (Eq, Generic, Show)
-
-instance FromJSON FillStyle
-instance ToJSON FillStyle
-instance Binary FillStyle
-instance NFData FillStyle
-
-instance Default FillStyle where
-  def = FillStyle_Simple ' '
-
--- TODO add line ends?
--- TODO add line thickness?
--- TODO add line fill?
-data SuperStyle = SuperStyle {
-  _superStyle_tl           :: MPChar
-  , _superStyle_tr         :: MPChar
-  , _superStyle_bl         :: MPChar
-  , _superStyle_br         :: MPChar
-  , _superStyle_vertical   :: MPChar
-  , _superStyle_horizontal :: MPChar
-  , _superStyle_point      :: MPChar -- used for 1x1 boxes and 1x lines
-  , _superStyle_fill       :: FillStyle
-} deriving (Eq, Generic)
-
-instance FromJSON SuperStyle
-instance ToJSON SuperStyle
-instance Binary SuperStyle
-instance NFData SuperStyle
-
-instance Default SuperStyle where
-  def = SuperStyle {
-    _superStyle_tl = Just '╔'
-    , _superStyle_tr = Just '╗'
-    , _superStyle_bl = Just '╚'
-    , _superStyle_br = Just '╝'
-    , _superStyle_vertical   = Just '║'
-    , _superStyle_horizontal = Just '═'
-    , _superStyle_point = Just '█'
-    , _superStyle_fill = def
-  }
-
-instance Show SuperStyle where
-  show = superStyle_toListFormat
-
-superStyle_fromListFormat :: [PChar] -> SuperStyle
-superStyle_fromListFormat chars = assert (l == 7 || l == 8) $ r where
-  l = length chars
-  r = SuperStyle {
-    _superStyle_tl = Just $ chars L.!! 0
-    , _superStyle_tr = Just $ chars L.!! 1
-    , _superStyle_bl = Just $ chars L.!! 2
-    , _superStyle_br = Just $ chars L.!! 3
-    , _superStyle_vertical   = Just $ chars L.!! 4
-    , _superStyle_horizontal = Just $ chars L.!! 5
-    , _superStyle_point = Just $ chars L.!! 6
-    , _superStyle_fill = if l == 7 then FillStyle_Blank else FillStyle_Simple (chars L.!! 7)
-  }
-
--- superStyle_fromListFormat "╔╗╚╝║═█" `shouldBe` def
--- empty styles are converted to space character
-superStyle_toListFormat :: SuperStyle -> [PChar]
-superStyle_toListFormat SuperStyle {..} = r where
-  mfill = case _superStyle_fill of
-    FillStyle_Blank    -> []
-    FillStyle_Simple c -> [c]
-  r = [
-      fromMaybe ' ' _superStyle_tl
-      ,fromMaybe ' ' _superStyle_tr
-      ,fromMaybe ' ' _superStyle_bl
-      ,fromMaybe ' ' _superStyle_br
-      ,fromMaybe ' ' _superStyle_vertical
-      ,fromMaybe ' ' _superStyle_horizontal
-      ,fromMaybe ' ' _superStyle_point
-    ] <> mfill
-
--- |
-data TextAlign = TextAlign_Left | TextAlign_Right | TextAlign_Center deriving (Eq, Generic, Show)
-
-instance FromJSON TextAlign
-instance ToJSON TextAlign
-instance Binary TextAlign
-instance NFData TextAlign
-
-instance Default TextAlign where
-  def = TextAlign_Left
-
-convertTextAlignToTextZipperTextAlignment :: TextAlign -> TZ.TextAlignment
-convertTextAlignToTextZipperTextAlignment = \case
-  TextAlign_Left -> TZ.TextAlignment_Left
-  TextAlign_Right -> TZ.TextAlignment_Right
-  TextAlign_Center -> TZ.TextAlignment_Center
-
--- |
-data TextStyle = TextStyle {
-  -- margins
-  _textStyle_alignment :: TextAlign
-} deriving (Eq, Generic)
-
-instance FromJSON TextStyle
-instance ToJSON TextStyle
-instance Binary TextStyle
-instance NFData TextStyle
-
-instance Default TextStyle where
-  def = TextStyle { _textStyle_alignment = def }
-
-instance Show TextStyle where
-  show TextStyle {..} = show _textStyle_alignment
-
-
--- TODO you need support for AL_Any and maybe AL_Center
--- TODO lowercase plz
-data AttachmentLocation = AL_Top | AL_Bot | AL_Left | AL_Right | AL_Any deriving (Eq, Generic, Show)
-
-instance FromJSON AttachmentLocation
-instance ToJSON AttachmentLocation
-instance Binary AttachmentLocation
-instance NFData AttachmentLocation
-
-type AttachmentOffsetRatio = Ratio Int
-
-data Attachment = Attachment {
-  _attachment_target :: REltId
-  , _attachment_location :: AttachmentLocation
-  -- you can prob just delete these, don't think we need them.
-  -- 1 is right/down most, 0 is left/top most, `1 % 2` is the middle
-  , _attachment_offset_rel :: AttachmentOffsetRatio
-} deriving (Eq, Generic, Show)
-
-instance FromJSON Attachment
-instance ToJSON Attachment
-instance Binary Attachment
-instance NFData Attachment
-
-
-attachment_offset_rel_default :: Ratio Int
-attachment_offset_rel_default =  1 % 2
-
-attachment_create_default :: REltId -> AttachmentLocation -> Attachment
-attachment_create_default rid al = Attachment {
-    _attachment_target = rid
-    , _attachment_location = al
-    , _attachment_offset_rel = attachment_offset_rel_default
-  }
-
--- |
-data SBoxTitle = SBoxTitle {
-  _sBoxTitle_title   :: Maybe Text
-  , _sBoxTitle_align :: TextAlign
-} deriving (Eq, Generic)
-
-instance FromJSON SBoxTitle
-instance ToJSON SBoxTitle
-instance Binary SBoxTitle
-instance NFData SBoxTitle
-
-instance Default SBoxTitle where
-  def = SBoxTitle {
-      _sBoxTitle_title = Nothing
-      , _sBoxTitle_align = def
-    }
-
-instance Show SBoxTitle where
-  show SBoxTitle {..} = "SBoxTitle: " <> show _sBoxTitle_align <> " " <> show _sBoxTitle_title
-
--- |
-data SBoxText = SBoxText {
-  _sBoxText_text    :: Text
-  , _sBoxText_style :: TextStyle
-} deriving (Eq, Generic)
-
-instance FromJSON SBoxText
-instance ToJSON SBoxText
-instance Binary SBoxText
-instance NFData SBoxText
-
-instance Default SBoxText where
-  def = SBoxText {
-      _sBoxText_text = ""
-      , _sBoxText_style = def
-    }
-
-instance Show SBoxText where
-  show SBoxText {..} = "SBoxText: " <> T.unpack _sBoxText_text <> " " <> show _sBoxText_style
-
-
-data SBoxType = SBoxType_Box | SBoxType_NoBox | SBoxType_BoxText | SBoxType_NoBoxText deriving (Eq, Generic, Show)
-
-instance FromJSON SBoxType
-instance ToJSON SBoxType
-instance Binary SBoxType
-instance NFData SBoxType
-
-instance Default SBoxType where
-  def = SBoxType_Box
-
-sBoxType_isText :: SBoxType -> Bool
-sBoxType_isText sbt = sbt == SBoxType_BoxText || sbt == SBoxType_NoBoxText
-
-sBoxType_hasBorder :: SBoxType -> Bool
-sBoxType_hasBorder sbt = sbt == SBoxType_Box || sbt == SBoxType_BoxText
-
-make_sBoxType :: Bool -> Bool -> SBoxType
-make_sBoxType border text = if border
-  then if text
-    then SBoxType_BoxText
-    else SBoxType_Box
-  else if text
-    then SBoxType_NoBoxText
-    else SBoxType_NoBox
-
--- |
-data SBox = SBox {
-  _sBox_box       :: LBox
-  , _sBox_superStyle   :: SuperStyle
-  , _sBox_title   :: SBoxTitle
-  , _sBox_text    :: SBoxText
-  , _sBox_boxType :: SBoxType
-} deriving (Eq, Generic)
-
-instance FromJSON SBox
-instance ToJSON SBox
-instance Binary SBox
-instance NFData SBox
-
-instance Default SBox where
-  def = SBox {
-      _sBox_box     = LBox 0 0
-      , _sBox_superStyle = def
-      , _sBox_title = def
-      , _sBox_text  = def
-      , _sBox_boxType = SBoxType_Box
-    }
-
-instance Show SBox where
-  show SBox {..} = "SBox: " <> show _sBox_box <> " " <> show _sBox_title <> " " <> show _sBox_text <> " " <> show _sBox_boxType <> " " <> show _sBox_superStyle
-
-sBox_hasLabel :: SBox -> Bool
-sBox_hasLabel sbox = sBoxType_hasBorder (_sBox_boxType sbox) && (isJust . _sBoxTitle_title ._sBox_title $ sbox)
-
--- TODO DELETE no longer used with SAutoLine
-data LineAutoStyle =
-  LineAutoStyle_Auto
-  | LineAutoStyle_AutoStraight
-  | LineAutoStyle_StraightAlwaysHorizontal
-  | LineAutoStyle_StraightAlwaysVertical
-  deriving (Eq, Generic, Show)
-
-instance FromJSON LineAutoStyle
-instance ToJSON LineAutoStyle
-instance Binary LineAutoStyle
-instance NFData LineAutoStyle
-
-instance Default LineAutoStyle where
-  def = LineAutoStyle_AutoStraight
-
-
-data LineStyle = LineStyle {
-  _lineStyle_leftArrows    :: Text
-  , _lineStyle_rightArrows :: Text
-  , _lineStyle_upArrows    :: Text
-  , _lineStyle_downArrows  :: Text
-} deriving (Eq, Generic)
-
-instance FromJSON LineStyle
-instance ToJSON LineStyle
-instance Binary LineStyle
-instance NFData LineStyle
-
-instance Default LineStyle where
-  def = LineStyle {
-      _lineStyle_leftArrows    = "<"
-      , _lineStyle_rightArrows = ">"
-      , _lineStyle_upArrows    = "^"
-      , _lineStyle_downArrows  = "v"
-    }
-
-lineStyle_fromListFormat :: ([PChar], [PChar], [PChar], [PChar]) -> LineStyle
-lineStyle_fromListFormat (l,r,u,d) = LineStyle {
-    _lineStyle_leftArrows    = T.pack l
-    , _lineStyle_rightArrows = T.pack r
-    , _lineStyle_upArrows    = T.pack u
-    , _lineStyle_downArrows  = T.pack d
-  }
-
-lineStyle_toListFormat :: LineStyle -> ([PChar], [PChar], [PChar], [PChar])
-lineStyle_toListFormat LineStyle {..} = (T.unpack _lineStyle_leftArrows, T.unpack _lineStyle_rightArrows, T.unpack _lineStyle_upArrows, T.unpack _lineStyle_downArrows)
-
-
-instance Show LineStyle where
-  show ls = r where
-    (a, b, c, d) = lineStyle_toListFormat ls
-    r = "LineStyle: " <> a <> " " <> b <> " " <> c <> " " <> d
-
--- someday we might have more than one constraint...
-data SAutoLineConstraint = SAutoLineConstraintFixed XY deriving (Eq, Generic, Show)
-
-instance FromJSON SAutoLineConstraint
-instance ToJSON SAutoLineConstraint
-instance Binary SAutoLineConstraint
-instance NFData SAutoLineConstraint
-
--- TODO provide absolute and relative positioning args
-data SAutoLineLabelPosition =
-  SAutoLineLabelPositionRelative Float -- 0 is at "left" anchor point and 1 is at "right" anchor point
-  deriving (Eq, Generic, Show)
-
-instance FromJSON SAutoLineLabelPosition
-instance ToJSON SAutoLineLabelPosition
-instance Binary SAutoLineLabelPosition
-instance NFData SAutoLineLabelPosition
-
-data SAutoLineLabel = SAutoLineLabel {
-  _sAutoLineLabel_index :: Int -- index relative to _sAutoLine_midpoints for where the midpoint lives
-  , _sAutoLineLabel_position :: SAutoLineLabelPosition
-  , _sAutoLineLabel_text :: Text
-  --, _sAutoLineLabel_vertical :: Bool -- WIP true if vertically oriented
-} deriving (Eq, Generic)
-
-instance Show SAutoLineLabel where
-  show SAutoLineLabel {..} = "SAutoLineLabel: " <> show _sAutoLineLabel_index <> " " <> show _sAutoLineLabel_position <> " " <> show _sAutoLineLabel_text
-
-instance FromJSON SAutoLineLabel
-instance ToJSON SAutoLineLabel
-instance Binary SAutoLineLabel
-instance NFData SAutoLineLabel
-
-instance Default SAutoLineLabel where
-  def = SAutoLineLabel {
-      -- anchor index, text shows AFTER index
-      _sAutoLineLabel_index = 0
-      , _sAutoLineLabel_position = SAutoLineLabelPositionRelative 0
-      , _sAutoLineLabel_text = ""
-      --, _sAutoLineLabel_vertical = False
-    }
-
-
--- |
-data SAutoLine = SAutoLine {
-  _sAutoLine_start       :: XY
-  , _sAutoLine_end       :: XY
-  , _sAutoLine_superStyle     :: SuperStyle
-
-  -- TODO you need one for start/end of line (LineStyle, LineStyle)
-  , _sAutoLine_lineStyle :: LineStyle
-  , _sAutoLine_lineStyleEnd :: LineStyle
-
-  -- NOTE attachments currently are not guaranteed to exist
-  -- in particular, if you copy a line, delete its target and paste, it will be attached to something that doesn't exist
-  -- tinytools will attempt to correct attachment in some cases but don't get too cozy about it!
-  , _sAutoLine_attachStart :: Maybe Attachment
-  , _sAutoLine_attachEnd :: Maybe Attachment
-
-  , _sAutoLine_midpoints :: [SAutoLineConstraint]
-  , _sAutoLine_labels :: [SAutoLineLabel] -- WIP currently does nothing
-} deriving (Eq, Generic)
-
-instance FromJSON SAutoLine
-instance ToJSON SAutoLine
-instance Binary SAutoLine
-instance NFData SAutoLine
-
-instance Show SAutoLine where
-  show SAutoLine {..} = r where
-    start = maybe (show _sAutoLine_start) show _sAutoLine_attachStart
-    end = maybe (show _sAutoLine_end) show _sAutoLine_attachEnd
-    r = "SAutoLine: " <> start <> " " <> end <> " " <> show _sAutoLine_midpoints <> " " <> show _sAutoLine_labels
-
--- makes writing tests easier...
-instance Default SAutoLine where
-  def = SAutoLine {
-      _sAutoLine_start       = 0
-      , _sAutoLine_end       = 0
-      , _sAutoLine_superStyle     = def
-      , _sAutoLine_lineStyle = def
-      , _sAutoLine_lineStyleEnd = def
-      , _sAutoLine_attachStart = Nothing
-      , _sAutoLine_attachEnd = Nothing
-      , _sAutoLine_midpoints = []
-      , _sAutoLine_labels = []
-    }
-    
-type TextAreaMapping = Map XY PChar
-
--- | abitrary text confined to a box
-data STextArea = STextArea {
-  _sTextArea_box           :: LBox
-  , _sTextArea_text        :: TextAreaMapping
-  -- TODO consider using SuperStyle here instead and using Fill property only
-  , _sTextArea_transparent :: Bool
-} deriving (Eq, Generic, Show)
-
-instance Default STextArea where
-  def = STextArea {
-      _sTextArea_box   =        LBox 0 0
-      , _sTextArea_text        = Map.empty
-      , _sTextArea_transparent = True
-    }
-
-instance FromJSON STextArea
-instance ToJSON STextArea
-instance Binary STextArea
-instance NFData STextArea
-
--- TODO consider removing this all together and serializing Owl stuff directly
-data SElt =
-  SEltNone
-  | SEltFolderStart
-  | SEltFolderEnd
-  | SEltBox SBox
-  | SEltLine SAutoLine
-  | SEltTextArea STextArea
-  deriving (Eq, Generic, Show)
-
-instance FromJSON SElt
-instance ToJSON SElt
-instance Binary SElt
-instance NFData SElt
-
--- TODO consider removing this all together and serializing Owl stuff directly
-data SEltLabel = SEltLabel {
- _sEltLabel_name   :: Text
- , _sEltLabel_sElt :: SElt
-} deriving (Eq, Generic, Show)
-
-instance FromJSON SEltLabel
-instance ToJSON SEltLabel
-instance Binary SEltLabel
-instance NFData SEltLabel
diff --git a/src/Potato/Flow/Serialization/Snake.hs b/src/Potato/Flow/Serialization/Snake.hs
--- a/src/Potato/Flow/Serialization/Snake.hs
+++ b/src/Potato/Flow/Serialization/Snake.hs
@@ -1,83 +1,7 @@
-{-# LANGUAGE RecordWildCards #-}
-
-module Potato.Flow.Serialization.Snake where
-
-import           Relude
-
-import           Potato.Flow.Types
-import Potato.Flow.Controller.Types
-
-import qualified Data.Aeson               as Aeson
-import qualified Data.Aeson.Encode.Pretty as PrettyAeson
-import qualified Data.Binary as Binary
-import qualified Data.ByteString.Lazy as LBS
-import qualified Data.Text.Encoding as Text
-
-
-
--- | list of all version supported
-versions :: [Int]
-versions = [1]
-
--- | version of the current build
-currentVersion :: Int
-currentVersion = 1
-
-data Snake = Snake {
-  _snake_version :: Int
-  -- string instead of enum type to ensure compatibility if new formats are ever added
-  -- currently supports "binary" and "json"
-  , _snake_format :: String
-  , _snake_data :: Text
-} deriving (Eq, Generic, Show)
-
-instance Aeson.FromJSON Snake
-instance Aeson.ToJSON Snake
-instance Binary.Binary Snake
-instance NFData Snake
-
-data SnakeFormat = SF_Json | SF_Binary
-
-serialize :: SnakeFormat -> (SPotatoFlow, ControllerMeta) -> LBS.ByteString
-serialize f x = r where
-  (inner, format) = case f of
-    SF_Json -> (PrettyAeson.encodePretty x, "json")
-    SF_Binary -> (Binary.encode x, "binary")
-  outer = Snake {
-    _snake_version = currentVersion
-    , _snake_format = format
-    , _snake_data = Text.decodeUtf8 (LBS.toStrict inner)
-  }
-  r = PrettyAeson.encodePretty outer
-
-deserialize :: LBS.ByteString -> Either String (SPotatoFlow, ControllerMeta)
-deserialize lbs = case Aeson.eitherDecode lbs of
-  Left e -> Left $ "failed to decode Snake with error " <> e
-  Right vso -> deserialize_internal vso
-
-
-deserialize_internal :: Snake -> Either String (SPotatoFlow, ControllerMeta)
-deserialize_internal Snake {..} = do
-  if _snake_version /= currentVersion 
-    then Left $ "version mismatch, got: " <> show _snake_version <> " expected: " <> show currentVersion
-    else return ()
-  case _snake_format of
-    "json" -> Aeson.eitherDecodeStrict (Text.encodeUtf8 _snake_data)
-    "binary" -> case Binary.decode (LBS.fromStrict $ Text.encodeUtf8 _snake_data) of
-      Just x -> Right x
-      Nothing -> Left "failed to decode binary"
-    x -> Left $ "unrecognized fromat" <> show x
-
-
-decodeFile :: FilePath -> IO (Either String (SPotatoFlow, ControllerMeta))
-decodeFile fp = do
-  vsobs <- LBS.readFile fp
-  return $ deserialize vsobs
+module Potato.Flow.Serialization.Snake (
+  
 
+  module Potato.Flow.Serialization.Versions.V1.SElts
+) where
 
-decodeFileMaybe :: FilePath -> IO (Maybe (SPotatoFlow, ControllerMeta))
-decodeFileMaybe fp = do
-  x <- decodeFile fp
-  case x of
-    Left _ -> return Nothing
-    Right x -> return (Just x)
+import           Potato.Flow.Serialization.Versions.V1.SElts
diff --git a/src/Potato/Flow/Serialization/SnakeWrangler.hs b/src/Potato/Flow/Serialization/SnakeWrangler.hs
new file mode 100644
--- /dev/null
+++ b/src/Potato/Flow/Serialization/SnakeWrangler.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Potato.Flow.Serialization.SnakeWrangler where
+
+import           Relude
+
+import           Potato.Flow.Types
+import Potato.Flow.Controller.Types
+
+import qualified Data.Aeson               as Aeson
+import qualified Data.Aeson.Encode.Pretty as PrettyAeson
+import qualified Data.Binary as Binary
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.Text.Encoding as Text
+
+
+
+-- | list of all version supported
+versions :: [Int]
+versions = [1]
+
+-- | version of the current build
+currentVersion :: Int
+currentVersion = 1
+
+data Snake = Snake {
+  _snake_version :: Int
+  -- string instead of enum type to ensure compatibility if new formats are ever added
+  -- currently supports "binary" and "json"
+  , _snake_format :: String
+  , _snake_data :: Text
+} deriving (Eq, Generic, Show)
+
+instance Aeson.FromJSON Snake
+instance Aeson.ToJSON Snake
+instance Binary.Binary Snake
+instance NFData Snake
+
+data SnakeFormat = SF_Json | SF_Binary
+
+serialize :: SnakeFormat -> (SPotatoFlow, ControllerMeta) -> LBS.ByteString
+serialize f x = r where
+  (inner, format) = case f of
+    SF_Json -> (PrettyAeson.encodePretty x, "json")
+    SF_Binary -> (Binary.encode x, "binary")
+  outer = Snake {
+    _snake_version = currentVersion
+    , _snake_format = format
+    , _snake_data = Text.decodeUtf8 (LBS.toStrict inner)
+  }
+  r = PrettyAeson.encodePretty outer
+
+deserialize :: LBS.ByteString -> Either String (SPotatoFlow, ControllerMeta)
+deserialize lbs = case Aeson.eitherDecode lbs of
+  Left e -> Left $ "failed to decode Snake with error " <> e
+  Right vso -> deserialize_internal vso
+
+
+deserialize_internal :: Snake -> Either String (SPotatoFlow, ControllerMeta)
+deserialize_internal Snake {..} = do
+  if _snake_version /= currentVersion 
+    then Left $ "version mismatch, got: " <> show _snake_version <> " expected: " <> show currentVersion
+    else return ()
+  case _snake_format of
+    "json" -> Aeson.eitherDecodeStrict (Text.encodeUtf8 _snake_data)
+    "binary" -> case Binary.decode (LBS.fromStrict $ Text.encodeUtf8 _snake_data) of
+      Just x -> Right x
+      Nothing -> Left "failed to decode binary"
+    x -> Left $ "unrecognized fromat" <> show x
+
+
+decodeFile :: FilePath -> IO (Either String (SPotatoFlow, ControllerMeta))
+decodeFile fp = do
+  vsobs <- LBS.readFile fp
+  return $ deserialize vsobs
+
+
+decodeFileMaybe :: FilePath -> IO (Maybe (SPotatoFlow, ControllerMeta))
+decodeFileMaybe fp = do
+  x <- decodeFile fp
+  case x of
+    Left _ -> return Nothing
+    Right x -> return (Just x)
diff --git a/src/Potato/Flow/Serialization/Versions/V1/SElts.hs b/src/Potato/Flow/Serialization/Versions/V1/SElts.hs
new file mode 100644
--- /dev/null
+++ b/src/Potato/Flow/Serialization/Versions/V1/SElts.hs
@@ -0,0 +1,460 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Potato.Flow.Serialization.Versions.V1.SElts where
+
+import           Relude
+
+import           Potato.Flow.Math
+import Potato.Data.Text.Unicode
+import Potato.Flow.DebugHelpers
+
+import           Control.Exception (assert)
+import           Data.Aeson
+import           Data.Binary
+import           Data.Default
+import qualified Text.Show
+import qualified Data.Text as T
+import qualified Data.List         as L
+import qualified Data.Map as Map
+import qualified Potato.Data.Text.Zipper as TZ
+import Data.Ratio
+
+
+
+type REltId = Int
+type PChar = Char
+type MPChar = Maybe PChar
+
+getPCharWidth :: Char -> Int8
+getPCharWidth = getCharWidth
+
+
+data FillStyle = FillStyle_Blank | FillStyle_Simple PChar deriving (Eq, Generic, Show)
+
+instance FromJSON FillStyle
+instance ToJSON FillStyle
+instance Binary FillStyle
+instance NFData FillStyle
+
+instance Default FillStyle where
+  def = FillStyle_Simple ' '
+
+-- TODO add line ends?
+-- TODO add line thickness?
+-- TODO add line fill?
+data SuperStyle = SuperStyle {
+  _superStyle_tl           :: MPChar
+  , _superStyle_tr         :: MPChar
+  , _superStyle_bl         :: MPChar
+  , _superStyle_br         :: MPChar
+  , _superStyle_vertical   :: MPChar
+  , _superStyle_horizontal :: MPChar
+  , _superStyle_point      :: MPChar -- used for 1x1 boxes and 1x lines
+  , _superStyle_fill       :: FillStyle
+} deriving (Eq, Generic)
+
+instance FromJSON SuperStyle
+instance ToJSON SuperStyle
+instance Binary SuperStyle
+instance NFData SuperStyle
+
+instance Default SuperStyle where
+  def = SuperStyle {
+    _superStyle_tl = Just '╔'
+    , _superStyle_tr = Just '╗'
+    , _superStyle_bl = Just '╚'
+    , _superStyle_br = Just '╝'
+    , _superStyle_vertical   = Just '║'
+    , _superStyle_horizontal = Just '═'
+    , _superStyle_point = Just '█'
+    , _superStyle_fill = def
+  }
+
+instance Show SuperStyle where
+  show = superStyle_toListFormat
+
+superStyle_fromListFormat :: [PChar] -> SuperStyle
+superStyle_fromListFormat chars = assert (l == 7 || l == 8) $ r where
+  l = length chars
+  r = SuperStyle {
+    _superStyle_tl = Just $ chars L.!! 0
+    , _superStyle_tr = Just $ chars L.!! 1
+    , _superStyle_bl = Just $ chars L.!! 2
+    , _superStyle_br = Just $ chars L.!! 3
+    , _superStyle_vertical   = Just $ chars L.!! 4
+    , _superStyle_horizontal = Just $ chars L.!! 5
+    , _superStyle_point = Just $ chars L.!! 6
+    , _superStyle_fill = if l == 7 then FillStyle_Blank else FillStyle_Simple (chars `debugBangBang` 7)
+  }
+
+-- superStyle_fromListFormat "╔╗╚╝║═█" `shouldBe` def
+-- empty styles are converted to space character
+superStyle_toListFormat :: SuperStyle -> [PChar]
+superStyle_toListFormat SuperStyle {..} = r where
+  mfill = case _superStyle_fill of
+    FillStyle_Blank    -> []
+    FillStyle_Simple c -> [c]
+  r = [
+      fromMaybe ' ' _superStyle_tl
+      ,fromMaybe ' ' _superStyle_tr
+      ,fromMaybe ' ' _superStyle_bl
+      ,fromMaybe ' ' _superStyle_br
+      ,fromMaybe ' ' _superStyle_vertical
+      ,fromMaybe ' ' _superStyle_horizontal
+      ,fromMaybe ' ' _superStyle_point
+    ] <> mfill
+
+-- |
+data TextAlign = TextAlign_Left | TextAlign_Right | TextAlign_Center deriving (Eq, Generic, Show)
+
+instance FromJSON TextAlign
+instance ToJSON TextAlign
+instance Binary TextAlign
+instance NFData TextAlign
+
+instance Default TextAlign where
+  def = TextAlign_Left
+
+convertTextAlignToTextZipperTextAlignment :: TextAlign -> TZ.TextAlignment
+convertTextAlignToTextZipperTextAlignment = \case
+  TextAlign_Left -> TZ.TextAlignment_Left
+  TextAlign_Right -> TZ.TextAlignment_Right
+  TextAlign_Center -> TZ.TextAlignment_Center
+
+-- |
+data TextStyle = TextStyle {
+  -- margins
+  _textStyle_alignment :: TextAlign
+} deriving (Eq, Generic)
+
+instance FromJSON TextStyle
+instance ToJSON TextStyle
+instance Binary TextStyle
+instance NFData TextStyle
+
+instance Default TextStyle where
+  def = TextStyle { _textStyle_alignment = def }
+
+instance Show TextStyle where
+  show TextStyle {..} = show _textStyle_alignment
+
+
+-- TODO you need support for AL_Any and maybe AL_Center
+-- TODO lowercase plz
+data AttachmentLocation = AL_Top | AL_Bot | AL_Left | AL_Right | AL_Any deriving (Eq, Generic, Show)
+
+instance FromJSON AttachmentLocation
+instance ToJSON AttachmentLocation
+instance Binary AttachmentLocation
+instance NFData AttachmentLocation
+
+type AttachmentOffsetRatio = Ratio Int
+
+data Attachment = Attachment {
+  _attachment_target :: REltId
+  , _attachment_location :: AttachmentLocation
+  -- you can prob just delete these, don't think we need them.
+  -- 1 is right/down most, 0 is left/top most, `1 % 2` is the middle
+  , _attachment_offset_rel :: AttachmentOffsetRatio
+} deriving (Eq, Generic, Show)
+
+instance FromJSON Attachment
+instance ToJSON Attachment
+instance Binary Attachment
+instance NFData Attachment
+
+
+attachment_offset_rel_default :: Ratio Int
+attachment_offset_rel_default =  1 % 2
+
+attachment_create_default :: REltId -> AttachmentLocation -> Attachment
+attachment_create_default rid al = Attachment {
+    _attachment_target = rid
+    , _attachment_location = al
+    , _attachment_offset_rel = attachment_offset_rel_default
+  }
+
+-- |
+data SBoxTitle = SBoxTitle {
+  _sBoxTitle_title   :: Maybe Text
+  , _sBoxTitle_align :: TextAlign
+} deriving (Eq, Generic)
+
+instance FromJSON SBoxTitle
+instance ToJSON SBoxTitle
+instance Binary SBoxTitle
+instance NFData SBoxTitle
+
+instance Default SBoxTitle where
+  def = SBoxTitle {
+      _sBoxTitle_title = Nothing
+      , _sBoxTitle_align = def
+    }
+
+instance Show SBoxTitle where
+  show SBoxTitle {..} = "SBoxTitle: " <> show _sBoxTitle_align <> " " <> show _sBoxTitle_title
+
+-- |
+data SBoxText = SBoxText {
+  _sBoxText_text    :: Text
+  , _sBoxText_style :: TextStyle
+} deriving (Eq, Generic)
+
+instance FromJSON SBoxText
+instance ToJSON SBoxText
+instance Binary SBoxText
+instance NFData SBoxText
+
+instance Default SBoxText where
+  def = SBoxText {
+      _sBoxText_text = ""
+      , _sBoxText_style = def
+    }
+
+instance Show SBoxText where
+  show SBoxText {..} = "SBoxText: " <> T.unpack _sBoxText_text <> " " <> show _sBoxText_style
+
+
+data SBoxType = SBoxType_Box | SBoxType_NoBox | SBoxType_BoxText | SBoxType_NoBoxText deriving (Eq, Generic, Show)
+
+instance FromJSON SBoxType
+instance ToJSON SBoxType
+instance Binary SBoxType
+instance NFData SBoxType
+
+instance Default SBoxType where
+  def = SBoxType_Box
+
+sBoxType_isText :: SBoxType -> Bool
+sBoxType_isText sbt = sbt == SBoxType_BoxText || sbt == SBoxType_NoBoxText
+
+sBoxType_hasBorder :: SBoxType -> Bool
+sBoxType_hasBorder sbt = sbt == SBoxType_Box || sbt == SBoxType_BoxText
+
+make_sBoxType :: Bool -> Bool -> SBoxType
+make_sBoxType border text = if border
+  then if text
+    then SBoxType_BoxText
+    else SBoxType_Box
+  else if text
+    then SBoxType_NoBoxText
+    else SBoxType_NoBox
+
+-- |
+data SBox = SBox {
+  _sBox_box       :: LBox
+  , _sBox_superStyle   :: SuperStyle
+  , _sBox_title   :: SBoxTitle
+  , _sBox_text    :: SBoxText
+  , _sBox_boxType :: SBoxType
+} deriving (Eq, Generic)
+
+instance FromJSON SBox
+instance ToJSON SBox
+instance Binary SBox
+instance NFData SBox
+
+instance Default SBox where
+  def = SBox {
+      _sBox_box     = LBox 0 0
+      , _sBox_superStyle = def
+      , _sBox_title = def
+      , _sBox_text  = def
+      , _sBox_boxType = SBoxType_Box
+    }
+
+instance Show SBox where
+  show SBox {..} = "SBox: " <> show _sBox_box <> " " <> show _sBox_title <> " " <> show _sBox_text <> " " <> show _sBox_boxType <> " " <> show _sBox_superStyle
+
+sBox_hasLabel :: SBox -> Bool
+sBox_hasLabel sbox = sBoxType_hasBorder (_sBox_boxType sbox) && (isJust . _sBoxTitle_title ._sBox_title $ sbox)
+
+-- TODO DELETE no longer used with SAutoLine
+data LineAutoStyle =
+  LineAutoStyle_Auto
+  | LineAutoStyle_AutoStraight
+  | LineAutoStyle_StraightAlwaysHorizontal
+  | LineAutoStyle_StraightAlwaysVertical
+  deriving (Eq, Generic, Show)
+
+instance FromJSON LineAutoStyle
+instance ToJSON LineAutoStyle
+instance Binary LineAutoStyle
+instance NFData LineAutoStyle
+
+instance Default LineAutoStyle where
+  def = LineAutoStyle_AutoStraight
+
+
+data LineStyle = LineStyle {
+  _lineStyle_leftArrows    :: Text
+  , _lineStyle_rightArrows :: Text
+  , _lineStyle_upArrows    :: Text
+  , _lineStyle_downArrows  :: Text
+} deriving (Eq, Generic)
+
+instance FromJSON LineStyle
+instance ToJSON LineStyle
+instance Binary LineStyle
+instance NFData LineStyle
+
+instance Default LineStyle where
+  def = LineStyle {
+      _lineStyle_leftArrows    = "<"
+      , _lineStyle_rightArrows = ">"
+      , _lineStyle_upArrows    = "^"
+      , _lineStyle_downArrows  = "v"
+    }
+
+lineStyle_fromListFormat :: ([PChar], [PChar], [PChar], [PChar]) -> LineStyle
+lineStyle_fromListFormat (l,r,u,d) = LineStyle {
+    _lineStyle_leftArrows    = T.pack l
+    , _lineStyle_rightArrows = T.pack r
+    , _lineStyle_upArrows    = T.pack u
+    , _lineStyle_downArrows  = T.pack d
+  }
+
+lineStyle_toListFormat :: LineStyle -> ([PChar], [PChar], [PChar], [PChar])
+lineStyle_toListFormat LineStyle {..} = (T.unpack _lineStyle_leftArrows, T.unpack _lineStyle_rightArrows, T.unpack _lineStyle_upArrows, T.unpack _lineStyle_downArrows)
+
+
+instance Show LineStyle where
+  show ls = r where
+    (a, b, c, d) = lineStyle_toListFormat ls
+    r = "LineStyle: " <> a <> " " <> b <> " " <> c <> " " <> d
+
+-- someday we might have more than one constraint...
+data SAutoLineConstraint = SAutoLineConstraintFixed XY deriving (Eq, Generic, Show)
+
+instance FromJSON SAutoLineConstraint
+instance ToJSON SAutoLineConstraint
+instance Binary SAutoLineConstraint
+instance NFData SAutoLineConstraint
+
+-- TODO provide absolute and relative positioning args
+data SAutoLineLabelPosition =
+  SAutoLineLabelPositionRelative Float -- 0 is at "left" anchor point and 1 is at "right" anchor point
+  deriving (Eq, Generic, Show)
+
+instance FromJSON SAutoLineLabelPosition
+instance ToJSON SAutoLineLabelPosition
+instance Binary SAutoLineLabelPosition
+instance NFData SAutoLineLabelPosition
+
+data SAutoLineLabel = SAutoLineLabel {
+  _sAutoLineLabel_index :: Int -- index relative to _sAutoLine_midpoints for where the midpoint lives
+  , _sAutoLineLabel_position :: SAutoLineLabelPosition
+  , _sAutoLineLabel_text :: Text
+  --, _sAutoLineLabel_vertical :: Bool -- WIP true if vertically oriented
+} deriving (Eq, Generic)
+
+instance Show SAutoLineLabel where
+  show SAutoLineLabel {..} = "SAutoLineLabel: " <> show _sAutoLineLabel_index <> " " <> show _sAutoLineLabel_position <> " " <> show _sAutoLineLabel_text
+
+instance FromJSON SAutoLineLabel
+instance ToJSON SAutoLineLabel
+instance Binary SAutoLineLabel
+instance NFData SAutoLineLabel
+
+instance Default SAutoLineLabel where
+  def = SAutoLineLabel {
+      -- anchor index, text shows AFTER index
+      _sAutoLineLabel_index = 0
+      , _sAutoLineLabel_position = SAutoLineLabelPositionRelative 0
+      , _sAutoLineLabel_text = ""
+      --, _sAutoLineLabel_vertical = False
+    }
+
+
+-- |
+data SAutoLine = SAutoLine {
+  _sAutoLine_start       :: XY
+  , _sAutoLine_end       :: XY
+  , _sAutoLine_superStyle     :: SuperStyle
+
+  -- TODO you need one for start/end of line (LineStyle, LineStyle)
+  , _sAutoLine_lineStyle :: LineStyle
+  , _sAutoLine_lineStyleEnd :: LineStyle
+
+  -- NOTE attachments currently are not guaranteed to exist
+  -- in particular, if you copy a line, delete its target and paste, it will be attached to something that doesn't exist
+  -- tinytools will attempt to correct attachment in some cases but don't get too cozy about it!
+  , _sAutoLine_attachStart :: Maybe Attachment
+  , _sAutoLine_attachEnd :: Maybe Attachment
+
+  , _sAutoLine_midpoints :: [SAutoLineConstraint]
+  , _sAutoLine_labels :: [SAutoLineLabel] -- WIP currently does nothing
+} deriving (Eq, Generic)
+
+instance FromJSON SAutoLine
+instance ToJSON SAutoLine
+instance Binary SAutoLine
+instance NFData SAutoLine
+
+instance Show SAutoLine where
+  show SAutoLine {..} = r where
+    start = maybe (show _sAutoLine_start) show _sAutoLine_attachStart
+    end = maybe (show _sAutoLine_end) show _sAutoLine_attachEnd
+    r = "SAutoLine: " <> start <> " " <> end <> " " <> show _sAutoLine_midpoints <> " " <> show _sAutoLine_labels
+
+-- makes writing tests easier...
+instance Default SAutoLine where
+  def = SAutoLine {
+      _sAutoLine_start       = 0
+      , _sAutoLine_end       = 0
+      , _sAutoLine_superStyle     = def
+      , _sAutoLine_lineStyle = def
+      , _sAutoLine_lineStyleEnd = def
+      , _sAutoLine_attachStart = Nothing
+      , _sAutoLine_attachEnd = Nothing
+      , _sAutoLine_midpoints = []
+      , _sAutoLine_labels = []
+    }
+    
+type TextAreaMapping = Map XY PChar
+
+-- | abitrary text confined to a box
+data STextArea = STextArea {
+  _sTextArea_box           :: LBox
+  , _sTextArea_text        :: TextAreaMapping
+  -- TODO consider using SuperStyle here instead and using Fill property only
+  , _sTextArea_transparent :: Bool
+} deriving (Eq, Generic, Show)
+
+instance Default STextArea where
+  def = STextArea {
+      _sTextArea_box   =        LBox 0 0
+      , _sTextArea_text        = Map.empty
+      , _sTextArea_transparent = True
+    }
+
+instance FromJSON STextArea
+instance ToJSON STextArea
+instance Binary STextArea
+instance NFData STextArea
+
+-- TODO consider removing this all together and serializing Owl stuff directly
+data SElt =
+  SEltNone
+  | SEltFolderStart
+  | SEltFolderEnd
+  | SEltBox SBox
+  | SEltLine SAutoLine
+  | SEltTextArea STextArea
+  deriving (Eq, Generic, Show)
+
+instance FromJSON SElt
+instance ToJSON SElt
+instance Binary SElt
+instance NFData SElt
+
+-- TODO consider removing this all together and serializing Owl stuff directly
+data SEltLabel = SEltLabel {
+ _sEltLabel_name   :: Text
+ , _sEltLabel_sElt :: SElt
+} deriving (Eq, Generic, Show)
+
+instance FromJSON SEltLabel
+instance ToJSON SEltLabel
+instance Binary SEltLabel
+instance NFData SEltLabel
diff --git a/src/Potato/Flow/Types.hs b/src/Potato/Flow/Types.hs
--- a/src/Potato/Flow/Types.hs
+++ b/src/Potato/Flow/Types.hs
@@ -50,7 +50,7 @@
 import           Relude
 
 import           Potato.Flow.Math
-import           Potato.Flow.SElts
+import           Potato.Flow.Serialization.Snake
 
 import           Control.Exception         (assert)
 import           Data.Aeson
diff --git a/test/Potato/Flow/AttachmentsSpec.hs b/test/Potato/Flow/AttachmentsSpec.hs
--- a/test/Potato/Flow/AttachmentsSpec.hs
+++ b/test/Potato/Flow/AttachmentsSpec.hs
@@ -12,7 +12,7 @@
 import Potato.Flow.Methods.LineDrawer
 import Potato.Flow.Methods.LineTypes
 import           Potato.Flow.Math
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 
 spec :: Spec
 spec = do
diff --git a/test/Potato/Flow/Controller/Manipulator/BoxSpec.hs b/test/Potato/Flow/Controller/Manipulator/BoxSpec.hs
--- a/test/Potato/Flow/Controller/Manipulator/BoxSpec.hs
+++ b/test/Potato/Flow/Controller/Manipulator/BoxSpec.hs
@@ -16,6 +16,44 @@
 import           Potato.Flow.Common
 import           Potato.Flow.Controller.Manipulator.TestHelpers
 
+
+constrainDeltaLBox_test :: Spec
+constrainDeltaLBox_test = do
+  it "constrains as expected" $ do
+    constrainDeltaLBox 1 (DeltaLBox 0 (V2 (-10) (-10))) ((LBox 0 (V2 5 5))) `shouldBe` DeltaLBox 0 (V2 (-4) (-4))
+    constrainDeltaLBox 1 (DeltaLBox 0 (V2 (-10) 5)) ((LBox 0 (V2 5 5))) `shouldBe` DeltaLBox 0 (V2 (-4) 5)
+    constrainDeltaLBox 1 (DeltaLBox (V2 10 0) (V2 (-10) 0)) ((LBox 0 (V2 5 5))) `shouldBe` DeltaLBox (V2 4 0) (V2 (-4) 0)
+
+fetchLatestBox :: OwlPFState -> Either Text SBox
+fetchLatestBox pfs = do
+  sowl <- case maybeGetMostRecentlyCreatedOwl' pfs of
+    Nothing -> Left "failed, no 🦉s"
+    Just x  -> Right x
+  case _owlItem_subItem (_superOwl_elt sowl) of
+    OwlSubItemBox x -> Right x
+    x                -> Left $ "expected SBox got: " <> show x
+
+verifyMostRecentlyCreatedBoxLabelHasSize :: (Int, Int) -> GoatTester ()
+verifyMostRecentlyCreatedBoxLabelHasSize (x, y) = verifyStateObjectHasProperty "verifyMostRecentlyCreatedBoxLabelHasSize" fetchLatestBox checkfn where 
+  checkfn sbox = r where
+    LBox _ (V2 x' y') = _sBox_box sbox
+    r = if x == x' && y == y'
+      then Nothing
+      else Just $ "got size " <> show (x', y') <> " expected " <> show (x, y)
+
+verifyMostRecentlyCreatedBoxHasText :: Maybe Text -> GoatTester ()
+verifyMostRecentlyCreatedBoxHasText mt = verifyStateObjectHasProperty "verifyMostRecentlyCreatedBoxHasText" fetchLatestBox checkfn where
+  checkfn sbox = r where
+    istext = sBoxType_isText (_sBox_boxType sbox)
+    r = case 
+      mt of 
+        Nothing -> if istext then Just "expected no text" else Nothing
+        Just t -> if not istext 
+          then Just "expected text" 
+          else if t == _sBoxText_text (_sBox_text sbox)
+            then Nothing 
+            else Just $ "got text " <> show (_sBox_text sbox) <> " expected " <> t
+
 initSimpleBox :: GoatTester ()
 initSimpleBox = drawCanvasBox (0, 0, 100, 100)
 
@@ -26,7 +64,6 @@
 
   -- TODO
 
-
 basic_cancel_test :: Spec
 basic_cancel_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
 
@@ -51,9 +88,42 @@
 
   initSimpleBox
 
+noinvert_test :: Spec
+noinvert_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
 
+  setMarker "draw a box"
+  drawCanvasBox (0, 0, 101, 101)
+
+  setMarker "resize the box"
+  canvasMouseDown (101,101)
+  canvasMouseDown (-10,-10)
+  canvasMouseUp (-10,-10)
+  verifyMostRecentlyCreatedBoxLabelHasSize (1, 1)
+
+  
+boxtext_test :: Spec
+boxtext_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+
+  setMarker "draw a box"
+  drawCanvasBox (0, 0, 101, 101)
+
+  verifyMostRecentlyCreatedBoxHasText Nothing
+
+  setMarker "click in the box to convert it to a text box"
+  canvasMouseDown (50,50)
+  canvasMouseUp (50,50)
+  verifyMostRecentlyCreatedBoxHasText (Just "")
+
+  setMarker "write some text"
+  pressKeys "meow meow meow meow"
+  verifyMostRecentlyCreatedBoxHasText (Just "meow meow meow meow")
+
+
 spec :: Spec
 spec = do
   describe "Box" $ do
     describe "basic" $ basic_test
     describe "basic_cancel" $ basic_cancel_test
+    describe "constrainDeltaLBox" $ constrainDeltaLBox_test
+    describe "noinvert" $ noinvert_test
+    describe "boxtext" $ boxtext_test
diff --git a/test/Potato/Flow/Controller/Manipulator/LayersSpec.hs b/test/Potato/Flow/Controller/Manipulator/LayersSpec.hs
--- a/test/Potato/Flow/Controller/Manipulator/LayersSpec.hs
+++ b/test/Potato/Flow/Controller/Manipulator/LayersSpec.hs
@@ -32,9 +32,23 @@
 verifyLayersCount n =  verifyState ("layers count is " <> show n) $ \s -> if (countentriesfn s) == n then Nothing else Just $ "expected " <> show n <> " elts, got " <> show (countentriesfn s)
   where countentriesfn = Seq.length . _layersState_entries . _goatState_layersState
 
-basic_test :: Spec
-basic_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+verifyNthEntryInLayersIs :: Int -> REltId -> GoatTester()
+verifyNthEntryInLayersIs n rid = verifyState (show n <> " entry in layers is " <> show rid) $ vf where
+  vf s = r where
+    lentries = _layersState_entries . _goatState_layersState $ s
+    mfirstentry = Seq.lookup n lentries
+    r = case mfirstentry of
+      Nothing -> Just $ "no " <> show n <> " entry in layers"
+      Just lentry -> if firstentry == rid then Nothing else Just $ "expected REltId " <> show rid <> ", got " <> show firstentry where
+        firstentry = _superOwl_id . _layerEntry_superOwl $ lentry
 
+verifyFirstEntryInLayersIs :: REltId -> GoatTester()
+verifyFirstEntryInLayersIs = verifyNthEntryInLayersIs 0
+    
+
+create_select_test :: Spec
+create_select_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+
   setMarker "create a folder"
   verifyOwlCount 0
   addFolder someFolderName
@@ -50,10 +64,37 @@
   verifyFolderSelected someFolderName
 
 
+rename_focus_clickCanvas_test :: Spec
+rename_focus_clickCanvas_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
 
-rename_focus_test :: Spec
-rename_focus_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+  setMarker "draw a box"
+  drawCanvasBox (0, 0, 100, 100)
+  verifyOwlCount 1
 
+  setMarker "select the box via layers"
+  layerMouseDownUpRel LMO_Normal 0 0
+  verifySelectionCount 1
+
+  setMarker "begin renaming the box"
+  layerMouseDownUpRel LMO_Normal 0 0
+  pressKeys "aoeu"
+
+  let
+    origname = "<box>"
+    -- NOTE, when when have multi-select, we should auto-select the previous name such that it gets deleted
+    expectedname = "aoeu<box>"
+
+  setMarker "verify the name has not been changed yet (no preview)"
+  verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == origname then Nothing else Just $ "expected name " <> show origname <> " got " <> show (hasOwlItem_name sowl)
+
+  setMarker "change focus by clicking on canvas and ensure rename took effect"
+  canvasMouseDownUp (50, 50)
+  verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == expectedname then Nothing else Just $ "expected name " <> show expectedname <> " got " <> show (hasOwlItem_name sowl)
+
+-- same as rename_focus_clickCanvas_test but calls setFocus instead
+rename_focus_setFocus_test :: GoatFocusedArea -> Spec
+rename_focus_setFocus_test gfa = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+
   setMarker "draw a box"
   drawCanvasBox (0, 0, 100, 100)
   verifyOwlCount 1
@@ -75,7 +116,7 @@
   verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == origname then Nothing else Just $ "expected name " <> show origname <> " got " <> show (hasOwlItem_name sowl)
   
   setMarker "change focus and ensure rename took effect"
-  setFocusArea GoatFocusedArea_Other
+  setFocusArea gfa
   verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == expectedname then Nothing else Just $ "expected name " <> show expectedname <> " got " <> show (hasOwlItem_name sowl)
 
 create_in_folder_and_collapse_test :: Spec
@@ -132,6 +173,7 @@
   layerMouseDownUpRel LMO_Collapse 0 0
   verifyLayersCount 2
 
+
   setMarker "expand the second folder"
   layerMouseDownUpRel LMO_Collapse 1 1
   verifyLayersCount 4
@@ -171,15 +213,135 @@
   canvasMouseDownUp (5,5)
   verifySelectionCount 1
 
+drag_folder_test :: Spec
+drag_folder_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+  setMarker "create a folder"
+  verifyOwlCount 0
+  addFolder someFolderName
+  verifyOwlCount 1
+  verifyFolderSelected someFolderName
+  folderrid <- _superOwl_id <$> mustGetMostRecentlyCreatedOwl 
+  verifyFirstEntryInLayersIs folderrid
 
+  setMarker "create box 1"
+  drawCanvasBox (0,0,10,10)
+  verifySelectionCount 1
+  -- TODO verify the box is in the folder
+  verifyFirstEntryInLayersIs folderrid
+
+  setMarker "unselect the box so that the next box is not in the folder"
+  pressEscape
+  verifySelectionCount 0
+
+  setMarker "create box 2"
+  drawCanvasBox (10,10,10,10)
+  verifySelectionCount 1
+  boxrid <- _superOwl_id <$> mustGetMostRecentlyCreatedOwl 
+  verifyFirstEntryInLayersIs boxrid
+
+  beforeDragTheFolder <- getLayersState
   
 
+  setMarker "select and drag the folder"
+  layerMouseDownUpRel LMO_Normal 1 0
+  layerMouseDownRel LMO_Normal 1 0
+  layerMouseDownRel LMO_Normal 0 0
+  layerMouseUpRel LMO_Normal 0 0
+  -- TODO verify the folder is in the first position in layers
+  verifyFirstEntryInLayersIs folderrid
+
+  beforeUndo <- getLayersState
+
+  setMarker "undo"
+  pressUndo
+  afterPressUndo <- getLayersState
+  verifyEqual "undo state is same as before drag the folder" beforeDragTheFolder afterPressUndo
+
+  setMarker "redo"
+  pressRedo
+  afterPressRedo <- getLayersState
+  verifyEqual "undo state is same as before undo redo" beforeUndo afterPressRedo
+
+drag_folder2_test :: Spec
+drag_folder2_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+
+  setMarker "setup scene"
+  drawCanvasBox (0,0,10,10) -- 1
+  addFolder someFolderName -- 2
+  drawCanvasBox (10,10,10,10) -- 3
+  addFolder "innerfolder" -- 4
+  -- 2testfolder 
+  --   4innerfolder
+  --   3<box>
+  -- 1<box> 
+
+  setMarker "drag second box into inner folder"
+  layerMouseDownUpRel LMO_Normal 2 1
+  layerMouseDownRel LMO_Normal 2 1
+  -- need to drag away first in order to move it into the folder
+  layerMouseDownRel LMO_Normal 1 1
+  layerMouseDownRel LMO_Normal 2 1
+  layerMouseDownRel LMO_Normal 2 1
+  layerMouseUpRel LMO_Normal 2 1
+  verifyNthEntryInLayersIs 2 3
+  -- 2testfolder
+  --   4innerfolder
+  --     3<box>
+  -- 1<box>
+
+  beforeDrag <- getLayersState
+
+  -- DELETE
+  --failWithMessage ("\n" <> show beforeDrag)
+
+  setMarker "drag inner folder outside of outer folder"
+  layerMouseDownUpRel LMO_Normal 1 1
+  layerMouseDownRel LMO_Normal 1 1
+  layerMouseDownRel LMO_Normal 5 0
+  layerMouseUpRel LMO_Normal 5 0
+  verifyNthEntryInLayersIs 2 4
+  -- 2testfolder
+  -- 1<box>
+  -- 4innerfolder
+  --   3<box>
+
+  -- DELETE
+  --afterDrag <- getLayersState
+  --failWithMessage ("\n" <> show afterDrag)
+
+  setMarker "undo"
+  pressUndo
+  afterUndo <- getLayersState
+  verifyEqual "undo state is same as before drag the folder" beforeDrag afterUndo
+
+  -- DELETE
+  --failWithMessage ("\n" <> show afterUndo)
+
+  setMarker "collapse the inner folder"
+  layerMouseDownUpRel LMO_Collapse 1 1
+
+  -- DELETE
+  --ls <- getLayersState
+  --failWithMessage ("\n" <> show ls)
+
+  verifyNthEntryInLayersIs 1 4
+  verifyNthEntryInLayersIs 2 1
+  -- 2testfolder
+  --   4innerfolder
+  -- 1<box>
+  
+
+
 spec :: Spec
 spec = do
   describe "Layers" $ do
-    describe "basic" $ basic_test
-    describe "rename_focus_test" $ rename_focus_test
+    describe "create_select_test" $ create_select_test
+    describe "rename_focus_clickCanvas_test" $ rename_focus_clickCanvas_test 
+    describe "rename_focus_setFocus_test canvas" $ rename_focus_setFocus_test GoatFocusedArea_Canvas
+    describe "rename_focus_setFocus_test other" $ rename_focus_setFocus_test GoatFocusedArea_Other
     describe "create_in_folder_and_collapse_test" $ create_in_folder_and_collapse_test
     describe "folder_collapse_test" $ folder_collapse_test
-    describe "hide_select_test" $ lock_or_hide_select_test LMO_Hide
-    describe "lock_select_test" $ lock_or_hide_select_test LMO_Lock
+    describe "lock_or_hide_select_test hide" $ lock_or_hide_select_test LMO_Hide
+    describe "lock_or_hide_select_test lock" $ lock_or_hide_select_test LMO_Lock
+    describe "drag_folder_test" $ drag_folder_test
+    describe "drag_folder2_test" $ drag_folder2_test
diff --git a/test/Potato/Flow/Controller/Manipulator/PanSpec.hs b/test/Potato/Flow/Controller/Manipulator/PanSpec.hs
--- a/test/Potato/Flow/Controller/Manipulator/PanSpec.hs
+++ b/test/Potato/Flow/Controller/Manipulator/PanSpec.hs
@@ -25,7 +25,16 @@
       pan = (x,y)
 
 
+verifyScreenSize :: V2 Int -> GoatTester ()
+verifyScreenSize expected = verifyState "verifyScreenSize" f where
+  f gs = if screensize == expected
+    then Nothing
+    else Just $ "got " <> show screensize <> " expected " <> show expected
+    where
+      screensize = _goatState_screenRegion gs
 
+
+
 -- TODO broken...
 basic_test :: Spec
 basic_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
@@ -34,17 +43,22 @@
   drawCanvasBox (0, 0, 10, 10)
   -- TODO verify box position
 
-  let pan = (5, -7)
+  let 
+    pan1 = (1, 1)
+    pan2 = (5, -7)
+    -- after panning to pan1 when we pan to pan2 it's actually panning to pan2+pan1
+    pan2p1 = (6,-6)
   setMarker "pan"
   verifyPan (0,0)
   setTool Tool_Pan
   canvasMouseDown (0, 0)
-  canvasMouseDown (1, 1)
-  verifyPan (1, 1)
-  canvasMouseDown pan
-  verifyPan pan
-  canvasMouseUp pan
-  verifyPan pan
+  canvasMouseDown pan1
+  verifyPan pan1
+  canvasMouseDown pan2
+  verifyPan pan2p1
+  -- now we are at 
+  canvasMouseUp (0, 0)
+  verifyPan pan2p1
 
   setMarker "draw a box"
   drawCanvasBox (0, 0, 10, 10)
@@ -82,9 +96,45 @@
   verifyOwlCount 1
 
 
+pan_resize_pan_test :: Spec
+pan_resize_pan_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do
+
+  let
+    pan = (5, 10)
+    minuspan = (-5, -10)
+    newscreensize = V2 150 150
+
+  setMarker "draw a box"
+  drawCanvasBox (0, 0, 10, 10)
+  
+  setMarker "pan"
+  verifyPan (0,0)
+  setTool Tool_Pan
+  canvasMouseDown (0, 0)
+  canvasMouseUp pan
+  verifyPan pan
+
+  setMarker "resize"
+  verifyScreenSize goatTesterInitialScreenSize
+  resizeScreen newscreensize
+  verifyScreenSize newscreensize
+  
+  setMarker "pan back"
+  setTool Tool_Pan
+  canvasMouseDown (0, 0)
+  canvasMouseUp minuspan
+  verifyPan (0, 0)
+  -- TODO verify box position on screen
+
+  setMarker "resize back"
+  resizeScreen goatTesterInitialScreenSize
+  verifyScreenSize goatTesterInitialScreenSize
+  -- TODO verify something
+
 spec :: Spec
 spec = do
   describe "Pan" $ do
-    --describe "basic_test" $ basic_test
+    describe "basic_test" $ basic_test
     describe "cancel_test" $ cancel_test
     describe "middle_button_pan_test" $ middle_button_pan_test
+    describe "pan_resize_pan_test" $ pan_resize_pan_test
diff --git a/test/Potato/Flow/Deprecated/Controller/Manipulator/BoxTextSpec.hs b/test/Potato/Flow/Deprecated/Controller/Manipulator/BoxTextSpec.hs
--- a/test/Potato/Flow/Deprecated/Controller/Manipulator/BoxTextSpec.hs
+++ b/test/Potato/Flow/Deprecated/Controller/Manipulator/BoxTextSpec.hs
@@ -265,7 +265,7 @@
       , EWCLabel "exit BoxText"
       , EWCKeyboard (KeyboardData KeyboardKey_Esc [])
 
-      , EWCLabel "resize box to 0"
+      , EWCLabel "resize box to 1"
       , EWCMouse (LMouseData (V2 12 12) False MouseButton_Left [] False)
       , EWCMouse (LMouseData (V2 10 10) False MouseButton_Left [] False)
       , EWCMouse (LMouseData (V2 10 10) True MouseButton_Left [] False)
@@ -287,11 +287,11 @@
       , LabelCheck "exit BoxText"
       , checkHandlerNameAndState handlerName_box False
 
-      , LabelCheck "resize box to 0"
+      , LabelCheck "resize box to 1"
       , AlwaysPass
       , AlwaysPass
       , firstSuperOwlPredicate (Just "<text>") $ \sowl -> case hasOwlItem_toSElt_hack sowl of
-        SEltBox (SBox lbox _ _ _ _) -> lbox == LBox (V2 10 10) (V2 0 0)
+        SEltBox (SBox lbox _ _ _ _) -> lbox == LBox (V2 10 10) (V2 1 1)
         _                           -> False
 
     ]
diff --git a/test/Potato/Flow/GoatTester.hs b/test/Potato/Flow/GoatTester.hs
--- a/test/Potato/Flow/GoatTester.hs
+++ b/test/Potato/Flow/GoatTester.hs
@@ -20,6 +20,10 @@
 import           Data.Tuple.Extra
 
 
+goatTesterInitialScreenSize :: V2 Int
+goatTesterInitialScreenSize = V2 100 100
+
+
 type GoatTesterTrackingState = (Text, Int, Int) -- (marker, operations since start of test, operations since last marker)
 
 -- TODO include ref to rendered canvas and then you can render it on failure cases!!
@@ -42,7 +46,7 @@
 
 instance Default GoatTesterState where
   def = GoatTesterState {
-      _goatTesterState_goatState = makeGoatState (V2 100 100) (emptyOwlPFState, emptyControllerMeta)
+      _goatTesterState_goatState = makeGoatState goatTesterInitialScreenSize (emptyOwlPFState, emptyControllerMeta)
       , _goatTesterState_rawOperationCount = 0
       , _goatTesterState_marker = "__START__"
       , _goatTesterState_rawOperationCountSinceLastMarker = 0
@@ -99,6 +103,14 @@
 verify desc mf = GoatTesterT $ do
   unGoatTesterT $ putRecord desc mf
 
+verifyBool :: (Monad m) => Text -> Bool -> GoatTesterT m ()
+verifyBool desc b = GoatTesterT $ do
+  unGoatTesterT $ putRecord desc (if b then Nothing else Just "failed")
+
+verifyEqual :: (Monad m, Show a, Eq a) => Text -> a -> a -> GoatTesterT m ()
+verifyEqual desc a b = GoatTesterT $ do
+  unGoatTesterT $ putRecord desc (if a == b then Nothing else Just $ "expected: " <> show a <> "\ngot: " <> show b)
+
 verifyState' :: (Monad m) => Text -> (GoatState -> Maybe Text) -> GoatTesterT m Bool
 verifyState' desc fn = GoatTesterT $ do
   gts <- get
@@ -112,6 +124,9 @@
 --verifyStateFatal :: (GoatState -> Maybe Text) -> GoatTesterT m ()
 --verifyStateFatal = undefined
 
+failWithMessage :: (Monad m) => Text -> GoatTesterT m ()
+failWithMessage = putRecord "failed: " . Just
+
 runGoatTesterT :: (Monad m) => GoatState -> GoatTesterT m a -> m [GoatTesterRecord]
 runGoatTesterT gs m = do
   gts <- execStateT (unGoatTesterT m) $ def { _goatTesterState_goatState = gs }
@@ -166,6 +181,10 @@
   pfs <- getOwlPFState
   return $ fromJust $ maybeGetMostRecentlyCreatedOwl' pfs
 
+getLayersState :: (Monad m) => GoatTesterT m LayersState
+getLayersState = GoatTesterT $ do
+  gts <- get
+  return $ _goatState_layersState $ _goatTesterState_goatState gts
 
 -- operation helpers
 
@@ -276,9 +295,12 @@
 pressRedo :: (Monad m) => GoatTesterT m ()
 pressRedo = runEndo $ endoGoatCmdKeyboard  (KeyboardData (KeyboardKey_Char 'y') [KeyModifier_Ctrl])
 
-
 markSaved :: (Monad m) => GoatTesterT m ()
 markSaved = runEndo $ endoGoatCmdMarkSaved ()
+
+resizeScreen :: (Monad m) => V2 Int -> GoatTesterT m ()
+resizeScreen (V2 w h) = runEndo $ endoGoatCmdSetCanvasRegionDim (V2 w h)
+
 
 -- verification helpers
 
diff --git a/test/Potato/Flow/Methods/SEltMethodsSpec.hs b/test/Potato/Flow/Methods/SEltMethodsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Potato/Flow/Methods/SEltMethodsSpec.hs
@@ -0,0 +1,119 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# OPTIONS_GHC -fno-warn-unused-local-binds #-}
+
+module Potato.Flow.Methods.SEltMethodsSpec(
+  spec
+) where
+
+import           Relude       hiding (empty, fromList)
+
+import           Test.Hspec
+
+import qualified Data.Map as Map
+import           Data.Default (def)
+
+import           Potato.Flow
+
+
+spec :: Spec
+spec = do
+  let
+    testsstyle = def { _superStyle_fill = FillStyle_Simple '@' }
+    renderfn sd = _sEltDrawer_renderFn sd emptyOwlTree
+  describe "SEltMethod" $ do
+    describe "getDrawerFromSEltForTest" $ do
+      describe "SBox" $ do
+        let
+          somesbox1 style = def {
+              _sBox_box       = LBox 0 (V2 5 5)
+              , _sBox_text    = def {
+                  _sBoxText_text = "m ow meow meow"
+                }
+              , _sBox_boxType = style
+              , _sBox_superStyle = testsstyle
+            }
+          somesbox2 style = def {
+              _sBox_box       = LBox 0 (V2 1 1)
+              , _sBox_boxType = style
+              , _sBox_superStyle = testsstyle
+            }
+          somesbox3 style = def {
+              _sBox_box       = LBox 0 (V2 10 10)
+              , _sBox_text    = def {
+                  _sBoxText_text = "m ow meeuew ee  meow hello boop no12 meow whatever"
+                  , _sBoxText_style = TextStyle TextAlign_Right
+                }
+              , _sBox_boxType = style
+              , _sBox_superStyle = testsstyle
+            }
+          somesbox4 style = def {
+              _sBox_box       = LBox 0 (V2 10 10)
+              , _sBox_text    = def {
+                  _sBoxText_text = "ｔｈｅｒｅ　ａｒｅ　ｎｏ　ｓｐａｃｅ　ｂｅｔｗｅｅｎ　ａｄｊａｃｅｎｔ　ｃｈａｒａｃｔｅｒｓ"
+                  , _sBoxText_style = TextStyle TextAlign_Left
+                }
+              , _sBox_boxType = style
+              , _sBox_superStyle = testsstyle
+            }
+          somesbox5 style = def {
+            _sBox_box       = LBox 0 (V2 10 5)
+            , _sBox_text    = def {
+                _sBoxText_text = "aoeuaoeu"
+              }
+            , _sBox_title = SBoxTitle {
+                _sBoxTitle_title = Just "boop"
+                , _sBoxTitle_align = TextAlign_Right
+              }
+            , _sBox_boxType = style
+            , _sBox_superStyle = testsstyle
+          }
+        it "SBoxType_NoBoxText" $ do
+          let
+            sd = getDrawerFromSEltForTest (SEltBox (somesbox1 SBoxType_NoBoxText))
+          renderfn sd (V2 100 0) `shouldBe` Nothing
+          renderfn sd (V2 (-1) 0) `shouldBe` Nothing
+          renderfn sd (V2 0 0) `shouldBe` Just 'm'
+          renderfn sd (V2 1 0) `shouldBe` Just ' '
+          renderfn sd (V2 2 0) `shouldBe` Just 'o'
+          -- TODO it seems EOL space characters are not rendered and this gives the fill char. Maybe this is desired behavior thought?
+          --renderfn sd (V2 3 0) `shouldBe` Just ' '
+          renderfn sd (V2 1 1) `shouldBe` Just 'e'
+          renderfn sd (V2 4 4) `shouldBe` Just '@'
+        it "SBoxType_Box" $ do
+          let
+            sd = getDrawerFromSEltForTest (SEltBox (somesbox2 SBoxType_Box))
+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
+          renderfn sd (V2 0 0) `shouldBe` _superStyle_point def
+        it "SBoxType_NoBoxText_alignRight" $ do
+          let
+            sd = getDrawerFromSEltForTest (SEltBox (somesbox3 SBoxType_NoBoxText))
+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
+          renderfn sd (V2 0 0) `shouldBe` Just '@'
+        it "SBoxType_NoBoxText_widechar" $ do
+          let
+            sd = getDrawerFromSEltForTest (SEltBox (somesbox4 SBoxType_NoBoxText))
+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
+          renderfn sd (V2 0 0) `shouldBe` Just 'ｔ'
+          renderfn sd (V2 1 0) `shouldBe` Nothing
+          renderfn sd (V2 2 0) `shouldBe` Just 'ｈ'
+          renderfn sd (V2 3 0) `shouldBe` Nothing
+        it "box label" $ do
+          let
+            sd = getDrawerFromSEltForTest (SEltBox (somesbox5 SBoxType_BoxText))
+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
+          renderfn sd (V2 0 0) `shouldBe` Just '╔'
+          renderfn sd (V2 8 0) `shouldBe` Just 'p'
+      describe "STextArea" $ do
+        let
+          sometextarea = STextArea {
+              _sTextArea_box         = LBox (V2 1 1) (V2 10 10)
+              , _sTextArea_text      = Map.fromList [((V2 0 0),'a'),((V2 1 1),'b'),((V2 9 9),'c')]
+              , _sTextArea_transparent = False
+            }
+        it "basic" $ do
+          let
+            sd = getDrawerFromSEltForTest (SEltTextArea $ sometextarea)
+          --forM_ ((sEltDrawer_renderToLines sd) emptyOwlTree sd) putTextLn
+          renderfn sd (V2 1 1) `shouldBe` Just 'a'
+          renderfn sd (V2 10 10) `shouldBe` Just 'c'
+          renderfn sd (V2 100 0) `shouldBe` Nothing
diff --git a/test/Potato/Flow/OwlSpec.hs b/test/Potato/Flow/OwlSpec.hs
--- a/test/Potato/Flow/OwlSpec.hs
+++ b/test/Potato/Flow/OwlSpec.hs
@@ -15,7 +15,7 @@
 import           Potato.Flow.Deprecated.TestStates
 import           Potato.Flow.Deprecated.State
 import Potato.Flow.Types
-import Potato.Flow.SElts
+import Potato.Flow.Serialization.Snake
 
 
 -- | 'owlTree_toSEltTree' may change SEltFolderEnd name and REltId during reconstruction
@@ -55,8 +55,8 @@
         let
           sowl1 = owlTree_mustFindSuperOwl owlTree0 2 -- b1
           sowl2 = owlTree_mustFindSuperOwl owlTree0 1 -- fstart2
-          ot1 = owlTree_removeSuperOwl sowl1 owlTree0
-          ot2 = owlTree_removeSuperOwl sowl2 owlTree0
+          ot1 = owlTree_removeSuperOwl False sowl1 owlTree0
+          ot2 = owlTree_removeSuperOwl False sowl2 owlTree0
         owlTree_owlCount ot1 `shouldBe` 8
         owlTree_owlCount ot2 `shouldBe` 4
         fst (owlTree_validate ot1) `shouldBe` True
diff --git a/test/Potato/Flow/SEltMethodsSpec.hs b/test/Potato/Flow/SEltMethodsSpec.hs
deleted file mode 100644
--- a/test/Potato/Flow/SEltMethodsSpec.hs
+++ /dev/null
@@ -1,119 +0,0 @@
-{-# LANGUAGE RecordWildCards #-}
-{-# OPTIONS_GHC -fno-warn-unused-local-binds #-}
-
-module Potato.Flow.SEltMethodsSpec(
-  spec
-) where
-
-import           Relude       hiding (empty, fromList)
-
-import           Test.Hspec
-
-import qualified Data.Map as Map
-import           Data.Default (def)
-
-import           Potato.Flow
-
-
-spec :: Spec
-spec = do
-  let
-    testsstyle = def { _superStyle_fill = FillStyle_Simple '@' }
-    renderfn sd = _sEltDrawer_renderFn sd emptyOwlTree
-  describe "SEltMethod" $ do
-    describe "getDrawerFromSEltForTest" $ do
-      describe "SBox" $ do
-        let
-          somesbox1 style = def {
-              _sBox_box       = LBox 0 (V2 5 5)
-              , _sBox_text    = def {
-                  _sBoxText_text = "m ow meow meow"
-                }
-              , _sBox_boxType = style
-              , _sBox_superStyle = testsstyle
-            }
-          somesbox2 style = def {
-              _sBox_box       = LBox 0 (V2 1 1)
-              , _sBox_boxType = style
-              , _sBox_superStyle = testsstyle
-            }
-          somesbox3 style = def {
-              _sBox_box       = LBox 0 (V2 10 10)
-              , _sBox_text    = def {
-                  _sBoxText_text = "m ow meeuew ee  meow hello boop no12 meow whatever"
-                  , _sBoxText_style = TextStyle TextAlign_Right
-                }
-              , _sBox_boxType = style
-              , _sBox_superStyle = testsstyle
-            }
-          somesbox4 style = def {
-              _sBox_box       = LBox 0 (V2 10 10)
-              , _sBox_text    = def {
-                  _sBoxText_text = "ｔｈｅｒｅ　ａｒｅ　ｎｏ　ｓｐａｃｅ　ｂｅｔｗｅｅｎ　ａｄｊａｃｅｎｔ　ｃｈａｒａｃｔｅｒｓ"
-                  , _sBoxText_style = TextStyle TextAlign_Left
-                }
-              , _sBox_boxType = style
-              , _sBox_superStyle = testsstyle
-            }
-          somesbox5 style = def {
-            _sBox_box       = LBox 0 (V2 10 5)
-            , _sBox_text    = def {
-                _sBoxText_text = "aoeuaoeu"
-              }
-            , _sBox_title = SBoxTitle {
-                _sBoxTitle_title = Just "boop"
-                , _sBoxTitle_align = TextAlign_Right
-              }
-            , _sBox_boxType = style
-            , _sBox_superStyle = testsstyle
-          }
-        it "SBoxType_NoBoxText" $ do
-          let
-            sd = getDrawerFromSEltForTest (SEltBox (somesbox1 SBoxType_NoBoxText))
-          renderfn sd (V2 100 0) `shouldBe` Nothing
-          renderfn sd (V2 (-1) 0) `shouldBe` Nothing
-          renderfn sd (V2 0 0) `shouldBe` Just 'm'
-          renderfn sd (V2 1 0) `shouldBe` Just ' '
-          renderfn sd (V2 2 0) `shouldBe` Just 'o'
-          -- TODO it seems EOL space characters are not rendered and this gives the fill char. Maybe this is desired behavior thought?
-          --renderfn sd (V2 3 0) `shouldBe` Just ' '
-          renderfn sd (V2 1 1) `shouldBe` Just 'e'
-          renderfn sd (V2 4 4) `shouldBe` Just '@'
-        it "SBoxType_Box" $ do
-          let
-            sd = getDrawerFromSEltForTest (SEltBox (somesbox2 SBoxType_Box))
-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
-          renderfn sd (V2 0 0) `shouldBe` _superStyle_point def
-        it "SBoxType_NoBoxText_alignRight" $ do
-          let
-            sd = getDrawerFromSEltForTest (SEltBox (somesbox3 SBoxType_NoBoxText))
-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
-          renderfn sd (V2 0 0) `shouldBe` Just '@'
-        it "SBoxType_NoBoxText_widechar" $ do
-          let
-            sd = getDrawerFromSEltForTest (SEltBox (somesbox4 SBoxType_NoBoxText))
-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
-          renderfn sd (V2 0 0) `shouldBe` Just 'ｔ'
-          renderfn sd (V2 1 0) `shouldBe` Nothing
-          renderfn sd (V2 2 0) `shouldBe` Just 'ｈ'
-          renderfn sd (V2 3 0) `shouldBe` Nothing
-        it "box label" $ do
-          let
-            sd = getDrawerFromSEltForTest (SEltBox (somesbox5 SBoxType_BoxText))
-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn
-          renderfn sd (V2 0 0) `shouldBe` Just '╔'
-          renderfn sd (V2 8 0) `shouldBe` Just 'p'
-      describe "STextArea" $ do
-        let
-          sometextarea = STextArea {
-              _sTextArea_box         = LBox (V2 1 1) (V2 10 10)
-              , _sTextArea_text      = Map.fromList [((V2 0 0),'a'),((V2 1 1),'b'),((V2 9 9),'c')]
-              , _sTextArea_transparent = False
-            }
-        it "basic" $ do
-          let
-            sd = getDrawerFromSEltForTest (SEltTextArea $ sometextarea)
-          --forM_ ((sEltDrawer_renderToLines sd) emptyOwlTree sd) putTextLn
-          renderfn sd (V2 1 1) `shouldBe` Just 'a'
-          renderfn sd (V2 10 10) `shouldBe` Just 'c'
-          renderfn sd (V2 100 0) `shouldBe` Nothing
diff --git a/tinytools.cabal b/tinytools.cabal
--- a/tinytools.cabal
+++ b/tinytools.cabal
@@ -1,17 +1,14 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.34.4.
---
--- see: https://github.com/sol/hpack
-
 name:           tinytools
-version:        0.1.0.4
+version:        0.1.0.5
 description:    tinytools is a mono-space unicode diagram editor
 homepage:       https://github.com/minimapletinytools/tinytools#readme
 bug-reports:    https://github.com/minimapletinytools/tinytools/issues
 author:         minimaple
 maintainer:     minimapletinytools@gmail.com
-synopsis:       tinytools is a mono-space unicode diagram editor
+synopsis:       tinytools is a monospace unicode diagram editor
+category:       User Interfaces
 copyright:      2023 minimaple (Peter Lu)
 license:        BSD3
 license-file:   LICENSE
@@ -72,9 +69,10 @@
       Potato.Flow.Reflex.GoatWidget
       Potato.Flow.Render
       Potato.Flow.RenderCache
-      Potato.Flow.SEltMethods
-      Potato.Flow.SElts
+      Potato.Flow.Methods.SEltMethods
       Potato.Flow.Serialization.Snake
+      Potato.Flow.Serialization.SnakeWrangler
+      Potato.Flow.Serialization.Versions.V1.SElts
       Potato.Flow.TestStates
       Potato.Flow.Types
   hs-source-dirs:
@@ -143,12 +141,11 @@
     , pretty-simple
     , random-shuffle
     , ref-tf
-    , reflex >= 0.8 && <1
+    , reflex >= 0.9.2 && < 1
     , reflex-potatoes >=0.1
-    , reflex-test-host >=0.1.2
+    , reflex-test-host >=0.1.2.3
     , relude
     , semialign
-    , template-haskell
     , text
     , text-icu
     , these
@@ -190,7 +187,7 @@
       Potato.Flow.Methods.LineDrawerSpec
       Potato.Flow.OwlSpec
       Potato.Flow.RenderSpec
-      Potato.Flow.SEltMethodsSpec
+      Potato.Flow.Methods.SEltMethodsSpec
       Paths_tinytools
   hs-source-dirs:
       test
@@ -260,12 +257,11 @@
     , pretty-simple
     , random-shuffle
     , ref-tf
-    , reflex >= 0.8 && <1
+    , reflex >= 0.9.2 && < 1
     , reflex-potatoes >=0.1
-    , reflex-test-host >=0.1.2
+    , reflex-test-host >=0.1.2.3
     , relude
     , semialign
-    , template-haskell
     , text
     , text-icu
     , these
