tinytools-vty 0.1.0.4 → 0.1.0.7
raw patch · 26 files changed
+445/−458 lines, 26 filesdep ~aesondep ~aeson-prettydep ~ansi-terminalbinary-added
Dependency ranges changed: aeson, aeson-pretty, ansi-terminal, bimap, bytestring, clock, constraints-extras, containers, data-default, dependent-map, dependent-sum, directory, extra, file-embed, filepath, http-conduit, ilist, lens, mtl, optparse-applicative, primitive, ref-tf, reflex, reflex-potatoes, reflex-test-host, reflex-vty, relude, semialign, template-haskell, text, these, time, tinytools, vty
Files
- ChangeLog.md +14/−0
- README.md +81/−58
- app/Main.hs +42/−29
- app/termwidth.hs +0/−20
- src/Potato/Flow/TutorialState.hs +3/−6
- src/Potato/Flow/Vty/Attrs.hs +1/−0
- src/Potato/Flow/Vty/Input.hs +0/−3
- src/Potato/Flow/Vty/Layer.hs +8/−20
- src/Potato/Flow/Vty/Left.hs +19/−13
- src/Potato/Flow/Vty/Main.hs +33/−15
- src/Potato/Flow/Vty/Params.hs +48/−47
- src/Potato/Flow/Vty/PotatoReader.hs +1/−1
- src/Potato/Flow/Vty/Tools.hs +6/−3
- src/Potato/Reflex/Vty/Helpers.hs +4/−3
- src/Potato/Reflex/Vty/Host.hs +2/−3
- src/Potato/Reflex/Vty/Widget.hs +4/−4
- src/Potato/Reflex/Vty/Widget/Popup.hs +0/−1
- src/Potato/Reflex/Vty/Widget/ScrollBar.hs +11/−11
- src/Potato/Reflex/Vty/Widget/TextInputHelpers.hs +15/−1
- src/Reflex/Vty/Test/Monad/Host.hs +7/−16
- test/Reflex/Vty/Test/Common.hs +1/−10
- tinytools-vty.cabal +89/−189
- tutorial.md +51/−0
- tutorial.potato +0/−5
- tutorial.tt +5/−0
- xterm-256color_termwidthfile binary
ChangeLog.md view
@@ -1,5 +1,19 @@ # Changelog for tinytools-vty +## [unreleased]++## [0.1.0.7]+- fix several bugs+- update to use 0.1.0.7 of tinytools++## [0.1.0.5]+- add version string to welcome popup title+- add about button that brings up the welcome popup+- make the welcome popup contents scrollable+- clean up readme+- delete the old termwidth generation exe+- fix an issue where cursor doesn't show up at end of line in text input boxes+- update to use 0.1.0.5 of tinytools ## [0.1.0.4] - update to use 0.1.0.4 of tinytools
README.md view
@@ -4,78 +4,101 @@ 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/minimapletinytools/tinytools) model. + -# running+tinytools is still in BETA and may be quite unstable. Save often!!! -To install tiny tools run `cabal install exe:tinytools` and then run `tinytools`+# installation +### pre-requisites+[text-icu](https://github.com/haskell/text-icu) is required as a prerequisite. On mac++```+brew install icu4c+brew link icu4c --force+export PKG_CONFIG_PATH="$(brew --prefix)/opt/icu4c/lib/pkgconfig"+```++You will also need the haskell build tool cabal. The best way to do this is using [ghcup](https://www.haskell.org/ghcup/)++To install tiny tools run `cabal install tinytools-vty` and then you can run `tinytools` Or if you are building locally then `cabal run tinytools` -NOTE that `tinytools` is also the name of the base model library for `tinytools-vty` which is why the `exe` is necessary. If you just `cabal install tinytools` you will install the library!+NOTE that `tinytools` is also the name of the base model library for `tinytools-vty`. If you just `cabal install tinytools` you will install the library and not the exe!!! # 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.+When you first run `tinytools` a tutorial file will open which contains information on how to use `tinytools-vty`. +- To open an empty document do `tinytools --empty` (or `cabal run tinytools -- --empty`) +- In order to support unicode wide chars (optional), you must first generate the term width file for your terminal `tinytools --widthtable` -hotkeys are supported, unfortunately they seem to sometimes they get captured by the OS or the terminal and never get forwarded to the app :(.+`tinytools-vty` has a very intuitive interface and most operations should be clear. -# tutorial+## tools+```+[(v)select][(p)an][(b)ox] +[(l)ine][pai(n)t] +``` -(open in the app instead for a better view, browser code preview doesn't render certain unicode characters in monospace correctly)+### the [(v)select] tool +The select tool is your do all tool for interacting with existing elements in your document. Click on individual elements to select or modify it. Click and drag to select multiple elements.++### the [(p)an] tool++click + drag in the canvas area with the pan tool to pan the canvas.++### the [(b)ox] tool++Use the box tool to create boxes. After creating or selecting a box, click on the top border to add or modify a label. Click inside the box to add or modify text. You can change the styling of your lines in the left menu.++### the [(l)ine] tool++Use the line tool to create lines. Lines can attach to existing boxes on either end. After selecting a line, you can move either end point of line or change their attachment. You click and drag in the middle of a line to add midpoints to your line. You can double click on a line to add a line label. Line labels can be moved by clicking and dragging on highlighted handle in the middle. You can change the styling of your lines in the left menu.++### the [(t)ext] tool++Same as the box tool except you can immediately add text to the box without additional clicking!++### the [pai(n)t] tool++The paint tool lets you create areas that take free form text. After selecting a paint area, you can move the cursor anywhere inside and add text.++## layers++On the left hand side is a very sophisticated layering system. All elements will show up in the layer system. You can drag elements around to reorder them. You can add folders to create a hierarchical layer system. You can hide or lock elements in the layers menu. You can select elements in the layers menu.++## shortucts++- (ctrl-s) save+- (ctrl-p) print+- (ctrl-q) quit+- (ctrl-c) copy+- (ctrl-v) paste+- (ctrl-z) undo+- (ctrl-y) redo+- (ctrl-o) open+- (ctrl-n) new+++## menu ```+[new][open][save][save as] +[write to "tt.txt"][quit] +``` -[new][open][save][save as] * ╔══════════════════════════════╗ -[export to "potato.txt"][quit] * ║ ║ ╔IMPORTANT#########################╗ --------------------------------* ███████████████████ ╚>#to explore the canvas, click the # -[(v)select][(p)an][(b)ox] * █THIS IS TINYTOOLS█═╗ #[(p)an] button or press p # -[(l)ine][(t)extbox][pai(n)t] * ███████████████████ ║ # # --------------------------------* v #then click and drag in the canvas # -⇊ea 𐃛 starter #* <═══╗ ╔══════════════════════╗ #area to move your view # - ea ⧈ info: more features #* ║ ║the menu on the left ║ # # - ea ⤡ <line> #* ╚═══║has a lot of important║═══╗ # # - ea ⤡ <line> #* ║operations ║ ║ ╚##################################╝ - ea ⤡ <line> #* ╚══════════════════════╝ ║ ^ - ea ⧈ info: pan #* ║ ║ ║ - ea ⤡ <line> #* v ║ ║ - ea ⧈ info: box parameters #* ╔═══════════════════════════╗ ║ ║ - ea ⤡ <line> #* ║to draw a box, click the ║ ║ ║ - ea ⤡ <line> #* ║[(b)ox] button or press b ║ ║ ╚════════════════════╗ - ea ⧈ info: canvas #* ║ ║ ║ ║ - ea ⧈ info: box #* ║then click and drag in the ║ ║ ║ - ea ⧈ info: menu #* ║canvas area ║ ║ *------------------* ║ - ea ⤡ <line> #* ╚═══════════════════════════╝ ║ |this area (to the | ║ - ea ⤡ <line> #* ║ ╚═══════════════════════>|right) is called |═╝ - ea ⧈ title #* ║ |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! ║ - #* ║ ║ ║ ║ - #* ╚═════════════════════════════════╝ ║ ║ - #* ║ ║ - #* ║ ║ - #* ║ ║ - #* ║ ║ - #* ║ ║ - #* ║ ║ - #* ║ ║ - #* ║ ║ -[new folder] * ║ ║ --------------------------------* ╚═══════════════════════════════╝ -canvas: * - width: 500 * -height: 200 * -``` +- [new]: creates a new empty doc, prompts to save if there are unsaved changes+- [open]: open a doc from disk, all docs must end .potato extension+- [save]: save the doc to the existing file context, same as save as if no file context+- [save as]: save the doc to a new file context+- [write to "tt.txt"]: writes the doc to "tt.txt"+- [quit]: quit the app, prompts to save if there are unsaved changes -# 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 widechar and grapheme cluster support -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.+Unicode character display width seems to vary by terminal. `tinytools-vty` uses the xterm-256 (mac default terminal) terminal width mappings by default. 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. Grapheme clusters are not really supported by terminals so they are currently disabled 😭.++Also note that many mono-space text displays do not display unicode widechars at a width consistent with its mono-spacing 😕. ++
app/Main.hs view
@@ -10,14 +10,18 @@ import Potato.Flow.TestStates import Potato.Flow.TutorialState import Potato.Flow.Vty.Main+import qualified Paths_tinytools_vty (version) + import Control.Exception (try) import GHC.IO.Handle import Options.Applicative import System.Directory import System.FilePath import System.IO hiding (putStrLn)+import Data.Version (showVersion)+import qualified Data.Text as T import qualified Graphics.Vty as V import qualified Graphics.Vty.UnicodeWidthTable.IO as V@@ -28,8 +32,9 @@ data PotatoCLIOptions = PotatoCLIOptions { _potatoCLIOptions_args :: [String]- , _potatoCLIOptions_empty :: Bool+ , _potatoCLIOptions_tutorial :: Bool , _potatoCLIOptions_generateUnicodeWidthTable :: Bool+ , _potatoCLIOptions_version :: Bool } @@ -37,12 +42,16 @@ sample = PotatoCLIOptions <$> many (argument str (metavar "FILE")) <*> switch- ( long "empty"- <> short 'e'- <> help "open an empty document" )+ ( long "tutorial"+ <> short 't'+ <> help "open the tutorial doc" ) <*> switch ( long "widthtable" <> help "generate unicode width table for your terminal using vty" )+ <*> switch+ ( long "version"+ <> short 'v'+ <> help "show version" ) @@ -86,7 +95,7 @@ -- see if the argument file we passed in exists or not- minitfile <- if _potatoCLIOptions_empty opts+ minitfile <- if not (_potatoCLIOptions_tutorial opts) then return Nothing else case nonEmpty (_potatoCLIOptions_args opts) of@@ -95,42 +104,46 @@ exists <- doesFileExist x return $ if exists then Just x else Nothing - if _potatoCLIOptions_generateUnicodeWidthTable opts+ if _potatoCLIOptions_version opts then do- mTermName <- V.currentTerminalName- case mTermName of- Just termName -> do- configdir <- tinytoolsConfigDir- let- fn = configdir </> (termName <> "_termwidthfile")- rslt <- try $ do- wt <- V.buildUnicodeWidthTable V.defaultUnicodeTableUpperBound- createDirectoryIfMissing False configdir- V.writeUnicodeWidthTable fn wt- case rslt of- Right _ -> do- putStrLn "\n"- putStrLn $ "successfully wrote " <> fn- exitSuccess- Left (SomeException e) -> do- putStrLn "\n"- die $ "failed to generate or write unicode width table " <> fn <> " with exception " <> show e- Nothing -> do- die "failed to generate unicode width table because could not obtain terminal name"-- else return ()+ putStrLn $ "tinytools-vty version " <> showVersion Paths_tinytools_vty.version+ exitSuccess+ else if _potatoCLIOptions_generateUnicodeWidthTable opts+ then do+ mTermName <- V.currentTerminalName+ case mTermName of+ Just termName -> do+ configdir <- tinytoolsConfigDir+ let+ fn = configdir </> (termName <> "_termwidthfile")+ rslt <- try $ do+ wt <- V.buildUnicodeWidthTable V.defaultUnicodeTableUpperBound+ createDirectoryIfMissing False configdir+ V.writeUnicodeWidthTable fn wt+ case rslt of+ Right _ -> do+ putStrLn "\n"+ putStrLn $ "successfully wrote " <> fn+ exitSuccess+ Left (SomeException e) -> do+ putStrLn "\n"+ die $ "failed to generate or write unicode width table " <> fn <> " with exception " <> show e+ Nothing -> do+ die "failed to generate unicode width table because could not obtain terminal name"+ else return () homeDir <- getHomeDirectory let config = MainPFWidgetConfig { _mainPFWidgetConfig_initialFile = minitfile , _mainPFWidgetConfig_homeDirectory = homeDir- , _mainPFWidgetConfig_initialState = if _potatoCLIOptions_empty opts+ , _mainPFWidgetConfig_initialState = if not (_potatoCLIOptions_tutorial opts) then (owlpfstate_newProject, emptyControllerMeta) -- TODO load tutorial here owlpfstate_tutorial --else owlpfstate_attachments1 else tutorialState , _mainPFWidgetConfig_showWelcome = True+ , _mainPFWidgetConfig_version_tinytools_vty = T.pack $ showVersion Paths_tinytools_vty.version } -- set the title
− app/termwidth.hs
@@ -1,20 +0,0 @@-{-# OPTIONS_GHC -threaded #-}--import Relude--import Data.Maybe--import qualified Graphics.Vty as V-import qualified Graphics.Vty.UnicodeWidthTable.IO as V-import qualified Graphics.Vty.UnicodeWidthTable.Query as V----main :: IO ()-main = do- mTermName <- V.currentTerminalName- let- widthMapFile = fromJust mTermName <> "_termwidthfile"- putStrLn $ "writing width for term: " <> show mTermName- table <- V.buildUnicodeWidthTable '🥱'- V.writeUnicodeWidthTable widthMapFile table
src/Potato/Flow/TutorialState.hs view
@@ -6,15 +6,12 @@ import Relude import Potato.Flow-import qualified Potato.Flow.Serialization.Snake as Snake+import qualified Potato.Flow.Serialization.SnakeWrangler as Snake+import Potato.Flow.TestStates -import Data.ByteString import qualified Data.ByteString.Lazy as LBS import Data.FileEmbed (embedFile) -import qualified Data.Text as T-import qualified Data.Aeson as Aeson-import Potato.Flow.TestStates @@ -25,4 +22,4 @@ Right j -> Just j tutorialjson :: ByteString-tutorialjson = $(embedFile "tutorial.potato")+tutorialjson = $(embedFile "tutorial.tt")
src/Potato/Flow/Vty/Attrs.hs view
@@ -50,6 +50,7 @@ renderHandlerColorToVtyColor :: RenderHandleColor -> Color renderHandlerColorToVtyColor = \case RHC_Default -> brightMagenta+ RHC_Cursor -> brightBlue RHC_Attachment -> brightBlue RHC_AttachmentHighlight -> brightCyan
src/Potato/Flow/Vty/Input.hs view
@@ -8,15 +8,12 @@ import Relude import Potato.Flow-import Potato.Flow.Controller import qualified Graphics.Vty as V-import qualified Graphics.Vty.Input.Events as V import Reflex import Reflex.Vty import Control.Monad.Fix-import qualified Data.Text.Encoding as T convertModifiers :: [V.Modifier] -> [KeyModifier] convertModifiers = fmap $ \case
src/Potato/Flow/Vty/Layer.hs view
@@ -11,32 +11,23 @@ import Relude import Potato.Flow-import Potato.Flow.Controller import Potato.Flow.Vty.Attrs import Potato.Flow.Vty.Input import Potato.Reflex.Vty.Helpers-import Potato.Reflex.Vty.Widget import Potato.Flow.Vty.PotatoReader import Potato.Flow.Vty.Common import Potato.Reflex.Vty.Widget.ScrollBar+import Potato.Reflex.Vty.Widget.TextInputHelpers import qualified Potato.Data.Text.Zipper-import Control.Monad.Fix-import Data.Align-import Data.Dependent.Sum (DSum ((:=>)))-import qualified Data.IntMap.Strict as IM import qualified Data.List as L import qualified Data.Sequence as Seq import qualified Data.Text as T-import Data.Text.Zipper import qualified Data.Text.Zipper as TZ-import Data.These import qualified Graphics.Vty as V import Reflex-import Reflex.Network-import Reflex.Potato.Helpers import Reflex.Vty @@ -101,12 +92,14 @@ makeLayerImage width lhrentry = case lhrentry of LayersHandlerRenderEntryDummy ident -> r where r = V.text' lg_layer_selected . T.pack . L.take width- $ replicate ident ' '+ $ + replicate (max 0 (ident-1)) ' '+ <> (if ident > 0 then "└" else "")+ <> " " <> replicate 10 '*' LayersHandlerRenderEntryNormal selected mdots mrenaming lentry@LayerEntry{..} -> r where ident = layerEntry_depth lentry sowl = _layerEntry_superOwl- rid = _superOwl_id sowl label = hasOwlItem_name sowl attr = case selected of@@ -128,6 +121,7 @@ OwlSubItemBox _ -> "⧈" OwlSubItemLine _ -> "⤡" OwlSubItemTextArea _ -> "𐂂"+ _ -> "?" t1 = V.text' attr . T.pack $ @@ -138,7 +132,7 @@ -- render folder hide lock icons -- <> [moveChar]- <> if' (layerEntry_isFolder lentry) (if' _layerEntry_isCollapsed [expandChar] [closeChar]) [' ']+ <> if' (layerEntry_isFolder lentry) (if' _layerEntry_isCollapsed [expandChar] [closeChar]) ['●'] <> if' (lockHiddenStateToBool _layerEntry_hideState) [hiddenChar] [visibleChar] <> if' (lockHiddenStateToBool _layerEntry_lockState) [lockedChar] [unlockedChar] <> " " @@ -149,7 +143,7 @@ Nothing -> V.text' attr label Just renaming -> img where dls = TZ.displayLines 999999 attrrenamingbg attrrenamingcur (coerceZipper renaming)- img = V.vertCat . images $ TZ._displayLines_spans dls+ img = V.vertCat . images . addCursorSpace $ TZ._displayLines_spans dls r = t1 V.<|> t2 @@ -166,12 +160,6 @@ => LayerWidgetConfig t -> m (LayerWidget t) holdLayerWidget lwc@LayerWidgetConfig {..} = do----- potatostylebeh <- fmap _potatoConfig_style askPotato- PotatoStyle {..} <- sample potatostylebeh regionWidthDyn <- displayWidth --regionHeightDyn <- displayHeight
src/Potato/Flow/Vty/Left.hs view
@@ -13,7 +13,6 @@ import Potato.Flow-import Potato.Flow.Vty.Info import Potato.Flow.Vty.Layer import Potato.Flow.Vty.Params import Potato.Flow.Vty.PotatoReader@@ -36,6 +35,7 @@ , _menuButtonsWidget_saveAsEv :: Event t () , _menuButtonsWidget_exportEv :: Event t () , _menuButtonsWidget_quitEv :: Event t ()+ , _menuButtonsWidget_aboutEv :: Event t () } data LeftWidgetConfig t = LeftWidgetConfig {@@ -78,8 +78,7 @@ -- this only works because you use splitHDrag to split the Left/Canvas panels (it toggles the focus) focusDyn <- focus let-- loseFocusEv = void $ ffilter (not . id) $ updated focusDyn+ leftLoseFocusEv = void $ ffilter (not . id) $ updated focusDyn initLayout $ col $ mdo @@ -88,23 +87,24 @@ -- TODO height should be dynamic but not sure if there's away to do this dynamically because width (from which buttonsHeightDyn) is derived depends on `grout . fixed`. You need to pull width from outside of the `grout . fixed` call to make this work right... (menuButtons, menuFocusEv, buttonsHeightDyn) <- (grout . fixed) buttonsHeightDyn $ row $ do - (buttonsEv, heightDyn) <- buttonList (constDyn ["new", "open", "save", "save as", "export to \"potato.txt\"", "quit"]) (Just widthDyn)+ (buttonsEv, heightDyn) <- buttonList (constDyn ["about", "new", "open", "save", "save as", "write to \"tt.txt\"", "quit"]) (Just widthDyn) let- exportEv = ffilterButtonIndex 4 buttonsEv+ exportEv = ffilterButtonIndex 5 buttonsEv menuButtons' = MenuButtonsWidget {- _menuButtonsWidget_newEv = ffilterButtonIndex 0 buttonsEv- , _menuButtonsWidget_openEv = ffilterButtonIndex 1 buttonsEv- , _menuButtonsWidget_saveEv = ffilterButtonIndex 2 buttonsEv- , _menuButtonsWidget_saveAsEv = ffilterButtonIndex 3 buttonsEv+ _menuButtonsWidget_aboutEv = ffilterButtonIndex 0 buttonsEv+ , _menuButtonsWidget_newEv = ffilterButtonIndex 1 buttonsEv+ , _menuButtonsWidget_openEv = ffilterButtonIndex 2 buttonsEv+ , _menuButtonsWidget_saveEv = ffilterButtonIndex 3 buttonsEv+ , _menuButtonsWidget_saveAsEv = ffilterButtonIndex 4 buttonsEv , _menuButtonsWidget_exportEv = exportEv- , _menuButtonsWidget_quitEv = ffilterButtonIndex 5 buttonsEv+ , _menuButtonsWidget_quitEv = ffilterButtonIndex 6 buttonsEv } clickPrintEv = tag (current $ _goatWidget_renderedCanvas _layersWidgetConfig_goatW) (void exportEv) -- TODO don't do this here cmon... performEvent_ $ ffor clickPrintEv $ \rc -> do let t = renderedCanvasToText rc -- TODO at least use filename...- liftIO $ T.writeFile "potato.txt" t+ liftIO $ T.writeFile "tt.txt" t menuFocusEv' <- mouseFocus return (menuButtons', menuFocusEv', heightDyn) @@ -165,24 +165,30 @@ , _paramsWidgetConfig_canvasDyn = _goatWidget_canvas _layersWidgetConfig_goatW , _paramsWidgetConfig_defaultParamsDyn = _goatWidget_potatoDefaultParameters _layersWidgetConfig_goatW , _paramsWidgetConfig_toolDyn = _goatWidget_tool _layersWidgetConfig_goatW- , _paramsWidgetConfig_loseFocusEv = loseFocusEv+ -- must manually remove focus from Params widget :|+ , _paramsWidgetConfig_loseFocusEv = leftmost [leftLoseFocusEv, void refineFocusNoParamsEv] } paramsFocusEv' <- mouseFocus return (params', paramsFocusEv') let - refinedFocusEv = leftmost+ refineFocusNoParamsEv :: (Event t Text) = leftmost [ fmap (const "menu") menuFocusEv , fmap (const "tools") toolsFocusEv , fmap (const "toolsOptions") toolsOptionsFocusEv , fmap (const "layers") layersFocusEv , fmap (const "info") infoFocusEv+ + ]+ refinedFocusEv = leftmost+ [ refineFocusNoParamsEv , fmap (const "params") paramsFocusEv ] refinedFocusDyn <- holdDyn "none" refinedFocusEv let + setFocusDyn' = ffor2 focusDyn refinedFocusDyn $ \f1 f2 -> case (f1, f2) of (True, "layers") -> Just GoatFocusedArea_Layers (True, _) -> Just GoatFocusedArea_Other
src/Potato/Flow/Vty/Main.hs view
@@ -13,7 +13,7 @@ import Potato.Flow-import qualified Potato.Flow.Serialization.Snake as Snake+import qualified Potato.Flow.Serialization.SnakeWrangler as Snake import Potato.Flow.TestStates import Potato.Flow.Vty.Canvas import Potato.Flow.Vty.Input@@ -47,7 +47,6 @@ import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT import qualified Data.Text.IO as T-import Data.Time.Clock import qualified Data.ByteString.Lazy as LBS import Data.These @@ -117,14 +116,18 @@ cfg'' <- V.standardIOConfig configDir <- tinytoolsConfigDir let+ -- using the xterm-256 one by default since it's better than nothing+ defaultWidthMapFile = "xterm-256color_termwidthfile" mTermName = V.termName cfg'' widthMapFile = case mTermName of+ -- use the xterm one by default, should be fine lol Nothing -> "" Just termName -> configDir FP.</> (termName <> "_termwidthfile") doesWidthMapFileExist <- FP.doesFileExist widthMapFile+ doesDefaultWidthMapFileExist <- FP.doesFileExist defaultWidthMapFile if doesWidthMapFileExist then putStrLn $ "attempting to load unicode width table file " <> widthMapFile- else putStrLn $ "could not find unicode width table file " <> widthMapFile <> " please run --widthtable to generate unicode width table file"+ else putStrLn $ "could not find unicode width table file " <> widthMapFile <> " using the xterm-256 one by default instead. Please run --widthtable to generate unicode width table file" let cfg' = cfg'' { V.mouseMode = Just True } cfg = if doesWidthMapFileExist@@ -132,7 +135,12 @@ V.allowCustomUnicodeWidthTables = Just True , V.termWidthMaps = [(fromJust mTermName, widthMapFile)] }- else cfg'+ else if doesDefaultWidthMapFileExist+ then cfg' {+ V.allowCustomUnicodeWidthTables = Just True+ , V.termWidthMaps = [(fromJust mTermName, defaultWidthMapFile)]+ }+ else cfg' vty <- V.mkVty cfg potatoMainWidgetWithHandle vty child @@ -155,7 +163,7 @@ somedefaultpfcfg :: MainPFWidgetConfig somedefaultpfcfg = def {- --_mainPFWidgetConfig_initialFile = Just "potato.flow"+ --_mainPFWidgetConfig_initialFile = Just "" _mainPFWidgetConfig_initialState = (owlpfstate_newProject, emptyControllerMeta) } @@ -185,14 +193,22 @@ -- NOTE, this will query welcome message each time you recreate this welcomeWidget :: forall t m. (MonadWidget t m)- => m (Event t ())-welcomeWidget = do+ => Text+ -> m (Event t ())+welcomeWidget version = do postBuildEv <- getPostBuild welcomeMessageEv <- fetchMOTDAsync postBuildEv welcomeMessageDyn <- holdDyn "loading..." welcomeMessageEv- boxTitle (constant def) "😱 tinytools-vty (beta) 😱" $ do+ let+ scrollcfg = ScrollableConfig {+ _scrollableConfig_scrollBy = never+ , _scrollableConfig_scrollTo = never+ , _scrollableConfig_startingPosition = ScrollPos_Top+ , _scrollableConfig_scrollToBottom = constant Nothing+ }+ boxTitle (constant def) (constant $ "😱 tinytools-vty (beta v." <> version <> ") 😱") $ do initLayout $ col $ do- (grout . stretch) 1 $ text (current welcomeMessageDyn)+ _ <- (grout . stretch) 1 $ scrollableText scrollcfg welcomeMessageDyn (grout . fixed) 3 $ textButton def (constant "bye") @@ -238,6 +254,7 @@ -- should this include controller meta too? , _mainPFWidgetConfig_initialState :: (OwlPFState, ControllerMeta) -- ^ will be overriden by initialFile if set , _mainPFWidgetConfig_showWelcome :: Bool+ , _mainPFWidgetConfig_version_tinytools_vty :: Text } instance Default MainPFWidgetConfig where@@ -247,6 +264,7 @@ , _mainPFWidgetConfig_homeDirectory = "/home/minimaple/kitchen/faucet/potato-flow-vty" , _mainPFWidgetConfig_initialState = (emptyOwlPFState, emptyControllerMeta) , _mainPFWidgetConfig_showWelcome = False+ , _mainPFWidgetConfig_version_tinytools_vty = "0.0.0.0" } mainPFWidget :: forall t m. (MonadWidget t m)@@ -330,7 +348,7 @@ AppKbCmd {..} <- captureInputEvents (That inputCapturedByPopupBeh) holdAppKbCmd -- setup PotatoConfig- currentOpenFileDyn <- holdDyn Nothing $ fmap Just $ leftmost [saveSuccessEv, fmap snd eLoadFileEv]+ currentOpenFileDyn <- holdDyn Nothing $ leftmost [fmap Just saveSuccessEv, fmap (Just . snd) eLoadFileEv, newEmptyFileEv $> Nothing] let potatoConfig = PotatoConfig { _potatoConfig_style = constant def@@ -360,7 +378,7 @@ , _goatWidgetConfig_setFocusedArea = _leftWidget_setFocusEvent leftW -- TODO- --, _goatWidgetConfig_unicodeWidthFn =+ , _goatWidgetConfig_unicodeWidthFn = Nothing -- debugging stuff , _goatWidgetConfig_setDebugLabel = never@@ -379,7 +397,7 @@ rightPanel = do dreg' <- askRegion- let dreg = fmap (\region -> region { _region_left = 0, _region_top = 0}) dreg'+ let dreg = fmap (\reg -> reg { _region_left = 0, _region_top = 0}) dreg' f <- focus pane dreg f $ holdCanvasWidget $ CanvasWidgetConfig { _canvasWidgetConfig_pan = _goatWidget_pan everythingW@@ -408,10 +426,10 @@ (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] - -- 1 welcome popup+ -- 1 welcome/about popup let- showWelcomeEv = if _mainPFWidgetConfig_showWelcome then postBuildEv else never- (_, popupStateDyn1) <- popupPaneSimple def (showWelcomeEv $> welcomeWidget)+ showWelcomeEv = leftmost $ [_menuButtonsWidget_aboutEv . _leftWidget_menuButtonsW $ leftW, if _mainPFWidgetConfig_showWelcome then postBuildEv else never]+ (_, popupStateDyn1) <- popupPaneSimple def (showWelcomeEv $> welcomeWidget _mainPFWidgetConfig_version_tinytools_vty) -- 2 save as popup (saveAsEv, popupStateDyn2) <- flip runPotatoReader potatoConfig $ popupSaveAsWindow $ SaveAsWindowConfig (tag (_potatoConfig_appCurrentDirectory potatoConfig) clickSaveAsEv)
src/Potato/Flow/Vty/Params.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecursiveDo #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} module Potato.Flow.Vty.Params ( ParamsWidgetConfig(..)@@ -15,17 +16,12 @@ import Relude import Potato.Flow-import Potato.Flow.OwlHelpers import Potato.Flow.Vty.Common import Potato.Reflex.Vty.Helpers import Potato.Flow.Vty.PotatoReader-import Potato.Flow.Vty.Attrs import Potato.Reflex.Vty.Widget.TextInputHelpers -import Control.Monad.Fix-import Control.Monad.NodeId import Data.Align-import Data.Char (isNumber) import Data.Dependent.Sum (DSum ((:=>))) import qualified Data.IntMap as IM import qualified Data.List.Extra as L@@ -35,7 +31,6 @@ import qualified Data.Text.Zipper as TZ import Data.These import Data.Tuple.Extra-import qualified Data.List as List import qualified Graphics.Vty as V import Reflex@@ -53,17 +48,22 @@ controllersWithId_to_llama = makePFCLlama . OwlPFCManipulate -paramsNavigation :: (MonadWidget t m) => m (Event t Int)-paramsNavigation = do+paramsNavigation :: (MonadWidget t m) => Behavior t Bool -> m (Event t Int)+paramsNavigation allowReturnBeh = do tabEv <- key (V.KChar '\t')+ -- TODO this will cause the return key to never get sent to Layers when in renaming mode so you can't confirm a rename with enter+ -- I guess you need to disablee return navigation if you're in the layers menu... returnEv <- key V.KEnter- let fwd = fmap (const 1) $ leftmost [tabEv, returnEv]+ let fwd = fmap (const 1) $ leftmost [tabEv, gate allowReturnBeh returnEv] back <- fmap (const (-1)) <$> key V.KBackTab return $ leftmost [fwd, back] -repeatNavigation :: (MonadWidget t m, HasFocus t m) => m ()+repeatNavigation :: (MonadWidget t m, HasFocus t m) => m () repeatNavigation = do- navEv <- paramsNavigation+ -- only allow navigation if we have focus+ mfiddyn <- focusedId+ let allowReturnBeh = fmap isJust (current mfiddyn)+ navEv <- paramsNavigation allowReturnBeh requestFocus $ Refocus_Shift <$> navEv @@ -100,7 +100,7 @@ subSelection = SuperOwlParliament $ Seq.fromList $ fmap fst params r = case values of [] -> Nothing- x:xs -> if L.allSame values+ x:_ -> if L.allSame values then Just (subSelection, Just x) else Just (subSelection, Nothing) @@ -116,9 +116,9 @@ makeLineStyleInputDyn tool selection pdp = r where selectLineStyleFromSelection :: Selection -> Maybe (Selection, Maybe (Maybe LineStyle, Maybe LineStyle))- selectLineStyleFromSelection (SuperOwlParliament selection) = r_d1 where+ selectLineStyleFromSelection (SuperOwlParliament sel) = r_d1 where ps = (\x -> (getSEltLineStyle x, getSEltLineStyleEnd x)) . superOwl_toSElt_hack- rawparams = ffilter (\(_,(x,y)) -> isJust x || isJust y) . fmap (\sowl -> (sowl, ps sowl)) $ selection+ rawparams = ffilter (\(_,(x,y)) -> isJust x || isJust y) . fmap (\sowl -> (sowl, ps sowl)) $ sel startvalues = catMaybes . toList . fmap fst . fmap snd $ rawparams endvalues = catMaybes . toList . fmap snd . fmap snd $ rawparams subSelection = SuperOwlParliament $ fmap fst rawparams@@ -174,9 +174,11 @@ data SuperStyleCell = SSC_TL | SSC_TR | SSC_BL | SSC_BR | SSC_V | SSC_H | SSC_Fill deriving (Show) updateFromSuperStyle :: SuperStyleCell -> (SuperStyle -> TZ.TextZipper)-updateFromSuperStyle ssc = TZ.top . TZ.fromText . T.singleton . gettfn ssc where- gettfn ssc' = fromMaybe ' ' . gettfn' ssc'- gettfn' = \case+updateFromSuperStyle ssc = r where+ r ss = case gettfn ssc ss of+ Nothing -> TZ.empty+ Just c -> TZ.top . TZ.fromText . T.singleton $ c+ gettfn = \case SSC_TL -> _superStyle_tl SSC_TR -> _superStyle_tr SSC_BL -> _superStyle_bl@@ -188,23 +190,23 @@ _ -> Nothing) . _superStyle_fill -makeSuperStyleTextEntry :: (MonadWidget t m, HasPotato t m) => SuperStyleCell -> Dynamic t (Maybe SuperStyle) -> m (Behavior t PChar)+makeSuperStyleTextEntry :: (MonadWidget t m, HasPotato t m) => SuperStyleCell -> Dynamic t (Maybe SuperStyle) -> m (Behavior t (Maybe PChar)) makeSuperStyleTextEntry ssc mssDyn = do mss0 <- sample . current $ mssDyn let modifyEv = (fmap (maybe id (\ss -> const (updateFromSuperStyle ssc ss))) (updated mssDyn)) ti <- singleCellTextInput modifyEv $ case mss0 of Nothing -> "" Just ss0 -> updateFromSuperStyle ssc ss0- return . current . fmap (\t -> maybe ' ' (\(c,_) -> c) (T.uncons t)) $ ti+ return . current . fmap (\t -> fmap (\(c,_) -> c) (T.uncons t)) $ ti makeSuperStyleEvent :: (Reflex t)- => Behavior t PChar- -> Behavior t PChar- -> Behavior t PChar- -> Behavior t PChar- -> Behavior t PChar- -> Behavior t PChar- -> Behavior t PChar+ => Behavior t (Maybe PChar)+ -> Behavior t (Maybe PChar)+ -> Behavior t (Maybe PChar)+ -> Behavior t (Maybe PChar)+ -> Behavior t (Maybe PChar)+ -> Behavior t (Maybe PChar)+ -> Behavior t (Maybe PChar) -> Event t () -> Event t SuperStyle makeSuperStyleEvent tl v bl h f tr br trig = pushAlways pushfn trig where@@ -218,14 +220,14 @@ br' <- sample br return $ def { -- TODO Nothing is text cell was blank...- _superStyle_tl = Just tl'- , _superStyle_tr = Just tr'- , _superStyle_bl = Just bl'- , _superStyle_br = Just br'- , _superStyle_vertical = Just v'- , _superStyle_horizontal = Just h'+ _superStyle_tl = tl'+ , _superStyle_tr = tr'+ , _superStyle_bl = bl'+ , _superStyle_br = br'+ , _superStyle_vertical = v'+ , _superStyle_horizontal = h' --, _superStyle_point :: PChar- , _superStyle_fill = FillStyle_Simple f'+ , _superStyle_fill = maybe FillStyle_Blank FillStyle_Simple f' } -- TODO move to SELts.hs@@ -247,7 +249,7 @@ text (show i <> ". " <> constant (T.pack s)) fmap (fmap (\_ -> s)) (mouseDown V.BLeft) return $ fmap superStyle_fromListFormat (leftmost presetClicks)- return (5, never, setStyleEv')+ return (length presetSuperStyles + 2, never, setStyleEv') 0 -> do -- TODO the awesome version of this has a toggle box so that you can choose to do horiz/vertical together (once you support separate horiz/vert left/right/top/down styles) -- TODO also a toggle for setting corners to common sets@@ -283,13 +285,13 @@ setStyleEv' = makeSuperStyleEvent tl v bl h f tr br (void $ updated focusDynUnique) captureEv' = leftmost [void setStyleEv', captureEv1] return (5, captureEv', setStyleEv')+ _ -> error "radioList returned invalid index" setStyleEv <- switchHold never (fmap thd3 setStyleEvEv) captureEv <- switchHold never (fmap snd3 setStyleEvEv) heightDyn <- holdDyn 0 (fmap fst3 setStyleEvEv) let- selectionDyn = fmap fst3 inputDyn pushSuperStyleFn :: SuperStyle -> PushM t (Maybe (Either Llama SetPotatoDefaultParameters)) pushSuperStyleFn ss = do (SuperOwlParliament selection, _, tool) <- sample . current $ inputDyn@@ -404,6 +406,7 @@ 0 -> if start == end then start else Nothing 1 -> start 2 -> end+ _ -> error "radioList returned invalid index" (focusDyn,wasChangeDyn,l,r,u,d) <- do --(tile . fixed) 1 $ text (fmap (T.pack . superStyle_toListFormat . Data.Maybe.fromJust) $ current mssDyn)@@ -432,13 +435,13 @@ setStyleEv' = makeLineStyleEvent (current l) (current r) (current u) (current d) (void $ gate (current wasChangeDyn) (updated focusDynUnique)) captureEv' = leftmost [void setStyleEv', captureEv''] return (7, captureEv', setStyleEv')+ _ -> error "radioList returned invalid index" setStyleEv <- switchHold never (fmap thd3 setStyleEvEv) captureEv <- switchHold never (fmap snd3 setStyleEvEv) heightDyn <- holdDyn 0 (fmap fst3 setStyleEvEv) let- selectionDyn = fmap fst3 inputDyn pushLineStyleFn :: Either () LineStyle -> PushM t (Maybe (Either Llama SetPotatoDefaultParameters)) pushLineStyleFn eflipss = do pdp <- sample . current $ pdpDyn@@ -449,6 +452,7 @@ 0 -> SetLineStyleEnd_Both 1 -> SetLineStyleEnd_Start 2 -> SetLineStyleEnd_End+ _ -> error "radioList returned invalid index" (setstart, setend) = case whichEnd of SetLineStyleEnd_Start -> (True, False) SetLineStyleEnd_End -> (False, True)@@ -494,7 +498,6 @@ holdTextAlignmentWidget _ inputDyn = constDyn $ do let mtaDyn = fmap snd3 inputDyn- selectionDyn = fmap fst3 inputDyn let @@ -514,6 +517,7 @@ 0 -> TextAlign_Left 1 -> TextAlign_Center 2 -> TextAlign_Right+ _ -> error "radioList returned invalid index" ) $ setAlignmentEv' pushAlignmentFn :: TextAlign -> PushM t (Maybe (Either Llama SetPotatoDefaultParameters)) pushAlignmentFn ta = do@@ -537,8 +541,6 @@ holdSBoxTypeWidget _ inputDyn = constDyn $ do let mBoxType = fmap snd3 inputDyn- selectionDyn = fmap fst3 inputDyn- mbt0 <- sample . current $ mBoxType let stateDyn = ffor mBoxType $ \case@@ -577,14 +579,14 @@ where newbt = case bt of This border -> make_sBoxType border (sBoxType_isText oldbt)- That text -> make_sBoxType (sBoxType_hasBorder oldbt) text- These border text -> make_sBoxType border text+ That txt -> make_sBoxType (sBoxType_hasBorder oldbt) txt+ These border txt -> make_sBoxType border txt return $ if toolOverrideSBoxType tool -- UNTESTED, it's probably currect but the tool overrides this default so I never actually tested it then Just . Right $ def { _setPotatoDefaultParameters_sBoxType = Just $ case bt of This border -> make_sBoxType border (snd curState)- That text -> make_sBoxType (fst curState) text- These border text -> make_sBoxType border text+ That txt -> make_sBoxType (fst curState) txt+ These border txt -> make_sBoxType border txt } else case Data.Maybe.mapMaybe fmapfn . toList $ selection of [] -> Nothing@@ -629,10 +631,6 @@ captureEv = leftmost [void outputEv, captureEv1] return (3, captureEv, outputEv) -data SEltParams = SEltParams {- --_sEltParams_sBox =- }- data ParamsWidgetConfig t = ParamsWidgetConfig { _paramsWidgetConfig_selectionDyn :: Dynamic t Selection , _paramsWidgetConfig_canvasDyn :: Dynamic t SCanvas@@ -692,6 +690,9 @@ -- show canvas params when nothing is selected mCanvasSizeInputDyn = ffor2 toolDyn selectionDyn (\t s -> if isParliament_null s then Just (isParliament_empty, Nothing, t) else Nothing)+++ -- TODO consider doing initManager_ within the widgets if you don't want to tab from one widget to the next (paramsOutputEv, captureEv, canvasSizeOutputEv, heightDyn) <- initManager_ $ col $ do
src/Potato/Flow/Vty/PotatoReader.hs view
@@ -27,7 +27,7 @@ -- TODO move elsewhere kTinyToolsFileExtension :: (IsString a) => a-kTinyToolsFileExtension = ".potato"+kTinyToolsFileExtension = ".tt" -- TODO move elsewhere addTinyToolsFileExtensionIfNecessary :: FP.FilePath -> FP.FilePath
src/Potato/Flow/Vty/Tools.hs view
@@ -41,7 +41,8 @@ Tool_Pan -> 1 Tool_Box -> 2 Tool_Line -> 3- Tool_TextArea -> 4+ Tool_Text -> 4+ Tool_TextArea -> 5 _ -> 0 holdToolsWidget :: forall t m. (PostBuild t m, MonadWidget t m)@@ -49,13 +50,14 @@ -> m (ToolWidget t) holdToolsWidget ToolWidgetConfig {..} = mdo - (radioEvs, heightDyn) <- radioList (constDyn ["(v)select","(p)an","(b)ox","(l)ine","pai(n)t"]) (fmap ((:[]) . toolWidgetToIndex) _toolWidgetConfig_tool) (Just _toolWidgetConfig_widthDyn)+ (radioEvs, heightDyn) <- radioList (constDyn ["(v)select","(p)an","(b)ox","(l)ine","(t)ext","pai(n)t"]) (fmap ((:[]) . toolWidgetToIndex) _toolWidgetConfig_tool) (Just _toolWidgetConfig_widthDyn) let selectB = void $ ffilter (==0) radioEvs panB = void $ ffilter (==1) radioEvs boxB = void $ ffilter (==2) radioEvs lineB = void $ ffilter (==3) radioEvs- textareaB = void $ ffilter (==4) radioEvs+ textb = void $ ffilter (==4) radioEvs+ textareaB = void $ ffilter (==5) radioEvs let setTool = leftmost@@ -63,6 +65,7 @@ , Tool_Pan <$ leftmost [panB] , Tool_Box <$ leftmost [boxB] , Tool_Line <$ leftmost [lineB]+ , Tool_Text <$ leftmost [textb] , Tool_TextArea <$ leftmost [textareaB]] {- vLayoutPad 4 $ debugStream [
src/Potato/Reflex/Vty/Helpers.hs view
@@ -31,7 +31,6 @@ import Relude import Potato.Reflex.Vty.Widget-import Reflex.Potato.Helpers (simultaneous) import Control.Monad.Fix import Control.Monad.NodeId@@ -84,7 +83,7 @@ t <- holdDyn "" $ mergeWith (\a b -> a <> "\n" <> b) evs richText richTextConfig_simpleForeColorAttr (current t) -debugStreamBeh :: (MonadHold t m, HasDisplayRegion t m, HasImageWriter t m, HasTheme t m) => [Behavior t Text] -> m ()+debugStreamBeh :: (HasDisplayRegion t m, HasImageWriter t m, HasTheme t m) => [Behavior t Text] -> m () debugStreamBeh behs = text $ foldr (liftA2 (\t1 t2 -> t1 <> " " <> t2)) "" behs countEv :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Dynamic t Int)@@ -135,18 +134,20 @@ -} +-- TODO DELETE UNUSED drag2AttachOnStart :: forall t m a. (Reflex t, MonadFix m, MonadHold t m, HasInput t m) => V.Button -> Behavior t a -> m (Event t (a, Drag2)) drag2AttachOnStart btn beh = do+ -- TODO pretty sure this should be btn? dragEv <- drag2 V.BLeft let foldfn d ma = do anew <- case ma of Nothing -> sample beh- Just (a, _) | _drag2_state d == DragStart -> sample beh+ Just (_, _) | _drag2_state d == DragStart -> sample beh Just (a, _) -> return a return $ Just (anew, d) dragBeh <- foldDynM foldfn Nothing dragEv
src/Potato/Reflex/Vty/Host.hs view
@@ -21,8 +21,7 @@ import Prelude -import System.IO-import Control.Concurrent (forkIO, killThread, MVar, newMVar, putMVar, readMVar, modifyMVar_)+import Control.Concurrent (forkIO, killThread, MVar, newMVar, readMVar, modifyMVar_) import Control.Concurrent.Chan (newChan, readChan, writeChan) import Control.Exception (onException) import Control.Monad (forM, forM_, forever)@@ -203,7 +202,7 @@ modifyMVar_ chanSizeVar (return . (+1)) - numFramesVar :: MVar Int <- liftIO $ newMVar 0+ --numFramesVar :: MVar Int <- liftIO $ newMVar 0 -- The main application loop. We wait for new events, fire those that -- have subscribers, and update the display. If we detect a shutdown
src/Potato/Reflex/Vty/Widget.hs view
@@ -42,7 +42,7 @@ -import Control.Monad.NodeId+import Control.Monad.Fix import Control.Monad.Reader import System.Clock @@ -100,7 +100,7 @@ , _dobuleClickConfig_button :: V.Button } -doubleClick :: (Reflex t, MonadHold t m, MonadFix m, PerformEvent t m, MonadIO (Performable m), HasInput t m) => DoubleClickConfig -> m (Event t ())+doubleClick :: (MonadHold t m, MonadFix m, PerformEvent t m, MonadIO (Performable m), HasInput t m) => DoubleClickConfig -> m (Event t ()) doubleClick DoubleClickConfig {..} = do singleClickEv <- singleClickNoDragOffSimple _dobuleClickConfig_button singleClickTimeEv <- performEvent $ ffor singleClickEv $ \_ -> do@@ -112,7 +112,7 @@ then Just () else Nothing -doubleClickSimple :: (Reflex t, MonadHold t m, MonadFix m, PerformEvent t m, MonadIO (Performable m), HasInput t m) => m (Event t ())+doubleClickSimple :: (MonadHold t m, MonadFix m, PerformEvent t m, MonadIO (Performable m), HasInput t m) => m (Event t ()) doubleClickSimple = doubleClick DoubleClickConfig { --_doubleClickConfig_spaceTolerance = (0,0) _doubleClickConfig_timeTolerance = 300@@ -126,7 +126,7 @@ -- | A split of the available space into two parts with a draggable separator. -- Starts with half the space allocated to each, and the first pane has focus. -- Clicking in a pane switches focus.-splitHDrag :: (Reflex t, MonadFix m, MonadHold t m, HasDisplayRegion t m, HasInput t m, HasImageWriter t m, HasFocusReader t m)+splitHDrag :: (MonadFix m, MonadHold t m, HasDisplayRegion t m, HasInput t m, HasImageWriter t m, HasFocusReader t m) => Int -- ^ initial width of left panel -> m () -> m a
src/Potato/Reflex/Vty/Widget/Popup.hs view
@@ -9,7 +9,6 @@ import Relude import Potato.Reflex.Vty.Helpers-import Potato.Reflex.Vty.Widget import qualified Graphics.Vty.Input.Events as V import Reflex
src/Potato/Reflex/Vty/Widget/ScrollBar.hs view
@@ -13,14 +13,8 @@ import qualified Graphics.Vty as V import Reflex-import Reflex.Network-import Reflex.Potato.Helpers import Reflex.Vty --import Data.Default-import qualified Data.Sequence as Seq-import Data.Fixed (div') import Data.These import Data.Align (align) @@ -44,9 +38,17 @@ _ -> Nothing +-- TODO you can use reflex.vty.widget.scroll for this+--makeVScrollBarWidget :: forall t m a. (MonadWidget t m, MonadLayoutWidget t m')+-- => m a -- ^ contents+-- -> Dynamic t Int -- ^ content height+-- -> m' a ++ -- TODO write UTs -- TODO reduce constraints-vScrollBar :: forall t m a. (MonadWidget t m)+-- dynamically scaling vertical scroll bar+vScrollBar :: forall t m. (MonadWidget t m) => Int -- ^ width -> Dynamic t Int -- ^ content height -> m (Dynamic t Int) -- ^ offset@@ -54,10 +56,7 @@ maxSizeDyn <- displayHeight let screen_over_content_dyn :: Dynamic t Float = liftA2 (\a b -> fromIntegral a / fromIntegral b ) maxSizeDyn contentSizeDyn- maxSizeDiffDyn = liftA2 (-) maxSizeDyn boxHeightDyn- maxContentSizeDiffDyn = fromIntegral . max 0 <$> liftA2 (-) contentSizeDyn maxSizeDyn- boxHeightDyn = fmap ceiling $ liftA2 (*) screen_over_content_dyn (fromIntegral <$> maxSizeDyn) boxRegionDyn = Region <$> 0 <*> offsetScreenUnitDyn <*> constDyn scrollBarWidth <*> boxHeightDyn @@ -84,13 +83,14 @@ -- TODO movement when you click on areas off the bar -- TODO maybe do ^ v arrows at top and bottom to click scroll through 1 at a time- -- TODO ugg you probably need an inputCaptured event here :\ (or you could just get rid of keyboard movement...) -- keyboard/scroll movement kup <- key V.KUp kdown <- key V.KDown kpgup <- key V.KPageUp kpgdown <- key V.KPageDown++ -- TODO this is a mouse input so it only works if mouse is over the pane, which is not what we want. So instead you you probably need to capture moues input in the parent pane and pass it in instead.... mscroll <- mouseScroll let requestedScroll :: Event t Float
src/Potato/Reflex/Vty/Widget/TextInputHelpers.hs view
@@ -119,6 +119,7 @@ _ -> Nothing +-- DELETE -- UNTESTED -- prob don't need this version filenameInputFireEventOnLoseFocus@@ -172,6 +173,19 @@ dropSpan _ [] = [] dropSpan n ((TZ.Span tag text):xs) = TZ.Span tag (T.drop n text) : dropSpan (max 0 (n - T.length text)) xs +addCursorSpace' :: [TZ.Span V.Attr] -> [TZ.Span V.Attr]+addCursorSpace' spans = case reverse spans of+ [] -> []+ (TZ.Span tag ""):xs -> reverse $ (TZ.Span tag " "):xs+ _ -> spans++--the EoL cursor is reported as a span with no text so convert it to a space+addCursorSpace :: [[TZ.Span V.Attr]] -> [[TZ.Span V.Attr]]+addCursorSpace spanss = case reverse spanss of+ [] -> []+ spans:xs -> reverse $ (addCursorSpace' spans):xs++ renderTextZipper :: (MonadWidget t m, HasPotato t m) => Dynamic t Int -> Dynamic t Int -> Dynamic t TZ.TextZipper -> m (Dynamic t (TZ.DisplayLines V.Attr)) renderTextZipper offsetDyn dw tz = do f <- focus@@ -190,7 +204,7 @@ <$> dw <*> tz <*> attrsDyn- img = ffor2 rows offsetDyn $ \rows' ox -> images . fmap (dropSpan ox) . TZ._displayLines_spans $ rows'+ img = ffor2 rows offsetDyn $ \rows' ox -> images . fmap (dropSpan ox) . addCursorSpace . TZ._displayLines_spans $ rows' tellImages $ (\imgs -> (:[]) . V.vertCat $ imgs) <$> current img return rows
src/Reflex/Vty/Test/Monad/Host.hs view
@@ -23,10 +23,8 @@ import Relude hiding (getFirst) import Control.Monad.Ref-import qualified Data.Map as Map import qualified Graphics.Vty as V-import Potato.Reflex.Vty.Widget import Reflex import Reflex.Host.Class import Reflex.Test.Monad.Host (MonadReflexTest (..), ReflexTestT,@@ -36,14 +34,7 @@ import Reflex.Vty --- for debug layout/widget stuff-import Control.Monad.Fix-import Data.Bimap (Bimap)-import qualified Data.Bimap as Bimap-import Data.Semigroup -- -- | reflex-vty variant of 'ReflexTestT' which packages an 'VtyEvent' into the input and 'Behavior t [V.Image]' into the output -- 'uintref' and 'uout' allow user to add their own inputs and outputs -- 'uintref' will often just be some singleton type (e.g. '()') as the app being tested still has access to the input 'Event t VtyEvent' through the 'VtyWidget' monad@@ -96,10 +87,10 @@ queueMouseEventInRegion dr mouse = do let absCoords (Region l t _ _) (x,y) = (x+l, y+t)- region <- sample . current $ dr+ reg <- sample . current $ dr case mouse of- Left (MouseDown b c mods) -> queueVtyEvent $ uncurry V.EvMouseDown (absCoords region c) b mods- Right (MouseUp b c) -> queueVtyEvent $ uncurry V.EvMouseUp (absCoords region c) b+ Left (MouseDown b c mods) -> queueVtyEvent $ uncurry V.EvMouseDown (absCoords reg c) b mods+ Right (MouseUp b c) -> queueVtyEvent $ uncurry V.EvMouseUp (absCoords reg c) b -- | queue mouse event in a 'DynRegion' -- if (local) mouse coordinates are outside of the (absolute) region, returns False and does not queue any event@@ -108,18 +99,18 @@ -> Either MouseDown MouseUp -- ^ mouse coordinates are LOCAL to the input region -> ReflexVtyTestT t uintref uout m Bool queueMouseEventInRegionGated dr mouse = do- region <- sample . current $ dr+ reg <- sample . current $ dr let absCoords (Region l t _ _) (x,y) = (x+l, y+t) coordinates = case mouse of Left (MouseDown _ c _) -> c Right (MouseUp _ c) -> c withinRegion (Region _ _ w h) (x,y) = not $ or [ x < 0, y < 0, x >= w, y >= h ]- if withinRegion region coordinates+ if withinRegion reg coordinates then do case mouse of- Left (MouseDown b c mods) -> queueVtyEvent $ uncurry V.EvMouseDown (absCoords region c) b mods- Right (MouseUp b c) -> queueVtyEvent $ uncurry V.EvMouseUp (absCoords region c) b+ Left (MouseDown b c mods) -> queueVtyEvent $ uncurry V.EvMouseDown (absCoords reg c) b mods+ Right (MouseUp b c) -> queueVtyEvent $ uncurry V.EvMouseUp (absCoords reg c) b return True else return False
test/Reflex/Vty/Test/Common.hs view
@@ -9,19 +9,10 @@ import Relude -import Test.Hspec-import Test.Hspec.Contrib.HUnit (fromHUnitTest) import Test.HUnit -import Control.Monad.IO.Class (liftIO)-import Data.Kind-import qualified Data.List as L--import qualified Graphics.Vty as V import Reflex import Reflex.Host.Class-import Reflex.Vty-import Reflex.Vty.Test.Monad.Host subscribeDynamic :: (MonadSubscribeEvent t m) => Dynamic t a -> m (EventHandle t a, Behavior t a)@@ -48,7 +39,7 @@ Nothing -> assertFailure "empty list" Just x -> Just a @=? head x -checkNothing :: (Show a) => [Maybe a] -> Assertion+checkNothing :: [Maybe a] -> Assertion checkNothing values = case nonEmpty values of Nothing -> assertFailure "empty list" -- TODO prob check that all elts in list are Nothing
tinytools-vty.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 name: tinytools-vty-version: 0.1.0.4+version: 0.1.0.7 description: tinytools-vty is a terminal based monospace unicode diagram editing tool homepage: https://github.com/minimapletinytools/tinytools-vty#readme bug-reports: https://github.com/minimapletinytools/tinytools-vty/issues author: minimaple maintainer: minimapletinytools@gmail.com-category: tinytools+category: Graphics synopsis: tinytools-vty is a terminal based monospace unicode diagram editing tool copyright: 2023 minimaple (Peter Lu) license: BSD3@@ -15,10 +15,14 @@ build-type: Simple extra-source-files: README.md+ tutorial.md ChangeLog.md- tutorial.potato+ tutorial.tt+ xterm-256color_termwidthfile +Tested-With: GHC ==9.2.5 || ==9.6.2+ source-repository head type: git location: https://github.com/minimapletinytools/tinytools-vty@@ -95,46 +99,47 @@ NoImplicitPrelude ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints build-depends:- aeson- , aeson-pretty- , ansi-terminal- , base >=4.7 && <5- , bimap- , bytestring- , clock- , constraints-extras- , containers- , data-default- , dependent-map- , dependent-sum- , directory- , extra- , file-embed- , filepath- , http-conduit- , ilist- , lens- , mtl- , optparse-applicative- , primitive- , ref-tf- , reflex- , reflex-potatoes- , reflex-test-host >=0.1.2.2- , reflex-vty >= 0.5.1- , relude- , semialign- -- TODO set latest version- , template-haskell >=2.18- , text- , these- , time- , tinytools >= 0.1.0.3- , vty+ base >=4.7 && <5+ , reflex >= 0.9.2 && < 1+ , reflex-test-host >=0.1.2.3 && < 0.2+ , reflex-potatoes >= 0.1.0 && < 0.2+ , reflex-vty >= 0.5.1 && < 0.6+ , tinytools == 0.1.0.7+ , aeson >= 2.1.2 && < 2.2+ , template-haskell >= 2.18 && < 2.21+ , containers >= 0.6.7 && < 0.7+ , bytestring >= 0.11.4 && < 0.12+ , mtl >= 2.3.1 && < 2.4+ , filepath >= 1.4.100 && < 1.5+ , text >= 2.0.2 && < 2.1+ , primitive >= 0.7.4 && < 0.8+ , time >= 1.12.2 && < 1.13+ , semialign >= 1.3 && < 1.4+ , these >= 1.2 && < 1.3+ , aeson-pretty >= 0.8.10 && < 0.9+ , directory >= 1.3.8 && < 1.4+ , ansi-terminal >= 1.0 && < 1.1+ , bimap >= 0.5.0 && < 0.6+ , clock >= 0.8.4 && < 0.9+ , constraints-extras >= 0.4.0 && < 0.5+ , data-default >= 0.7.1 && < 0.8+ , dependent-map >= 0.4.0 && < 0.5+ , dependent-sum >= 0.7.2 && < 0.8+ , extra >= 1.7.14 && < 1.8+ , file-embed >= 0.0.15 && < 0.1+ , http-conduit >= 2.3.8 && < 2.4+ , ilist >= 0.4.0 && < 0.5+ , lens >= 5.2.3 && < 5.3+ , ref-tf >= 0.5.0 && < 0.6+ , relude >= 1.2.1 && < 1.3+ , vty>=5.28 && <5.39+ , optparse-applicative >= 0.18.1 && < 0.19 default-language: Haskell2010 executable tinytools main-is: Main.hs+ other-modules:+ Paths_tinytools_vty hs-source-dirs: app default-extensions:@@ -175,123 +180,44 @@ NoImplicitPrelude ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N +RTS -A128m -n2m -s -RTS build-depends:- aeson- , aeson-pretty- , ansi-terminal- , base >=4.7 && <5- , bimap- , bytestring- , clock- , constraints-extras- , containers- , data-default- , dependent-map- , dependent-sum- , directory- , extra- , file-embed- , filepath- , http-conduit- , ilist- , lens- , mtl- , optparse-applicative+ base >=4.7 && <5+ , reflex >= 0.9.2 && < 1+ , reflex-test-host >=0.1.2.3 && < 0.2+ , reflex-potatoes >= 0.1.0 && < 0.2+ , reflex-vty >= 0.5.1 && < 0.6+ , tinytools == 0.1.0.7+ , aeson >= 2.1.2 && < 2.2+ , template-haskell >= 2.18 && < 2.21+ , containers >= 0.6.7 && < 0.7+ , bytestring >= 0.11.4 && < 0.12+ , mtl >= 2.3.1 && < 2.4+ , filepath >= 1.4.100 && < 1.5+ , text >= 2.0.2 && < 2.1+ , primitive >= 0.7.4 && < 0.8+ , time >= 1.12.2 && < 1.13+ , semialign >= 1.3 && < 1.4+ , these >= 1.2 && < 1.3+ , aeson-pretty >= 0.8.10 && < 0.9+ , directory >= 1.3.8 && < 1.4+ , ansi-terminal >= 1.0 && < 1.1+ , bimap >= 0.5.0 && < 0.6+ , clock >= 0.8.4 && < 0.9+ , constraints-extras >= 0.4.0 && < 0.5+ , data-default >= 0.7.1 && < 0.8+ , dependent-map >= 0.4.0 && < 0.5+ , dependent-sum >= 0.7.2 && < 0.8+ , extra >= 1.7.14 && < 1.8+ , file-embed >= 0.0.15 && < 0.1+ , http-conduit >= 2.3.8 && < 2.4+ , ilist >= 0.4.0 && < 0.5+ , lens >= 5.2.3 && < 5.3+ , ref-tf >= 0.5.0 && < 0.6+ , relude >= 1.2.1 && < 1.3+ , vty>=5.28 && <5.39+ , optparse-applicative >= 0.18.1 && < 0.19 , tinytools-vty- , primitive- , ref-tf- , reflex-vty >= 0.5.1- , reflex >= 0.8 && <1- , reflex-potatoes >=0.1- , reflex-test-host >=0.1.2.2- , tinytools >= 0.1.0.3- , relude- , semialign- , template-haskell- , text- , these- , time- , vty default-language: Haskell2010 -executable write-term-width- main-is: termwidth.hs- other-modules:- Main- hs-source-dirs:- app- default-extensions:- ApplicativeDo- BangPatterns- DataKinds- ConstraintKinds- DeriveFoldable- DeriveFunctor- DeriveTraversable- DeriveGeneric- DeriveLift- DeriveTraversable- DerivingStrategies- EmptyCase- ExistentialQuantification- FlexibleContexts- FlexibleInstances- FunctionalDependencies- GADTs- GeneralizedNewtypeDeriving- InstanceSigs- KindSignatures- LambdaCase- MultiParamTypeClasses- MultiWayIf- NamedFieldPuns- OverloadedStrings- PatternSynonyms- RankNTypes- ScopedTypeVariables- StandaloneDeriving- TupleSections- TypeApplications- TypeFamilies- TypeFamilyDependencies- TypeOperators- NoImplicitPrelude- ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N- build-depends:- aeson- , aeson-pretty- , ansi-terminal- , base >=4.7 && <5- , bimap- , bytestring- , clock- , constraints-extras- , containers- , data-default- , dependent-map- , dependent-sum- , directory- , extra- , file-embed- , filepath- , http-conduit- , ilist- , lens- , mtl- , optparse-applicative- , primitive- , ref-tf- , reflex >= 0.8 && <1- , reflex-potatoes >=0.1- , reflex-test-host >=0.1.2.2- , relude- , semialign- , template-haskell- , text- , these- , time- , vty- default-language: Haskell2010- test-suite tinytools-vty-test type: exitcode-stdio-1.0 main-is: Spec.hs@@ -343,43 +269,17 @@ NoImplicitPrelude ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N build-depends:- HUnit- , aeson- , aeson-pretty- , ansi-terminal- , base >=4.7 && <5- , bimap- , bytestring- , clock- , constraints-extras- , containers- , data-default- , dependent-map- , dependent-sum- , directory- , extra- , file-embed- , filepath+ base >=4.7 && <5+ , HUnit , hspec , hspec-contrib- , http-conduit- , ilist- , lens- , mtl- , optparse-applicative+ , reflex >= 0.9.2 && < 1+ , reflex-test-host >=0.1.2.3 && < 0.2+ , reflex-vty >= 0.5.1 && < 0.6+ , vty>=5.28 && <5.39+ , relude >= 1.2.1 && < 1.3+ , tinytools >= 0.1.0.7 && < 0.2 , tinytools-vty- , primitive- , ref-tf- , reflex- , reflex-potatoes- , reflex-test-host >=0.1.2.2- , reflex-vty >= 0.5.1- , relude- , semialign- , template-haskell- , text- , these- , time- , tinytools >= 0.1.0.3- , vty+ , ref-tf >= 0.5.0 && < 0.6+ , data-default >= 0.7.1 && < 0.8 default-language: Haskell2010
+ tutorial.md view
@@ -0,0 +1,51 @@+(open in the app instead for a better view, browser code preview doesn't render certain unicode characters in monospace correctly)++```++[new][open][save][save as] * ╔══════════════════════════════╗ +[export to "potato.txt"][quit] * ║ ║ ╔IMPORTANT#########################╗ +-------------------------------* ███████████████████ ╚>#to explore the canvas, click the # +[(v)select][(p)an][(b)ox] * █THIS IS TINYTOOLS█═╗ #[(p)an] button or press p # +[(l)ine][(t)extbox][pai(n)t] * ███████████████████ ║ # # +-------------------------------* v #then click and drag in the canvas # +⇊ea 𐃛 starter #* <═══╗ ╔══════════════════════╗ #area to move your view # + ea ⧈ info: more features #* ║ ║the menu on the left ║ # # + ea ⤡ <line> #* ╚═══║has a lot of important║═══╗ # # + ea ⤡ <line> #* ║operations ║ ║ ╚##################################╝ + ea ⤡ <line> #* ╚══════════════════════╝ ║ ^ + ea ⧈ info: pan #* ║ ║ ║ + ea ⤡ <line> #* v ║ ║ + ea ⧈ info: box parameters #* ╔═══════════════════════════╗ ║ ║ + ea ⤡ <line> #* ║to draw a box, click the ║ ║ ║ + ea ⤡ <line> #* ║[(b)ox] button or press b ║ ║ ╚════════════════════╗ + ea ⧈ info: canvas #* ║ ║ ║ ║ + ea ⧈ info: box #* ║then click and drag in the ║ ║ ║ + ea ⧈ info: menu #* ║canvas area ║ ║ *------------------* ║ + ea ⤡ <line> #* ╚═══════════════════════════╝ ║ |this area (to the | ║ + ea ⤡ <line> #* ║ ╚═══════════════════════>|right) is called |═╝ + ea ⧈ title #* ║ |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! ║ + #* ║ ║ ║ ║ + #* ╚═════════════════════════════════╝ ║ ║ + #* ║ ║ + #* ║ ║ + #* ║ ║ + #* ║ ║ + #* ║ ║ + #* ║ ║ + #* ║ ║ + #* ║ ║ +[new folder] * ║ ║ +-------------------------------* ╚═══════════════════════════════╝ +canvas: * + width: 500 * +height: 200 * +```
− tutorial.potato
@@ -1,5 +0,0 @@-{- "_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-}
+ tutorial.tt view
@@ -0,0 +1,5 @@+{+ "_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+}
+ xterm-256color_termwidthfile view
binary file changed (absent → 11493 bytes)