caixanegra 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -2
- data/app/assets/javascripts/caixanegra/designer.js +163 -17
- data/app/assets/stylesheets/caixanegra/designer.scss +49 -3
- data/app/views/caixanegra/designer/index.html.erb +26 -2
- data/app/views/layouts/caixanegra/application.html.erb +1 -1
- data/lib/caixanegra/manager.rb +1 -1
- data/lib/caixanegra/unit_helper.rb +13 -4
- data/lib/caixanegra/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 002efa6f5c1a220426d102e5f055b50eff9fb55e5742d6c2c33d6474f890fd8a
|
4
|
+
data.tar.gz: 03c265dbfa950af4d043bfc56711593b2cb1661f76fc2ca40327fbc7a7f5e309
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da48c854b8646515af968b7c685cece267a5bed236f1f58d5331de39a2d75bded3f0896b84d09de7cbe70c0fad7f388ef5891684bc04f45995e0b1afdc9d1784
|
7
|
+
data.tar.gz: 28b98d23640368db0f32475c4cd6209fb39648e723e63d70ae2fd814e88c05ca6c9033dccc1accfa3c2c331413569fb54e1b52f115cc26841703019dec4fa0a2
|
data/README.md
CHANGED
@@ -32,18 +32,21 @@ Caixanegra.setup do |config|
|
|
32
32
|
config.units = [
|
33
33
|
Caixanegra::Units::AwesomeUnit,
|
34
34
|
Some::Other::Namespace::AnotherAwesomeUnit,
|
35
|
-
Caixanegra::Units::SuperUnit,
|
35
|
+
[Caixanegra::Units::NamespaceOne::SuperUnit, :ns1_super_unit],
|
36
|
+
[Caixanegra::Units::NamespaceTwo::SuperUnit, :ns2_super_unit],
|
36
37
|
]
|
37
38
|
config.transient_store = GreatTransientStore.new
|
38
39
|
end
|
39
40
|
```
|
41
|
+
While configuring units from your codebase, if provided in the array form, **caixanegra** will use the second array item as the class name. Otherwise it will infer from class.
|
42
|
+
**NOTE:** You can't have two classes with the same name as **caixanegra** will use the last one
|
40
43
|
|
41
44
|
With the designer configured, you can use `Caixanegra::Manager` to handle the previously stored definition (or an empty one).
|
42
45
|
This will give you the UID that **caixanegra** designer will understand, using transient store.
|
43
46
|
|
44
47
|
```ruby
|
45
48
|
my_flow = somewhere.get_flow # get from your own persistence or transport solution
|
46
|
-
uid = Caixanegra::Manager.handler(my_flow || {})
|
49
|
+
uid = Caixanegra::Manager.handler(flow_definition: my_flow || {})
|
47
50
|
```
|
48
51
|
|
49
52
|
You can then safely navigate to the designer.
|
@@ -303,6 +303,10 @@ window.Caixanegra.Designer = {
|
|
303
303
|
document.querySelector("#console").addEventListener("click", this.toggleExecutionConsole.bind(this));
|
304
304
|
document.querySelector("#clearCarryOverObject").addEventListener("click", this.clearCarryOverObject.bind(this));
|
305
305
|
document.querySelector("#reset").addEventListener("click", this.resetExecution.bind(this));
|
306
|
+
document.querySelector("#duplicate").addEventListener("click", this.duplicateUnit.bind(this));
|
307
|
+
document.querySelector("#importFlow").addEventListener("click", this.#importFlow.bind(this));
|
308
|
+
document.querySelector("#exportFlow").addEventListener("click", this.#exportFlow.bind(this));
|
309
|
+
document.querySelector("#importFlowFile").addEventListener("change", this.#importFlowFileChanged.bind(this));
|
306
310
|
this.toggleBlocker(true, "loading your flow");
|
307
311
|
window.addEventListener("resize", this.#windowResized.bind(drawingSurface));
|
308
312
|
drawingSurface.addEventListener("update_start", this.#engineUpdate.bind(this));
|
@@ -385,6 +389,8 @@ window.Caixanegra.Designer = {
|
|
385
389
|
}
|
386
390
|
|
387
391
|
container.append(wrapper);
|
392
|
+
|
393
|
+
return wrapper;
|
388
394
|
}
|
389
395
|
|
390
396
|
deleteUnit() {
|
@@ -394,6 +400,12 @@ window.Caixanegra.Designer = {
|
|
394
400
|
this.toggleDeleteConfirmation();
|
395
401
|
}
|
396
402
|
|
403
|
+
duplicateUnit() {
|
404
|
+
this.unitDetailPane.classList.remove("-open");
|
405
|
+
this.unitDebugHitsPane.classList.remove("-open");
|
406
|
+
this.cloneUnit(this.selectedUnit.oid);
|
407
|
+
}
|
408
|
+
|
397
409
|
flushToConsole(entries) {
|
398
410
|
const container = document.querySelector("#executionConsole");
|
399
411
|
container.innerHTML = "";
|
@@ -468,7 +480,8 @@ window.Caixanegra.Designer = {
|
|
468
480
|
flowSnapshot() {
|
469
481
|
const snapshot = {
|
470
482
|
entrypoint: this.#units.find((unit) => {return unit.type === "starter"})?.oid,
|
471
|
-
units: []
|
483
|
+
units: [],
|
484
|
+
initialCarryover: this.digInitialCarryOver(document.querySelector("#carryOverObject"))
|
472
485
|
};
|
473
486
|
|
474
487
|
this.#units.forEach((unit) => {
|
@@ -587,33 +600,81 @@ window.Caixanegra.Designer = {
|
|
587
600
|
});
|
588
601
|
|
589
602
|
this.#loadedComponents.catalog = true;
|
590
|
-
this
|
603
|
+
this.reveal();
|
591
604
|
});
|
592
605
|
}
|
593
606
|
|
594
607
|
loadFlow() {
|
595
608
|
this.api.getFlow(this.flowId).then((response) => {
|
596
609
|
const flowData = response;
|
597
|
-
this.gre.clear();
|
598
|
-
this.#units = [];
|
599
610
|
|
600
|
-
(flowData
|
601
|
-
this.createUnit(unit);
|
602
|
-
});
|
603
|
-
|
604
|
-
this.#units.forEach((unit) => {
|
605
|
-
(unit?.exits || []).forEach((exit) => {
|
606
|
-
const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
|
607
|
-
if (object) { exit.target = object; }
|
608
|
-
});
|
609
|
-
});
|
611
|
+
this.#loadFlowFromJSON(flowData);
|
610
612
|
|
611
613
|
this.#loadedComponents.flow = true;
|
612
|
-
this
|
614
|
+
this.reveal();
|
615
|
+
});
|
616
|
+
}
|
617
|
+
|
618
|
+
#loadFlowFromJSON(flowData) {
|
619
|
+
this.gre.clear();
|
620
|
+
this.#units = [];
|
621
|
+
|
622
|
+
(flowData?.units || []).forEach((unit) => {
|
623
|
+
this.createUnit(unit);
|
624
|
+
});
|
625
|
+
|
626
|
+
this.#units.forEach((unit) => {
|
627
|
+
(unit?.exits || []).forEach((exit) => {
|
628
|
+
const object = this.#units.find((targetUnit) => targetUnit.oid === exit.target);
|
629
|
+
if (object) { exit.target = object; }
|
630
|
+
});
|
613
631
|
});
|
632
|
+
|
633
|
+
this.buildInitialCarryover(flowData.initialCarryover);
|
634
|
+
}
|
635
|
+
|
636
|
+
digBuildInitialCarryover(container, key, objectPointer) {
|
637
|
+
const lastChild = container.childNodes[container.childNodes.length -1];
|
638
|
+
|
639
|
+
if (key !== null) {
|
640
|
+
container.childNodes[0].querySelector("input").value = key;
|
641
|
+
}
|
642
|
+
|
643
|
+
if (Array.isArray(objectPointer)) {
|
644
|
+
this.coeAssignType(lastChild, "array");
|
645
|
+
|
646
|
+
for (let idx = 0; idx < objectPointer.length; idx++) {
|
647
|
+
this.digBuildInitialCarryover(
|
648
|
+
this.coeAddRow(container.querySelector(".rows")),
|
649
|
+
null,
|
650
|
+
objectPointer[idx]
|
651
|
+
);
|
652
|
+
}
|
653
|
+
} else if (typeof objectPointer === "object") {
|
654
|
+
this.coeAssignType(lastChild, "object");
|
655
|
+
|
656
|
+
for (const key in objectPointer) {
|
657
|
+
this.digBuildInitialCarryover(this.coeAddRow(container.querySelector(".rows")), key, objectPointer[key]);
|
658
|
+
}
|
659
|
+
} else if (typeof objectPointer === "number") {
|
660
|
+
this.coeAssignType(lastChild, "number");
|
661
|
+
lastChild.querySelector("input").value = objectPointer;
|
662
|
+
} else {
|
663
|
+
this.coeAssignType(lastChild, "string");
|
664
|
+
lastChild.querySelector("input").value = objectPointer;
|
665
|
+
}
|
666
|
+
}
|
667
|
+
|
668
|
+
buildInitialCarryover(initialCarryover) {
|
669
|
+
this.clearCarryOverObject();
|
670
|
+
const base = document.querySelector("#carryOverObject > .rows");
|
671
|
+
|
672
|
+
for (const key in initialCarryover) {
|
673
|
+
this.digBuildInitialCarryover(this.coeAddRow(base), key, initialCarryover[key]);
|
674
|
+
}
|
614
675
|
}
|
615
676
|
|
616
|
-
|
677
|
+
reveal() {
|
617
678
|
if (this.#loadedComponents.flow && this.#loadedComponents.catalog) {
|
618
679
|
this.toggleBlocker(false);
|
619
680
|
}
|
@@ -652,7 +713,10 @@ window.Caixanegra.Designer = {
|
|
652
713
|
createUnit(params) {
|
653
714
|
this.#sequence++;
|
654
715
|
const newUnit = new Caixanegra.Designer.Unit({
|
655
|
-
position: new Sabertooth.Vector2(
|
716
|
+
position: new Sabertooth.Vector2(
|
717
|
+
-this.gre.worldCenter.x + 20,
|
718
|
+
-this.gre.worldCenter.y + 20
|
719
|
+
),
|
656
720
|
type: params.type,
|
657
721
|
title: params.title,
|
658
722
|
class: params.class,
|
@@ -710,6 +774,21 @@ window.Caixanegra.Designer = {
|
|
710
774
|
});
|
711
775
|
}
|
712
776
|
|
777
|
+
cloneUnit(oid) {
|
778
|
+
const unit = this.#units.find((unit) => unit.oid === oid);
|
779
|
+
const dup = this.#dupObject(unit);
|
780
|
+
|
781
|
+
dup.oid = Sabertooth.Utils.generateOId();
|
782
|
+
dup.position = { x: unit.position.x + 25, y: unit.position.y + 25 };
|
783
|
+
dup.title = `${dup.title} (copy)`;
|
784
|
+
|
785
|
+
dup.exits.forEach(exit => {
|
786
|
+
delete exit.target;
|
787
|
+
});
|
788
|
+
|
789
|
+
this.createUnit(dup);
|
790
|
+
}
|
791
|
+
|
713
792
|
showError(message) {
|
714
793
|
this.actionMessage(true, message, "error");
|
715
794
|
setTimeout(() => {this.actionMessage(false)}, 5000);
|
@@ -759,6 +838,73 @@ window.Caixanegra.Designer = {
|
|
759
838
|
this.unitDebugHitsPane.classList.remove("-open");
|
760
839
|
}
|
761
840
|
|
841
|
+
#dupObject(obj) {
|
842
|
+
if (typeof obj !== 'object' || obj === null) {
|
843
|
+
return obj;
|
844
|
+
}
|
845
|
+
|
846
|
+
const clonedObject = Array.isArray(obj) ? [] : {};
|
847
|
+
|
848
|
+
for (const key in obj) {
|
849
|
+
if (obj.hasOwnProperty(key)) {
|
850
|
+
clonedObject[key] = this.#dupObject(obj[key]);
|
851
|
+
}
|
852
|
+
}
|
853
|
+
|
854
|
+
return clonedObject;
|
855
|
+
}
|
856
|
+
|
857
|
+
#importFlow() {
|
858
|
+
document.querySelector("#importFlowFile").click();
|
859
|
+
}
|
860
|
+
|
861
|
+
#importFlowFileChanged() {
|
862
|
+
const fileInput = document.querySelector("#importFlowFile");
|
863
|
+
|
864
|
+
if (fileInput.files.length === 0) {
|
865
|
+
return;
|
866
|
+
}
|
867
|
+
|
868
|
+
const selectedFile = fileInput.files[0];
|
869
|
+
const reader = new FileReader();
|
870
|
+
|
871
|
+
reader.onload = (event) => {
|
872
|
+
this.toggleBlocker(true, "importing your flow");
|
873
|
+
this.gre.disable();
|
874
|
+
const flowBackup = this.flowSnapshot();
|
875
|
+
|
876
|
+
try {
|
877
|
+
this.#loadFlowFromJSON(JSON.parse(event.target.result));
|
878
|
+
|
879
|
+
this.actionMessage(true, "Flow imported", "ok");
|
880
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
881
|
+
} catch (error) {
|
882
|
+
this.#loadFlowFromJSON(flowBackup);
|
883
|
+
|
884
|
+
this.actionMessage(true, "Failed to import flow. Rolled back", "error");
|
885
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
886
|
+
}
|
887
|
+
|
888
|
+
this.gre.enable();
|
889
|
+
this.reveal();
|
890
|
+
};
|
891
|
+
|
892
|
+
reader.readAsText(selectedFile);
|
893
|
+
}
|
894
|
+
|
895
|
+
#exportFlow() {
|
896
|
+
const blob = new Blob([JSON.stringify(this.flowSnapshot())], { type: "application/json" });
|
897
|
+
const url = URL.createObjectURL(blob);
|
898
|
+
const link = document.createElement('a');
|
899
|
+
link.href = url;
|
900
|
+
link.download = "flow_definition.cxn";
|
901
|
+
link.click();
|
902
|
+
URL.revokeObjectURL(url);
|
903
|
+
|
904
|
+
this.actionMessage(true, "Flow exported", "ok");
|
905
|
+
setTimeout(() => {this.actionMessage(false)}, 4000);
|
906
|
+
}
|
907
|
+
|
762
908
|
#executeFlow() {
|
763
909
|
this.toggleBlocker(true, "saving your flow");
|
764
910
|
|
@@ -3,6 +3,10 @@ $accent-color: rgb(105, 105, 105);
|
|
3
3
|
$accent-highlight-color: lighten($accent-color, 20%);
|
4
4
|
$accent-contrast-color: darken($accent-color, 20%);
|
5
5
|
|
6
|
+
#importFlowFile {
|
7
|
+
display: none;
|
8
|
+
}
|
9
|
+
|
6
10
|
#execution {
|
7
11
|
position: fixed;
|
8
12
|
display: flex;
|
@@ -37,6 +41,33 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
37
41
|
}
|
38
42
|
}
|
39
43
|
|
44
|
+
#io {
|
45
|
+
position: fixed;
|
46
|
+
display: flex;
|
47
|
+
flex-direction: row;
|
48
|
+
bottom: 8px;
|
49
|
+
right: 8px;
|
50
|
+
|
51
|
+
div {
|
52
|
+
cursor: pointer;
|
53
|
+
transition: all 250ms ease-in-out;
|
54
|
+
height: 18px;
|
55
|
+
width: 18px;
|
56
|
+
margin-left: 8px;
|
57
|
+
|
58
|
+
&:hover {
|
59
|
+
transform: scale(1.1);
|
60
|
+
}
|
61
|
+
|
62
|
+
svg {
|
63
|
+
path, polygon, line, polyline {
|
64
|
+
fill: white;
|
65
|
+
stroke: white;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
40
71
|
#addUnit {
|
41
72
|
position: fixed;
|
42
73
|
border-radius: 50%;
|
@@ -649,10 +680,25 @@ $accent-contrast-color: darken($accent-color, 20%);
|
|
649
680
|
flex-direction: row;
|
650
681
|
justify-content: space-between;
|
651
682
|
|
652
|
-
.name {
|
653
|
-
font-size: 1em;
|
654
|
-
font-weight: bold;
|
683
|
+
.name-holder {
|
655
684
|
width: 100%;
|
685
|
+
display: flex;
|
686
|
+
flex-direction: row;
|
687
|
+
|
688
|
+
.name {
|
689
|
+
font-size: 1em;
|
690
|
+
font-weight: bold;
|
691
|
+
}
|
692
|
+
|
693
|
+
#duplicate {
|
694
|
+
margin-left: 5px;
|
695
|
+
width: 10px;
|
696
|
+
cursor: pointer;
|
697
|
+
|
698
|
+
svg path {
|
699
|
+
fill: $accent-contrast-color;
|
700
|
+
}
|
701
|
+
}
|
656
702
|
}
|
657
703
|
|
658
704
|
.delete {
|
@@ -25,11 +25,28 @@
|
|
25
25
|
</svg>
|
26
26
|
</div>
|
27
27
|
<div id="console">
|
28
|
-
<svg version="1.1"
|
28
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
29
29
|
<path d="M24,24H0V0h24V24z M2,22h20V2H6v2h16v2H2V22z M2,4h2V2H2V4z M16,16h-6v-2h6V16z M5.7,15.7l-1.4-1.4L6.6,12L4.3,9.7 l1.4-1.4L9.4,12L5.7,15.7z"/>
|
30
30
|
</svg>
|
31
31
|
</div>
|
32
32
|
</div>
|
33
|
+
<div id="io">
|
34
|
+
<input type="file" id="importFlowFile" accept=".cxn">
|
35
|
+
<div id="importFlow">
|
36
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
37
|
+
<polyline id="primary" points="13 9 13 13 9 13" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline>
|
38
|
+
<line id="primary-2" data-name="primary" x1="3" y1="3" x2="13" y2="13" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
39
|
+
<path id="primary-3" data-name="primary" d="M13.89,5H20a1,1,0,0,1,1,1V20a1,1,0,0,1-1,1H6a1,1,0,0,1-1-1V13.89" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
40
|
+
</svg>
|
41
|
+
</div>
|
42
|
+
<div id="exportFlow">
|
43
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" xml:space="preserve">
|
44
|
+
<polyline id="primary" points="17 3 21 3 21 7" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></polyline>
|
45
|
+
<line id="primary-2" data-name="primary" x1="11" y1="13" x2="21" y2="3" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></line>
|
46
|
+
<path id="primary-3" data-name="primary" d="M19,13.89V20a1,1,0,0,1-1,1H4a1,1,0,0,1-1-1V6A1,1,0,0,1,4,5h6.11" style="fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2;"></path>
|
47
|
+
</svg>
|
48
|
+
</div>
|
49
|
+
</div>
|
33
50
|
<div id="addUnit"><span>+</span></div>
|
34
51
|
<div id="save"><span>save</span></div>
|
35
52
|
<div id="unitMenu"></div>
|
@@ -51,7 +68,14 @@
|
|
51
68
|
<div class="color-code"></div>
|
52
69
|
<div class="content">
|
53
70
|
<div id="unitDetailClass" class="unit-detail-headers">
|
54
|
-
<div class="name"
|
71
|
+
<div class="name-holder">
|
72
|
+
<div class="name"></div>
|
73
|
+
<span id="duplicate">
|
74
|
+
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
75
|
+
<path d="M6 6V2c0-1.1.9-2 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-4v4a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8c0-1.1.9-2 2-2h4zm2 0h4a2 2 0 0 1 2 2v4h4V2H8v4zM2 8v10h10V8H2zm4 4v-2h2v2h2v2H8v2H6v-2H4v-2h2z"/>
|
76
|
+
</svg>
|
77
|
+
</span>
|
78
|
+
</div>
|
55
79
|
<div class="delete">
|
56
80
|
<div id="toggler">
|
57
81
|
<svg viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
|
data/lib/caixanegra/manager.rb
CHANGED
@@ -6,8 +6,13 @@ module Caixanegra
|
|
6
6
|
|
7
7
|
scopes = (scope || "").split(",").map(&:to_sym)
|
8
8
|
Caixanegra.units.each do |unit|
|
9
|
-
|
10
|
-
|
9
|
+
unit_scope = unit.is_a?(Array) ? unit[0].scope : unit.scope
|
10
|
+
if unit_scope.nil? || unit_scope.any? { |checking_scope| scopes.include?(checking_scope) }
|
11
|
+
if unit.is_a? Array
|
12
|
+
units[unit[1]] = unit[0]
|
13
|
+
else
|
14
|
+
units[unit.name.demodulize.underscore.to_sym] = unit
|
15
|
+
end
|
11
16
|
end
|
12
17
|
end
|
13
18
|
|
@@ -18,11 +23,15 @@ module Caixanegra
|
|
18
23
|
units = {}
|
19
24
|
|
20
25
|
Caixanegra.units.each do |unit|
|
21
|
-
|
26
|
+
if unit.is_a? Array
|
27
|
+
units[unit[1]] = unit[0]
|
28
|
+
else
|
29
|
+
units[unit.name.demodulize.underscore.to_sym] = unit
|
30
|
+
end
|
22
31
|
end
|
23
32
|
|
24
33
|
units
|
25
34
|
end
|
26
35
|
end
|
27
36
|
end
|
28
|
-
end
|
37
|
+
end
|
data/lib/caixanegra/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: caixanegra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergiorribeiro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|