Skip to content

Commit 93a3dac

Browse files
authored
Merge pull request #380 from statebox/development
Merge development into master
2 parents d7dadc1 + a0d07cb commit 93a3dac

File tree

294 files changed

+80061
-7725
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

294 files changed

+80061
-7725
lines changed

.gitignore

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1 @@
1-
/bower_components/
2-
/node_modules/
3-
/.pulp-cache/
4-
/output/
5-
/generated-docs/
6-
/.psc-package/
7-
/.psc*
8-
/.purs*
9-
/.psa*
1+
.psc-ide-port

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
language: node_js
2+
node_js: stable
3+
script:
4+
- scripts/buildandtest.sh

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
# Statebox Studio
1+
# Statebox PureScript monorepo
22

3-
## Try online
3+
studio demo: [![Netlify Status](https://api.netlify.com/api/v1/badges/8e848ea5-2d7f-4e74-98d7-330a13819bd0/deploy-status)](https://app.netlify.com/sites/statebox-studio-test/deploys).
44

5-
https://statebox-studio-test.netlify.com/
5+
source code docs: [![Netlify Status](https://api.netlify.com/api/v1/badges/40809c59-434f-4210-a088-903d2bea5fbf/deploy-status)](https://app.netlify.com/sites/zealous-swirles-adb9a4/deploys)
66

7-
## Build and run
7+
monorepo: [![Build Status](https://travis-ci.com/statebox/purescript-studio.svg?token=cVQf36PbhPuPrsV4Ks8k&branch=development)](https://travis-ci.com/statebox/purescript-studio)
88

9-
Clone and `cd` into the repo, then do:
10-
```
11-
npm i
12-
npm run bundle
13-
```
14-
Then open `dist/index.html` in your browser.
9+
---
10+
11+
This contains Statebox Studio, Statebox REST API, Statebox REST client and associated projects.
12+
13+
See the docs in the [studio](studio/README.md) directory.

dist/index.html

Lines changed: 0 additions & 51 deletions
This file was deleted.

dist/vendor/tailwind.min.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

halogen-diagram-editor/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/bower_components/
2+
/node_modules/
3+
/.pulp-cache/
4+
/output/
5+
/generated-docs/
6+
/.psc-package/
7+
/.psc*
8+
/.purs*
9+
/.psa*
10+
/.spago

halogen-diagram-editor/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "purescript-halogen-diagram-editor",
3+
"version": "1.0.0",
4+
"description": "Halogen brick diagram editor component",
5+
"author": "Erik Post <[email protected]>",
6+
"main": "index.js",
7+
"scripts": {
8+
"postinstall": "spago install",
9+
"build": "spago build --purs-args --censor-codes=ImplicitImport,ImplicitQualifiedImport,HidingImport",
10+
"watch": "spago build --watch --purs-args --censor-codes=ImplicitImport,ImplicitQualifiedImport,HidingImport"
11+
},
12+
"devDependencies": {
13+
"purescript": "^0.13.5",
14+
"purescript-psa": "^0.7.3",
15+
"spago": "^0.13"
16+
},
17+
"license": "ISC"
18+
}

halogen-diagram-editor/spago.dhall

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{ name =
2+
"halogen-diagram-editor"
3+
, dependencies =
4+
[ "vec"
5+
, "halogen-svg"
6+
]
7+
, packages =
8+
../packages.dhall
9+
, sources =
10+
[ "src/**/*.purs", "test/**/*.purs" ]
11+
}

src/Data/Diagram/FromNLL.purs renamed to halogen-diagram-editor/src/Data/Diagram/FromNLL.purs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module View.Diagram.FromNLL (fromNLL, ErrDiagramEncoding(..)) where
1+
module Data.Diagram.FromNLL (fromNLL, ErrDiagramEncoding(..)) where
22

33
import Data.Array hiding (head, tail, length)
44
import Data.Array.NonEmpty (toNonEmpty)
@@ -35,15 +35,15 @@ instance showErrDiagramEncoding :: Show ErrDiagramEncoding where
3535

3636
type DiagramM a = Either ErrDiagramEncoding a
3737

38-
-- | See https://adoring-curie-7b92fd.netlify.com/.
38+
-- | See https://adoring-curie-7b92fd.netlify.com and https://docs.statebox.org/spec.
3939
type BrickDiagram a =
4040
{ width :: Int
41-
, elements :: Array a
41+
, pixels :: Array a
4242
}
4343

4444
-- | Makes a brick diagram without checking bounds and sizes.
4545
mkBrickDiagramUnsafe :: a. Int -> Array a -> BrickDiagram a
46-
mkBrickDiagramUnsafe width ops = { width: width, elements: ops }
46+
mkBrickDiagramUnsafe width ops = { width: width, pixels: ops }
4747

4848
-- | Safe constructor for brick diagrams, checks that the encoded array has the right size.
4949
mkBrickDiagram :: a. Int -> Array a -> DiagramM (BrickDiagram a)
@@ -54,12 +54,12 @@ mkBrickDiagram w ops | length ops `mod` w == 0 = Right $ mkBrickDiagramUnsafe w
5454

5555
-- | Get the height of a brick diagram, assuming it's perfectly rectangular.
5656
height :: a. BrickDiagram a -> Int
57-
height b = (length b.elements) / b.width
57+
height b = (length b.pixels) / b.width
5858

5959
-- | Return the element below the given coordinates, if any.
6060
below :: a. BrickDiagram a -> Int -> Int -> Maybe a
6161
below b x y = let l = min (height b) (y + 1) in
62-
index b.elements (l * b.width + x)
62+
index b.pixels (l * b.width + x)
6363

6464
-- Converting brick diagrams to directed graphs -----------------------------------------------------------------------
6565

@@ -88,11 +88,11 @@ isSelfArrow {source, target} = source == target
8888
brickToGraph :: a. Eq a => BrickDiagram a -> Array (GraphArrow a)
8989
brickToGraph b = mapMaybe (edge b) indices
9090
where
91-
indices = range 0 (length b.elements - 1)
91+
indices = range 0 (length b.pixels - 1)
9292

9393
-- | Given a brick diagram and an index, return one of the arrows leaving the node at this index.
9494
edge :: a. Eq a => BrickDiagram a -> Int -> Maybe (GraphArrow a)
95-
edge b i = { source: _, target: _ } <$> index b.elements i <*> below b xPos yPos
95+
edge b i = { source: _, target: _ } <$> index b.pixels i <*> below b xPos yPos
9696
where
9797
yPos = i / b.width
9898
xPos = i `rem` b.width
@@ -142,7 +142,7 @@ type ConsecutiveValues a = { value :: a, length :: Int }
142142
-- | [--4--]
143143
-- | ```
144144
graphToOps :: a. Eq a => Show a => BrickDiagram a -> Array Operator
145-
graphToOps { width: width, elements: brick } =
145+
graphToOps { width: width, pixels: brick } =
146146
let lines = splitLines width brick
147147
l = mapWithIndex mapOperators $ map packConsecutive lines in
148148
concat l
@@ -170,8 +170,8 @@ nllToBrickDiagram :: Array Int -> DiagramM (BrickDiagram Int)
170170
nllToBrickDiagram nll = case uncons nll of
171171
Just { head, tail } -> mkBrickDiagram head tail
172172
Nothing -> Right $ mkBrickDiagramUnsafe 0 []
173-
174-
fromNLL :: Array Int -> String -> DiagramM DiagramInfo
175-
fromNLL nll name = do bricks <- nllToBrickDiagram nll
173+
174+
fromNLL :: String -> Array Int -> DiagramM DiagramInfo
175+
fromNLL name nll = do bricks <- nllToBrickDiagram nll
176176
_ <- parseBrickToGraph 0 bricks
177177
Right $ brickToDiagram bricks name
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module View.Diagram.Common where
2+
3+
import Prelude
4+
import Data.Int (toNumber, round)
5+
import Halogen.HTML.Properties as HP
6+
import Halogen.HTML.Core as HC
7+
8+
-- TODO copied from halogen-petrinet-editor.View.Common
9+
classesWithNames :: r i. Array String -> HP.IProp (class :: String | r) i
10+
classesWithNames names = HP.classes (HC.ClassName <$> names)
11+
12+
-- snap s x = x - (x % s)
13+
snap :: Int -> Int -> Int
14+
snap s x = s * round (toNumber x / toNumber s) -- s * (x // s)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
module View.Diagram.DiagramEditor where
2+
3+
import Prelude hiding (div)
4+
5+
import Data.Array (snoc, length)
6+
import Data.Maybe (Maybe(..), maybe)
7+
import Data.Tuple.Nested ((/\))
8+
import Data.Vec3 (Vec3, vec3, _x, _y, _z)
9+
import Effect.Aff.Class (class MonadAff)
10+
import GridKit.KeyHandler
11+
import Halogen as H
12+
import Halogen (ComponentHTML, HalogenM)
13+
import Halogen.HTML (HTML, div, text, button)
14+
import Halogen.HTML.Core (ClassName(..))
15+
import Halogen.HTML.Events (onClick)
16+
import Halogen.HTML.Properties (classes, tabIndex)
17+
import Unsafe.Coerce (unsafeCoerce)
18+
import Web.DOM (Element)
19+
import Web.HTML.HTMLElement (HTMLElement)
20+
21+
import View.ReactiveInput as ReactiveInput
22+
import View.Diagram.Common (classesWithNames)
23+
import View.Diagram.Model (DragStart(..), Operators)
24+
import View.Diagram.Update (Action(..), MouseMsg(..), Msg(..), State, evalModel)
25+
import View.Diagram.View as View
26+
import View.Diagram.Inspector as Inspector
27+
28+
initialState :: State
29+
initialState =
30+
{ model:
31+
{ config: { scale: 24, width: 550, height: 450 }
32+
, selectedOpId: Nothing
33+
, mouseOver: Nothing
34+
, mousePos: vec3 0 0 0
35+
, cursorPos: vec3 0 0 0
36+
, mousePressed: false
37+
, dragStart: DragNotStarted
38+
}
39+
, msg: ""
40+
, keyHelpVisible: false
41+
, componentElemMaybe: Nothing
42+
, inspectorVisible: false
43+
}
44+
45+
ui :: m q. MonadAff m => H.Component HTML q Operators Msg m
46+
ui = ReactiveInput.mkComponent { initialState, render, handleInput, handleAction }
47+
where
48+
keys :: KeysWithHelpPopup Action
49+
keys = keysWithHelpPopup
50+
{ keys: keyHandler [ Shortcut noMods "Space" ] (Just $ text "Insert a new operator") CreateOp <>
51+
cursorKeyHandler noMods MoveCursor
52+
, popupAction: ToggleKeyHelp
53+
}
54+
55+
render :: Operators -> State -> ComponentHTML Action () m
56+
render ops state =
57+
div [ classes [ ClassName "css-diagram-editor" ]
58+
, tabIndex 0
59+
, keys.onKeyDown
60+
]
61+
[ div [ classes [] ]
62+
[ View.diagramEditorSVG state.componentElemMaybe ops state.model <#> MouseAction
63+
, div [ classes [ ClassName "css-diagram-editor-inspector-container" ] ]
64+
[ div [ classes [ ClassName "css-diagram-editor-inspector-link-container" ] ]
65+
[ button [ onClick \_ -> Just ToggleInspector ]
66+
[ text $ (if state.inspectorVisible then "Hide" else "Show") <> " inspector" ]
67+
]
68+
, if state.inspectorVisible
69+
then div [ classesWithNames [ "mt-4", "rb-2", "p-4", "bg-grey-lightest", "text-grey-dark", "rounded", "text-sm" ] ]
70+
[ Inspector.view ops state ]
71+
else div [] []
72+
]
73+
]
74+
, keys.helpPopup state.keyHelpVisible
75+
]
76+
77+
handleInput :: Operators -> HalogenM State Action () Msg m Unit
78+
handleInput _ = do
79+
componentElemMaybe <- getHTMLElementRef' View.componentRefLabel
80+
H.modify_ \state -> state { componentElemMaybe = componentElemMaybe }
81+
82+
handleAction :: Operators -> Action -> HalogenM State Action () Msg m Unit
83+
handleAction ops = case _ of
84+
85+
CreateOp -> do
86+
m <- H.get <#> _.model
87+
let id = length ops
88+
let newOp = { identifier: "new" <> show id, pos: m.cursorPos + vec3 1 0 7, label: "new" <> show id }
89+
H.modify_ \st -> st { model = m { cursorPos = m.cursorPos + vec3 0 1 0 } }
90+
H.raise $ OperatorsChanged (ops `snoc` newOp)
91+
92+
MoveCursor delta -> do
93+
m <- H.get <#> _.model
94+
let { cursorPos, config: { scale, width, height } } = m
95+
let cursorPos' = clamp2d (width/scale+1) (height/scale+1) (cursorPos + delta)
96+
H.modify_ \st -> st { model = m { cursorPos = cursorPos' } }
97+
98+
MouseAction msg -> do
99+
state <- H.get
100+
let (opsChanged /\ model') = evalModel msg ops state.model
101+
state' = state { model = model' }
102+
103+
case opsChanged of
104+
Just ops' -> H.raise (OperatorsChanged ops')
105+
Nothing -> pure unit
106+
107+
let isOperatorClicked = case msg of
108+
MouseUp _ -> true
109+
_ -> false
110+
111+
clickedOperatorId = case state'.model.mouseOver of
112+
Just (op /\ oph) | isOperatorClicked -> Just op.identifier
113+
_ -> Nothing
114+
115+
state'' = if isOperatorClicked then state' { model = state'.model { selectedOpId = clickedOperatorId } }
116+
else state'
117+
118+
H.put state''
119+
120+
maybe (pure unit) (H.raise <<< OperatorClicked) clickedOperatorId
121+
122+
ToggleKeyHelp -> do
123+
H.modify_ $ \state -> state { keyHelpVisible = not state.keyHelpVisible }
124+
125+
ToggleInspector -> do
126+
H.modify_ \state -> state { inspectorVisible = not state.inspectorVisible }
127+
128+
--------------------------------------------------------------------------------
129+
130+
clamp2d :: Int -> Int -> Vec3 Int -> Vec3 Int
131+
clamp2d width height xy = vec3 (clamp 0 (width - 1) (_x xy)) (clamp 0 (height - 1) (_y xy)) (_z xy)
132+
133+
-- TODO this is generally useful; move elsewhere
134+
-- This was made because the original implementation from Halogen.Query doesn't seem to work, at least in this case:
135+
-- getHTMLElementRef = map (HTMLElement.fromElement =<< _) <<< getRef
136+
getHTMLElementRef' :: forall s a i o m. H.RefLabel -> HalogenM s a i o m (Maybe HTMLElement)
137+
getHTMLElementRef' = map (map elementToHTMLElement) <<< H.getRef
138+
where
139+
elementToHTMLElement :: Element -> HTMLElement
140+
elementToHTMLElement = unsafeCoerce

0 commit comments

Comments
 (0)