From 4a6a4ff699ee789cc24cde479be332ce3721d98e Mon Sep 17 00:00:00 2001
From: Jake <jake.read@cba.mit.edu>
Date: Thu, 9 May 2019 22:29:46 -0400
Subject: [PATCH] patches and bootstraps

---
 README.md                            |  7 ++-
 hunks/comm/websocketclient.js        | 10 +++-
 hunks/view.js                        |  6 +++
 programs/{ => cuttlefish}/wstst.json | 12 ++++-
 programs/nautilus/ntsave.json        | 22 ++++++++
 programs/tptch.json                  | 42 ---------------
 style.css                            | 21 ++++++++
 view/vdef.js                         |  8 +--
 view/vmsg.js                         | 13 ++++-
 view/vptch.js                        | 81 +++++++++++++++++++++++-----
 10 files changed, 159 insertions(+), 63 deletions(-)
 rename programs/{ => cuttlefish}/wstst.json (88%)
 create mode 100644 programs/nautilus/ntsave.json
 delete mode 100644 programs/tptch.json

diff --git a/README.md b/README.md
index 4d12802..f84ccd5 100644
--- a/README.md
+++ b/README.md
@@ -18,11 +18,16 @@ Native Cuttlefish 'Hunks' are given access to a DOM/div element, making them nic
 
 IAA ... it's all addressing -> ->
 
-remote managers can't just throw messages about: they have to have some contextual awareness of the view ? or their connectedness ? can keep existing structure and just wait until have-said-hello or not 
+remote managers can't just throw messages about: they have to have some contextual awareness of the view ? or their connectedness ? can keep existing structure and just wait until have-said-hello or not
+
+ -> added 'isConnectedTo' feature, won't work when connections disappear though !
+
+... so, and view needs to be sensitive to that, and watch state on a refresh also ?
 
 ... towards decent views ... and a debug-thru-cuttlefish portal for ponyo
  - patchload is nice, ok, this direction
  - layout ... save positions in patches ? unf- force layout ?
+ - write cobs, and a cobs patch -> proto logs out into a browser
  - ... link work ? ... links agreeing with one another before startup
   - results in link saying 'updatehunkdefinition' and manager ... reporting ? or not ?
  - load -> then load -> then log, up through a few links
