tinytools-vty 0.1.0.1 → 0.1.0.3
raw patch · 7 files changed
+98/−41 lines, 7 filesdep ~tinytoolsnew-component:exe:tinytools
Dependency ranges changed: tinytools
Files
- ChangeLog.md +2/−3
- README.md +61/−4
- app/Main.hs +2/−1
- src/Potato/Flow/TutorialState.hs +5/−6
- src/Potato/Flow/Vty/Main.hs +18/−18
- tinytools-vty.cabal +5/−8
- tutorial.potato +5/−1
ChangeLog.md view
@@ -1,10 +1,9 @@ # Changelog for tinytools-vty -## [Unreleased]--## [0.1.0.1]+## [0.1.0.2] ### Added - minor stuff to fix build+- Snake ## [0.1.0.0] - alpha ### Added
README.md view
@@ -1,19 +1,76 @@ # tinytools-vty -`tinytools-vty` is a mono-space unicode diagram editor written in Haskell. It is currently a WIP and expect to have an beta release SOON.+`tinytools-vty` is a mono-space unicode diagram editor written in Haskell -This repository contains the vty based view/controller implementation built on top of the [tinytools](https://github.com/pdlla/tinytools) model.+This repository contains the [reflex-vty](https://hackage.haskell.org/package/reflex-vty) based view/controller implementation built on top of the [tinytools](https://github.com/pdlla/tinytools) model. # running -`cabal run tinytools-vty-exe`+To install tiny tools run `cabal install exe:tinytools` and then run `tinytools` -you may need to install ICU depuendencies to get things to compile+Or if you are building locally then `cabal run tinytools` +# usage++When you first run `tinytools` a tutorial file will open which contains information on how to use `tinytools-vty`. This same tutorial is copied at the bottom of this README file.++`tinytools-vty` has a very intuitive interface and most operations should be clear.++hotkeys are supported, however sometimes they get captured by the OS or the terminal and never sent into the executable :(.+ # enabling unicode widechar support ## NOT WORKING, WILL CRASH RANDOMLY IF YOU USE UNICODE WIDE CHARS 😨 (this is due to bugs in TextZipper module that I still need to fix) Unicode character display width seems to vary by terminal so you will need to generate a unicode width table file in order to enable support for unicode wide characters. You can run `tinytools-vty` with the `--widthtable` arg to generate the table to your local config directory for the current terminal. Generating the table samples each unicode character inside the terminal and takes a few seconds to run. Please see the `Graphics.Vty.UnicodeWidthTable` module of the [vty](https://hackage.haskell.org/package/vty) for more info.++# tutorial++```+ ╔══════════════════════════════╗ + ║ ║ ╔IMPORTANT#########################╗ + ███████████████████ ╚>#to explore the canvas, click the # + █THIS IS TINYTOOLS█═╗ #[(p)an] button or press p # + ███████████████████ ║ # # + v #then click and drag in the canvas # + <═══╗ ╔══════════════════════╗ #area to move your view # + ║ ║the menu on the left ║ # # + ╚═══║has a lot of important║═══╗ # # + ║operations ║ ║ ╚##################################╝ + ╚══════════════════════╝ ║ ^ + ║ ║ ║ + v ║ ║ + ╔═══════════════════════════╗ ║ ║ + ║to draw a box, click the ║ ║ ║ + ║[(b)ox] button or press b ║ ║ ╚════════════════════╗ + ║ ║ ║ ║ + ║then click and drag in the ║ ║ ║ + ║canvas area ║ ║ *------------------* ║ + ╚═══════════════════════════╝ ║ |this area (to the | ║ + ║ ╚═══════════════════════>|right) is called |═╝ + ║ |the canvas | + v | | + ╔═════════════════════════════════╗ | | + ║after creating a box, it will be ║ | | + ║selected, you can use the options║ *------------------* + ║in the bottom right to change its║ + ╔═════║style. you can also convert your ║ + <═══╝ ║box into a text box or remove its║ ╔═══════════════════════════════╗ + ║border ║ ║of course there are many more ║ + ║ ║ ║features, play around! ║ + ║ ║ ║ ║ + ╚═════════════════════════════════╝ ║ ║ + ║ ║ + ║ ║ + ║ ║ + ║ ║ + ║ ║ + ║ ║ + ║ ║ + ║ ║ + ║ ║ + ╚═══════════════════════════════╝ ++```
app/Main.hs view
@@ -101,7 +101,7 @@ case mTermName of Just termName -> do configdir <- tinytoolsConfigDir- let + let fn = configdir </> (termName <> "_termwidthfile") rslt <- try $ do wt <- V.buildUnicodeWidthTable V.defaultUnicodeTableUpperBound@@ -130,6 +130,7 @@ -- TODO load tutorial here owlpfstate_tutorial --else owlpfstate_attachments1 else tutorialState+ , _mainPFWidgetConfig_showWelcome = True } -- set the title
src/Potato/Flow/TutorialState.hs view
@@ -6,6 +6,7 @@ import Relude import Potato.Flow+import qualified Potato.Flow.Serialization.Snake as Snake import Data.ByteString import qualified Data.ByteString.Lazy as LBS@@ -15,15 +16,13 @@ import qualified Data.Aeson as Aeson import Potato.Flow.TestStates ++ tutorialState :: (OwlPFState, ControllerMeta)-tutorialState = fromMaybe (owlpfstate_newProject, emptyControllerMeta) . fmap (\(x, cm) -> (sPotatoFlow_to_owlPFState x, cm)) $ case Aeson.eitherDecode (LBS.fromStrict tutorialjson) of- --Left e -> error (T.pack e) -- Nothing+tutorialState = fromMaybe (owlpfstate_newProject, emptyControllerMeta) . fmap (\(x, cm) -> (sPotatoFlow_to_owlPFState x, cm)) $ case Snake.deserialize (LBS.fromStrict tutorialjson) of+ --Left e -> trace e Nothing Left e -> Nothing Right j -> Just j tutorialjson :: ByteString tutorialjson = $(embedFile "tutorial.potato")----
src/Potato/Flow/Vty/Main.hs view
@@ -13,6 +13,7 @@ import Potato.Flow+import qualified Potato.Flow.Serialization.Snake as Snake import Potato.Flow.TestStates import Potato.Flow.Vty.Canvas import Potato.Flow.Vty.Input@@ -31,6 +32,7 @@ import Potato.Flow.Vty.AppKbCmd import Potato.Flow.Vty.Attrs + import System.Console.ANSI (hSetTitle) import qualified System.FilePath as FP import qualified System.Directory as FP@@ -38,10 +40,9 @@ import Control.Concurrent import Control.Monad.NodeId import Control.Exception (handle)-import qualified Data.Aeson as Aeson-import qualified Data.Aeson.Encode.Pretty as PrettyAeson import Data.Maybe import Data.Default+import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT@@ -189,7 +190,7 @@ postBuildEv <- getPostBuild welcomeMessageEv <- fetchMOTDAsync postBuildEv welcomeMessageDyn <- holdDyn "loading..." welcomeMessageEv- boxTitle (constant def) "😱😱😱" $ do+ boxTitle (constant def) "😱 tinytools-vty (beta) 😱" $ do initLayout $ col $ do (grout . stretch) 1 $ text (current welcomeMessageDyn) (grout . fixed) 3 $ textButton def (constant "bye")@@ -236,6 +237,7 @@ , _mainPFWidgetConfig_homeDirectory :: FP.FilePath -- should this include controller meta too? , _mainPFWidgetConfig_initialState :: (OwlPFState, ControllerMeta) -- ^ will be overriden by initialFile if set+ , _mainPFWidgetConfig_showWelcome :: Bool } instance Default MainPFWidgetConfig where@@ -244,6 +246,7 @@ --, _mainPFWidgetConfig_homeDirectory = "/" , _mainPFWidgetConfig_homeDirectory = "/home/minimaple/kitchen/faucet/potato-flow-vty" , _mainPFWidgetConfig_initialState = (emptyOwlPFState, emptyControllerMeta)+ , _mainPFWidgetConfig_showWelcome = False } mainPFWidget :: forall t m. (MonadWidget t m)@@ -276,14 +279,16 @@ tryOpenFileEv = leftmost [fforMaybe postBuildEv (const _mainPFWidgetConfig_initialFile), openFileEv] -- load file on start- mLoadFileEv <- performEvent $ ffor tryOpenFileEv+ eLoadFileEv <- performEvent $ ffor tryOpenFileEv $ \fp -> do absfp <- liftIO $ FP.makeAbsolute fp- mspf :: Maybe (SPotatoFlow, ControllerMeta) <- liftIO $ Aeson.decodeFileStrict absfp- return (mspf, absfp)+ espf <- liftIO $ Snake.decodeFile absfp+ return (espf, absfp) -- empty project event let+ loadFileFailAlertEv = fmap T.pack $ fmapMaybe (leftToMaybe . fst) eLoadFileEv+ loadFileEv = fmapMaybe (rightToMaybe . fst) eLoadFileEv -- a little silly to route a new empty project through the load file event but it's easy whatever newEmptyFileEv = fmap (const (owlPFState_to_sPotatoFlow owlpfstate_newProject, emptyControllerMeta)) _saveBeforeActionOutput_new @@ -307,10 +312,7 @@ , _controllerMeta_layers = _layersState_meta . _goatState_layersState $ gs } handle (\(SomeException e) -> return . Left $ "ERROR, Could not save to file " <> show fn <> " got exception \"" <> show e <> "\"") $ do- --liftIO $ Aeson.encodeFile "potato.flow" spf- --print $ "wrote to file: " <> fn- LBS.writeFile fn $ PrettyAeson.encodePretty (spf, cm)- --LBS.writeFile fn $ Aeson.encode (spf, cm)+ LBS.writeFile fn $ Snake.serialize Snake.SF_Json (spf, cm) return $ Right fn -- debug stuff (temp)@@ -328,7 +330,7 @@ AppKbCmd {..} <- captureInputEvents (That inputCapturedByPopupBeh) holdAppKbCmd -- setup PotatoConfig- currentOpenFileDyn <- holdDyn Nothing $ fmap Just $ leftmost [saveSuccessEv, fmap snd mLoadFileEv]+ currentOpenFileDyn <- holdDyn Nothing $ fmap Just $ leftmost [saveSuccessEv, fmap snd eLoadFileEv] let potatoConfig = PotatoConfig { _potatoConfig_style = constant def@@ -340,7 +342,7 @@ goatWidgetConfig = GoatWidgetConfig { _goatWidgetConfig_initialState = _mainPFWidgetConfig_initialState- , _goatWidgetConfig_load = leftmost [fmapMaybe fst mLoadFileEv, newEmptyFileEv]+ , _goatWidgetConfig_load = leftmost [loadFileEv, newEmptyFileEv] -- canvas direct input , _goatWidgetConfig_mouse = leftmostWarn "mouse" [(_layerWidget_mouse (_leftWidget_layersW leftW)), (_canvasWidget_mouse canvasW)]@@ -406,19 +408,17 @@ (clickSaveEv, nothingClickSaveEv) = fanMaybe $ tag (_potatoConfig_appCurrentOpenFile potatoConfig) $ leftmost [_menuButtonsWidget_saveEv . _leftWidget_menuButtonsW $ leftW, _appKbCmd_save, _saveBeforeActionOutput_save] clickSaveAsEv = leftmost $ [_menuButtonsWidget_saveAsEv . _leftWidget_menuButtonsW $ leftW, nothingClickSaveEv, _saveBeforeActionOutput_saveAs] - -- TODO probably have some sort of PopupManager -__- -- 1 welcome popup- --(_, popupStateDyn1) <- popupPaneSimple def (postBuildEv $> welcomeWidget)- (_, popupStateDyn1) <- popupPaneSimple def (never $> welcomeWidget)+ let+ showWelcomeEv = if _mainPFWidgetConfig_showWelcome then postBuildEv else never+ (_, popupStateDyn1) <- popupPaneSimple def (showWelcomeEv $> welcomeWidget) -- 2 save as popup (saveAsEv, popupStateDyn2) <- flip runPotatoReader potatoConfig $ popupSaveAsWindow $ SaveAsWindowConfig (tag (_potatoConfig_appCurrentDirectory potatoConfig) clickSaveAsEv) - -- TODO alert if mLoadFileEv fails (Nothing)- -- 3 alert popup let saveFailAlertEv = fmapMaybe maybeLeft finishSaveEv- popupStateDyn3 <- flip runPotatoReader potatoConfig $ popupAlert saveFailAlertEv+ popupStateDyn3 <- flip runPotatoReader potatoConfig $ popupAlert (leftmost [saveFailAlertEv, loadFileFailAlertEv]) -- 4 unsaved changes on action popup (SaveBeforeActionOutput {..}, popupStateDyn4) <- flip runPotatoReader potatoConfig $ popupSaveBeforeExit $
tinytools-vty.cabal view
@@ -1,7 +1,7 @@ cabal-version: 1.12 name: tinytools-vty-version: 0.1.0.1+version: 0.1.0.3 description: Please see the README on GitHub at <https://github.com/pdlla/tinytools-vty#readme> homepage: https://github.com/pdlla/tinytools-vty#readme bug-reports: https://github.com/pdlla/tinytools-vty/issues@@ -129,11 +129,11 @@ , text , these , time- , tinytools+ , tinytools >= 0.1.0.3 , vty default-language: Haskell2010 -executable tinytools-vty-exe+executable tinytools main-is: Main.hs hs-source-dirs: app@@ -203,7 +203,7 @@ , reflex >= 0.8 && <1 , reflex-potatoes >=0.1 , reflex-test-host >=0.1.2.2- , tinytools >= 0.1+ , tinytools >= 0.1.0.3 , relude , semialign , template-haskell@@ -278,20 +278,17 @@ , lens , mtl , optparse-applicative- , tinytools-vty , primitive , ref-tf , reflex >= 0.8 && <1 , reflex-potatoes >=0.1 , reflex-test-host >=0.1.2.2- , tinytools >= 0.1 , relude , semialign , template-haskell , text , these , time- , tinytools , vty default-language: Haskell2010 @@ -383,6 +380,6 @@ , text , these , time- , tinytools+ , tinytools >= 0.1.0.3 , vty default-language: Haskell2010
tutorial.potato view
@@ -1,1 +1,5 @@-[{"_sPotatoFlow_sEltTree":[[3,{"_sEltLabel_name":"<line>","_sEltLabel_sElt":{"tag":"SEltLine","contents":{"_sAutoLine_start":[18,18],"_sAutoLine_lineStyleEnd":{"_lineStyle_leftArrows":"<","_lineStyle_rightArrows":">","_lineStyle_downArrows":"v","_lineStyle_upArrows":"^"},"_sAutoLine_superStyle":{"_superStyle_tr":"╗","_superStyle_horizontal":"═","_superStyle_tl":"╔","_superStyle_vertical":"║","_superStyle_point":"█","_superStyle_bl":"╚","_superStyle_br":"╝","_superStyle_fill":{"tag":"FillStyle_Simple","contents":" "}},"_sAutoLine_labels":[],"_sAutoLine_end":[31,5],"_sAutoLine_midpoints":[],"_sAutoLine_lineStyle":{"_lineStyle_leftArrows":"<","_lineStyle_rightArrows":">","_lineStyle_downArrows":"v","_lineStyle_upArrows":"^"},"_sAutoLine_attachEnd":{"_attachment_location":"AL_Top","_attachment_target":1},"_sAutoLine_attachStart":{"_attachment_location":"AL_Right","_attachment_target":2}}}}],[2,{"_sEltLabel_name":"<box>","_sEltLabel_sElt":{"tag":"SEltBox","contents":{"_sBox_superStyle":{"_superStyle_tr":"*","_superStyle_horizontal":"-","_superStyle_tl":"*","_superStyle_vertical":"|","_superStyle_point":" ","_superStyle_bl":"*","_superStyle_br":"*","_superStyle_fill":{"tag":"FillStyle_Blank"}},"_sBox_boxType":"SBoxType_BoxText","_sBox_text":{"_sBoxText_style":{"_textStyle_alignment":"TextAlign_Left"},"_sBoxText_text":"cool features!"},"_sBox_box":{"_lBox_size":[13,4],"_lBox_tl":[5,16]},"_sBox_title":{"_sBoxTitle_align":"TextAlign_Left","_sBoxTitle_title":null}}}}],[1,{"_sEltLabel_name":"<box>","_sEltLabel_sElt":{"tag":"SEltBox","contents":{"_sBox_superStyle":{"_superStyle_tr":"╗","_superStyle_horizontal":"═","_superStyle_tl":"╔","_superStyle_vertical":"║","_superStyle_point":"█","_superStyle_bl":"╚","_superStyle_br":"╝","_superStyle_fill":{"tag":"FillStyle_Simple","contents":" "}},"_sBox_boxType":"SBoxType_BoxText","_sBox_text":{"_sBoxText_style":{"_textStyle_alignment":"TextAlign_Left"},"_sBoxText_text":"this is a placeholder for the actualy tutorial"},"_sBox_box":{"_lBox_size":[29,8],"_lBox_tl":[14,7]},"_sBox_title":{"_sBoxTitle_align":"TextAlign_Left","_sBoxTitle_title":null}}}}]],"_sPotatoFlow_sCanvas":{"_sCanvas_box":{"_lBox_size":[50,20],"_lBox_tl":[0,0]}}},{"_controllerMeta_layers":[[1,{"_layerMeta_isCollapsed":true,"_layerMeta_isLocked":false,"_layerMeta_isHidden":false}],[2,{"_layerMeta_isCollapsed":true,"_layerMeta_isLocked":false,"_layerMeta_isHidden":false}],[3,{"_layerMeta_isCollapsed":true,"_layerMeta_isLocked":false,"_layerMeta_isHidden":false}]],"_controllerMeta_pan":[14,7]}]+{+ "_snake_data": "[\n {\n \"_sPotatoFlow_sCanvas\": {\n \"_sCanvas_box\": {\n \"_lBox_size\": [\n 500,\n 200\n ],\n \"_lBox_tl\": [\n 0,\n 0\n ]\n }\n },\n \"_sPotatoFlow_sEltTree\": [\n [\n 66,\n {\n \"_sEltLabel_name\": \"starter\",\n \"_sEltLabel_sElt\": {\n \"tag\": \"SEltFolderStart\"\n }\n }\n ],\n [\n 65,\n {\n \"_sEltLabel_name\": \"info: more features\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 33,\n 15\n ],\n \"_lBox_tl\": [\n 52,\n 28\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"of course there are many more features, play around!\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": null\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 64,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Top\",\n \"_attachment_offset_rel\": {\n \"denominator\": 21,\n \"numerator\": 11\n },\n \"_attachment_target\": 1\n },\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Left\",\n \"_attachment_offset_rel\": {\n \"denominator\": 11,\n \"numerator\": 8\n },\n \"_attachment_target\": 37\n },\n \"_sAutoLine_end\": [\n 13,\n 1\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 40,\n 3\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 55,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Top\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 3\n },\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Right\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 1\n },\n \"_sAutoLine_end\": [\n 23,\n 5\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 22,\n 2\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 46,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Bot\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 37\n },\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Right\",\n \"_attachment_offset_rel\": {\n \"denominator\": 10,\n \"numerator\": 3\n },\n \"_attachment_target\": 5\n },\n \"_sAutoLine_end\": [\n 71,\n 13\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 82,\n 20\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 37,\n {\n \"_sEltLabel_name\": \"info: pan\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 36,\n 9\n ],\n \"_lBox_tl\": [\n 46,\n 1\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"#\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"#\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"to explore the canvas, click the [(p)an] button or press p\\n \\nthen click and drag in the canvas area to move your view\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": \"IMPORTANT\"\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 36,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Left\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 12\n },\n \"_sAutoLine_attachStart\": null,\n \"_sAutoLine_end\": [\n 11,\n 27\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 2,\n 28\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 12,\n {\n \"_sEltLabel_name\": \"info: box parameters\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 35,\n 10\n ],\n \"_lBox_tl\": [\n 12,\n 23\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"after creating a box, it will be selected, you can use the options in the bottom right to change its style. you can also convert your box into a text box or remove its border\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": null\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 24,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Bot\",\n \"_attachment_offset_rel\": {\n \"denominator\": 31,\n \"numerator\": 8\n },\n \"_attachment_target\": 4\n },\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Top\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 12\n },\n \"_sAutoLine_end\": [\n 30,\n 19\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 29,\n 22\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 11,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Bot\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 3\n },\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Top\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 4\n },\n \"_sAutoLine_end\": [\n 22,\n 11\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 23,\n 16\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 5,\n {\n \"_sEltLabel_name\": \"info: canvas\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 20,\n 8\n ],\n \"_lBox_tl\": [\n 63,\n 18\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"*\",\n \"_superStyle_br\": \"*\",\n \"_superStyle_fill\": {\n \"tag\": \"FillStyle_Blank\"\n },\n \"_superStyle_horizontal\": \"-\",\n \"_superStyle_point\": \" \",\n \"_superStyle_tl\": \"*\",\n \"_superStyle_tr\": \"*\",\n \"_superStyle_vertical\": \"|\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"this area (to the right) is called the canvas\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": null\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 4,\n {\n \"_sEltLabel_name\": \"info: box\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 29,\n 7\n ],\n \"_lBox_tl\": [\n 8,\n 13\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"to draw a box, click the [(b)ox] button or press b\\n\\n \\nthen click and drag in the canvas area\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": null\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 3,\n {\n \"_sEltLabel_name\": \"info: menu\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 24,\n 5\n ],\n \"_lBox_tl\": [\n 11,\n 6\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"the menu on the left has a lot of important operations\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": null\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 8,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": {\n \"_attachment_location\": \"AL_Left\",\n \"_attachment_offset_rel\": {\n \"denominator\": 5,\n \"numerator\": 3\n },\n \"_attachment_target\": 5\n },\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Right\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 3\n },\n \"_sAutoLine_end\": [\n 63,\n 21\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_midpoints\": [\n [\n 38,\n 20\n ]\n ],\n \"_sAutoLine_start\": [\n 35,\n 8\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 2,\n {\n \"_sEltLabel_name\": \"<line>\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sAutoLine_attachEnd\": null,\n \"_sAutoLine_attachStart\": {\n \"_attachment_location\": \"AL_Left\",\n \"_attachment_offset_rel\": {\n \"denominator\": 2,\n \"numerator\": 1\n },\n \"_attachment_target\": 3\n },\n \"_sAutoLine_end\": [\n 3,\n 6\n ],\n \"_sAutoLine_labels\": [],\n \"_sAutoLine_lineStyle\": {\n \"_lineStyle_downArrows\": \"\",\n \"_lineStyle_leftArrows\": \"\",\n \"_lineStyle_rightArrows\": \"\",\n \"_lineStyle_upArrows\": \"\"\n },\n \"_sAutoLine_lineStyleEnd\": {\n \"_lineStyle_downArrows\": \"v\",\n \"_lineStyle_leftArrows\": \"<\",\n \"_lineStyle_rightArrows\": \">\",\n \"_lineStyle_upArrows\": \"^\"\n },\n \"_sAutoLine_midpoints\": [],\n \"_sAutoLine_start\": [\n 10,\n 7\n ],\n \"_sAutoLine_superStyle\": {\n \"_superStyle_bl\": \"╚\",\n \"_superStyle_br\": \"╝\",\n \"_superStyle_fill\": {\n \"contents\": \" \",\n \"tag\": \"FillStyle_Simple\"\n },\n \"_superStyle_horizontal\": \"═\",\n \"_superStyle_point\": \"█\",\n \"_superStyle_tl\": \"╔\",\n \"_superStyle_tr\": \"╗\",\n \"_superStyle_vertical\": \"║\"\n }\n },\n \"tag\": \"SEltLine\"\n }\n }\n ],\n [\n 1,\n {\n \"_sEltLabel_name\": \"title\",\n \"_sEltLabel_sElt\": {\n \"contents\": {\n \"_sBox_box\": {\n \"_lBox_size\": [\n 19,\n 3\n ],\n \"_lBox_tl\": [\n 3,\n 2\n ]\n },\n \"_sBox_boxType\": \"SBoxType_BoxText\",\n \"_sBox_superStyle\": {\n \"_superStyle_bl\": \"█\",\n \"_superStyle_br\": \"█\",\n \"_superStyle_fill\": {\n \"tag\": \"FillStyle_Blank\"\n },\n \"_superStyle_horizontal\": \"█\",\n \"_superStyle_point\": \" \",\n \"_superStyle_tl\": \"█\",\n \"_superStyle_tr\": \"█\",\n \"_superStyle_vertical\": \"█\"\n },\n \"_sBox_text\": {\n \"_sBoxText_style\": {\n \"_textStyle_alignment\": \"TextAlign_Left\"\n },\n \"_sBoxText_text\": \"THIS IS TINYTOOLS\"\n },\n \"_sBox_title\": {\n \"_sBoxTitle_align\": \"TextAlign_Left\",\n \"_sBoxTitle_title\": null\n }\n },\n \"tag\": \"SEltBox\"\n }\n }\n ],\n [\n 67,\n {\n \"_sEltLabel_name\": \"starter(end)\",\n \"_sEltLabel_sElt\": {\n \"tag\": \"SEltFolderEnd\"\n }\n }\n ]\n ]\n },\n {\n \"_controllerMeta_layers\": [\n [\n 1,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 2,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 3,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 4,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 5,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 8,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 11,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 12,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 24,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 36,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 37,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 46,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 55,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 64,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 65,\n {\n \"_layerMeta_isCollapsed\": true,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ],\n [\n 66,\n {\n \"_layerMeta_isCollapsed\": false,\n \"_layerMeta_isHidden\": false,\n \"_layerMeta_isLocked\": false\n }\n ]\n ],\n \"_controllerMeta_pan\": [\n 0,\n 0\n ]\n }\n]",+ "_snake_format": "json",+ "_snake_version": 1+}