diff --git a/hunks/comm/websocketclient.js b/hunks/comm/websocketclient.js
index dc22b8c..94b5797 100644
--- a/hunks/comm/websocketclient.js
+++ b/hunks/comm/websocketclient.js
@@ -26,9 +26,10 @@ function WebSocketClient() {
   // this is hunk -> manager commune ...
   let statusMessage = new State('string', 'status', 'closed')
   let retryCountHandle = new State('number', 'retrycount', 3)
+  let resetRetryHandle = new State('boolean', 'retryreset', false)
   let addressState = new State('string', 'address', '127.0.0.1')
   let portState = new State('number', 'port', 2042)
-  this.states.push(statusMessage, retryCountHandle, addressState, portState)
+  this.states.push(statusMessage, retryCountHandle, resetRetryHandle, addressState, portState)
 
   // this ws is a client,
   let ws = {}
@@ -39,6 +40,13 @@ function WebSocketClient() {
     setTimeout(startWs, 500)
   }
 
+  resetRetryHandle.change = (value) => {
+    retryCountHandle.set(3)
+    startWs()
+    // to actually change the value, we would do:
+    // resetRetryHandle.set(value)
+  }
+
   let startWs = () => {
     // manager calls this once
     // it is loaded and state is updated (from program)
diff --git a/hunks/view.js b/hunks/view.js
index 74266ae..4e914ef 100644
--- a/hunks/view.js
+++ b/hunks/view.js
@@ -55,12 +55,16 @@ function View() {
 
   // we have a list of definitions,
   let defs = new Array()
+  // #ref, sloppy
+  this.defs = defs
 
   // tools to write dom-representations of hunks,
   let dt = new DomTools(this)
   // a handy box, for stuff
   let msgbox = new MessageBox(this)
   this.msgbox = msgbox
+  this.interpreterName = null
+  this.interpreterVersion = null
   // and tools for the links,
   let bzt = new BezierTools(this)
   // and for program (patch) management
@@ -880,6 +884,7 @@ function View() {
             msgbox.briefState.decrementHunks()
             msgbox.write(`added a new hunk: ${spec.name}`)
             patchset.onHunkLoaded(nd)
+            $(this.dom).find('.contextmenu').remove()
           } else {
             if(verbose) console.log('replacing hunk at', spec.ind)
             replaceDef(spec)
@@ -899,6 +904,7 @@ function View() {
             stDef = defs[stchHnkInd].states[stchStInd]
             let stValUpdate = MSGS.readFrom(msg, 6, stDef.type).item
             stDef.set(stValUpdate)
+            patchset.onStateChanged(stDef)
             msgbox.write(`changed state at ${stchHnkInd} ${stDef.name} to ${stValUpdate}`)
           } catch (err) {
             console.error('likely error from state change indexing', err)
diff --git a/programs/wstst.json b/programs/cuttlefish/wstst.json
similarity index 88%
rename from programs/wstst.json
rename to programs/cuttlefish/wstst.json
index 20e9a83..f87cc4f 100644
--- a/programs/wstst.json
+++ b/programs/cuttlefish/wstst.json
@@ -2,6 +2,12 @@
   "interpreterName": "cuttlefish",
   "interpreterVersion": "v0.1",
   "hunks": [{
+    "name": "manager",
+    "states": []
+  }, {
+    "name": "view",
+    "states": []
+  }, {
     "name": "link",
     "states": [{
       "ind": 0,
@@ -34,10 +40,14 @@
       "value": 3
     }, {
       "ind": 2,
+      "type": "boolean",
+      "value": false
+    }, {
+      "ind": 3,
       "type": "string",
       "value": "127.0.0.1"
     }, {
-      "ind": 3,
+      "ind": 4,
       "type": "number",
       "value": 2042
     }]
diff --git a/programs/nautilus/ntsave.json b/programs/nautilus/ntsave.json
new file mode 100644
index 0000000..3522a98
--- /dev/null
+++ b/programs/nautilus/ntsave.json
@@ -0,0 +1,22 @@
+{
+  "interpreterName": "cuttlefish",
+  "interpreterVersion": "v0.1",
+  "hunks": [{
+    "name": "comm/websocketserver",
+    "states": [{
+      "ind": 0,
+      "type": "string",
+      "value": "connected"
+    }, {
+      "ind": 1,
+      "type": "number",
+      "value": 2042
+    }]
+  }],
+  "links": [{
+    "outInd": 2,
+    "outputInd": 0,
+    "inInd": 1,
+    "inputInd": 0
+  }]
+}
diff --git a/programs/tptch.json b/programs/tptch.json
deleted file mode 100644
index 6db7ced..0000000
--- a/programs/tptch.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-  "interpreterName": "cuttlefish",
-  "interpreterVersion": "v0.1",
-  "hunks": [{
-    "name": "link",
-    "states": [{
-      "ind": 0,
-      "type": "string",
-      "value": "mgrMsgs (byteArray), numtype (number)"
-    }, {
-      "ind": 1,
-      "type": "string",
-      "value": "mgrMsgs (byteArray), numoot (number)"
-    }]
-  }, {
-    "name": "view",
-    "states": []
-  }, {
-    "name": "interface/number",
-    "states": [{
-      "ind": 0,
-      "type": "number",
-      "value": 12
-    }]
-  }],
-  "links": [{
-    "outInd": 2,
-    "outputInd": 1,
-    "inInd": 3,
-    "inputInd": 0
-  }, {
-    "outInd": 3,
-    "outputInd": 0,
-    "inInd": 2,
-    "inputInd": 1
-  }, {
-    "outInd": 4,
-    "outputInd": 0,
-    "inInd": 2,
-    "inputInd": 2
-  }]
-}
diff --git a/style.css b/style.css
index 76a5125..efe1d8a 100644
--- a/style.css
+++ b/style.css
@@ -215,17 +215,38 @@ textarea {
 }
 */
 
+.stateItem {
+	margin: 3px;
+}
+
 .stateItem input {
 	background-color: #1a1a1a;
 	color: #fcd17b;
 	font-size: 12px;
 	width: 70%;
 	margin-bottom: 2px;
+	margin-top: 4px;
 	padding: 2px;
 	border: 1px solid black;
 	border-radius: 3px;
 }
 
+.stateNumInput input {
+	float: right;
+	clear: both;
+	align: right;
+}
+
+.stateBooleanItem{
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+.stateBooleanItem:hover{
+	background-color: #1a1a1a;
+	cursor: pointer;
+}
+
 .dg.a {
 	margin-right: 0px;
 }
diff --git a/view/vdef.js b/view/vdef.js
index a9b8055..edc368a 100644
--- a/view/vdef.js
+++ b/view/vdef.js
@@ -261,7 +261,7 @@ function StateDefinition(stspec, ind, def, view, debug){
   // business,
   this.value = stspec.value
   // ok,
-  this.de = $('<div><p>' + this.name + " (" + this.type + ")" + '</p></div>').addClass('stateItem').get(0)
+  this.de = $('<div>' + this.name + " (" + this.type + ")" + '</div>').addClass('stateItem').get(0)
   this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}`
   this.newParentInd = () => {
     this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}`
@@ -292,7 +292,7 @@ function StateDefinition(stspec, ind, def, view, debug){
         }
         break // end string types
       case 'number':
-        let ninput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', this.value.toString()).css('width', '100px').get(0)
+        let ninput = $('<input>').addClass('stateNumInput').attr('type', 'text').attr('size', 24).attr('value', this.value.toString()).css('width', '100px').get(0)
         ninput.addEventListener('change', (evt) => {
           // ask for a change,
           view.requestStateChange(this, parseFloat(ninput.value))
@@ -312,6 +312,8 @@ function StateDefinition(stspec, ind, def, view, debug){
         break // end numnber type
       case 'boolean':
         let span = $('<span style="float:right;">' + this.value.toString() + '</span>').get(0)
+        $(this.de).addClass('stateBooleanItem')
+        this.de.append(span)
         this.de.addEventListener('click', (evt) => {
           // read the current 'state' (as written) and send the opposite
           let txt = $(span).text()
@@ -324,7 +326,7 @@ function StateDefinition(stspec, ind, def, view, debug){
         this.set = (value) => {
           if(typeof value === boolean){
             $(span).text(value.toString())
-            this.value = value 
+            this.value = value
           } else {
             throw new Error('bad type put into state dom')
           }
diff --git a/view/vmsg.js b/view/vmsg.js
index a45b635..da952e5 100644
--- a/view/vmsg.js
+++ b/view/vmsg.js
@@ -11,6 +11,7 @@ function MessageBox(View) {
     recipVersion: '',
     numHunksLeft: 0,
     numLinksLeft: 0,
+    isStateHappenning: false,
     setFromBrief: function(brief) {
       this.recipVer = brief.interpreterVersion
       this.recipName = brief.interpreterName
@@ -18,6 +19,14 @@ function MessageBox(View) {
       this.numLinksLeft = brief.numLinks
       this.postToDom()
     },
+    stateIsHappening: function() {
+      this.isStateHappenning = true
+      this.postToDom()
+    },
+    stateIsNotHappening: function() {
+      this.isStateHappenning = false
+      this.postToDom()
+    },
     decrementHunks: function() {
       this.numHunksLeft--
       this.postToDom()
@@ -36,7 +45,9 @@ function MessageBox(View) {
     },
     postToDom: function() {
       let str
-      if (this.numHunksLeft > 0 || this.numLinksLeft > 0) {
+      if(this.stateIsHappening){
+        str = `manager interpreter: ${this.recipName} ${this.recipVer} <br> awaiting state ...`
+      } else if (this.numHunksLeft > 0 || this.numLinksLeft > 0) {
         str = `manager interpreter: ${this.recipName} ${this.recipVer} <br> awaiting ${this.numHunksLeft} hunks and ${this.numLinksLeft} links`
       } else {
         str = `manager interpreter: ${this.recipName} ${this.recipVer} <br> all loaded OK`
diff --git a/view/vptch.js b/view/vptch.js
index fa45a19..e0b51af 100644
--- a/view/vptch.js
+++ b/view/vptch.js
@@ -14,8 +14,12 @@ function PatchSet(View, MsgBox) {
   // load from server should be assumed, just rip that *baybie*
   this.findPatches = (evt) => {
     // and then,
+    if (!(view.interpreterName)) {
+      msgbox.write('view on unknown interpeter, cannot save ... refresh view first')
+      return false
+    }
     $(evt.target).append(' > loading a list ...')
-    gg.recursivePathSearch('programs', '.json', true).then((list) => {
+    gg.recursivePathSearch('programs/' + view.interpreterName + '/', '.json', true).then((list) => {
       console.log('returns list', list)
       // title, and options
       view.changeContextTitle('available patches:')
@@ -33,26 +37,75 @@ function PatchSet(View, MsgBox) {
     })
   }
 
+  let patchStep = 0
   let unloadedHunks = []
+  let awaitStates = []
   let unloadedLinks = []
 
   let reqNextHunk = () => {
-    // from 0th position in array
-    msgbox.briefState.incrementHunks()
-    console.log('loading hunk from', unloadedHunks[0])
-    view.requestAddHunk(unloadedHunks[0].name, unloadedHunks[0].states)
+    // patchStep is the expected current place in an array of hunks, if
+    if (view.defs[patchStep] !== undefined) { // have one of these already,
+      if (view.defs[patchStep].name === unloadedHunks[0].name) {
+        msgbox.write(`found write-over for ${unloadedHunks[0].name}, checking for req change`)
+        // look at each state,
+        if (view.defs[patchStep].states.length < 1) {
+          unloadedHunks.shift()
+          patchStep ++
+          reqNextHunk() // recursive? pretty sure this is fn completion
+        } else {
+          for (let st in view.defs[patchStep].states) {
+            // might be a few of these, and there might be none
+            if (view.defs[patchStep].states[st].value !== unloadedHunks[0].states[st].value) {
+              view.requestStateChange(view.defs[patchStep].states[st], unloadedHunks[0].states[st].value)
+              // we have to track this ...
+              msgbox.briefState.stateIsHappening()
+              awaitState.push(view.defs[patchStep].states[st].name)
+            }
+          }
+        }
+      } else {
+        // an error, the in-place hunk doesn't exist here
+        // this is a hack for now ... we are assuming all contexts are starting from bootstrap,
+        // and that exactly that bootstrap is running already
+        msgbox.write('lost bootstrap, or existing program, while loading a patch')
+        console.error(`lost bootstrap, or existing program, while loading a patch: ${patchStep}, ulh, ${unloadedHunks[0].name}, defs, ${view.defs[patchStep].name}`);
+      }
+    } else {
+      // regular ops,
+      msgbox.briefState.incrementHunks()
+      console.log('loading hunk from', unloadedHunks[0])
+      view.requestAddHunk(unloadedHunks[0].name, unloadedHunks[0].states)
+    }
+  }
+
+  this.onStateChanged = (stateDef) => {
+    if(awaitStates.length < 1) return true
+    let indOf = awaitStates.indexOf(stateDef.name)
+    if(indOf === -1){
+      console.error(`misplaced state: ${stateDef.name}`)
+    } else {
+      awaitStates.splice(indOf, 1)
+      if(awaitStates.length < 0){
+        msgbox.briefState.stateIsNotHappening()
+        unloadedHunks.shift()
+        reqNextHunk()
+      }
+    }
   }
 
   // callbacks,
   this.onHunkLoaded = (def) => {
     // not interested,
     if (unloadedHunks.length < 1) return true
+    // ur loaded,
+    patchStep++
     // ok ...
     // find in list, rm
     console.log('checking', def, 'against', unloadedHunks[0])
     // check that we *are* receiving in order
     let checksOut = true
     if (def.name !== unloadedHunks[0].name) checksOut = false
+    // on a state update case, we might have different states ... for now, avoiding this problem
     for (let st in def.states) {
       if (def.states[st].value !== unloadedHunks[0].states[st].value) checksOut = false
     }
@@ -99,7 +152,7 @@ function PatchSet(View, MsgBox) {
 
   this.loadPatch = (name) => {
     msgbox.write(`ok, loading a patch from: ${name}`)
-    gg.getJson('/programs/' + name + '.json').then((patch) => {
+    gg.getJson('/programs/' + view.interpreterName + '/' + name + '.json').then((patch) => {
       console.log('the patch', patch)
       if (patch.interpreterName !== view.interpreterName) {
         msgbox.write(`WARN: loading patch built in a different interpreter... some hunks may not exist: patch for: "${patch.interpreterName}", but view is connected to "${view.interpreterName}"`)
@@ -121,19 +174,19 @@ function PatchSet(View, MsgBox) {
 
   this.saveCurrent = (evt, defs, debug) => {
     if (debug) console.log('saving ...')
-    // riperoni ok
+    // riperoni ok, save them all
     let patch = {
-      interpreterName: gg.interpreterName,
-      interpreterVersion: gg.interpreterVersion,
+      // cerntakt
+      interpreterName: view.interpreterName,
+      interpreterVersion: view.interpreterVersion,
       hunks: [],
       links: []
       // links added later
     }
     // the hunks,
     for (let df of defs) {
-      // exclude bootstrap hunks, for us for now this is just the first two
-      if (df.ind === 0 || df.ind === 1) continue
-      // otherwise
+      // we save them all,
+      // on load, compare to existing in same position
       console.log('writing prep for', df)
       // in order, u c
       let prep = {
@@ -141,7 +194,7 @@ function PatchSet(View, MsgBox) {
         states: []
       }
       // HERE: we save state just with the index / value ?
-      if(df.states !== undefined){
+      if (df.states !== undefined) {
         for (let st of df.states) {
           prep.states.push({
             ind: st.ind,
@@ -152,7 +205,7 @@ function PatchSet(View, MsgBox) {
       }
       // roll for links,
       // outputs
-      if(df.outputs !== undefined){
+      if (df.outputs !== undefined) {
         for (let otp in df.outputs) {
           for (let cn in df.outputs[otp].connections) {
             patch.links.push({
-- 
GitLab