From 5f036e38023d21e540ed202f50a89ae6f3d3aca6 Mon Sep 17 00:00:00 2001 From: Robin Guerrier Date: Tue, 23 Jul 2024 05:44:13 -0400 Subject: [PATCH 1/7] Add WIP mast and eyre channel neo additions --- pkg/arvo/app/neo.hoon | 40 +- pkg/arvo/neo/cod/std/src/imp/manx.hoon | 1 + .../neo/cod/std/src/imp/mast-diary-ui.hoon | 147 ++++ pkg/arvo/neo/cod/std/src/imp/mast.hoon | 739 ++++++++++++++++++ pkg/arvo/neo/cod/std/src/lib/mast.hoon | 19 + pkg/arvo/neo/cod/std/src/pro/json.hoon | 1 + pkg/arvo/neo/cod/std/src/pro/manx.hoon | 1 + pkg/arvo/neo/cod/std/src/pro/ui-event.hoon | 3 + pkg/arvo/sur/neo.hoon | 4 + 9 files changed, 951 insertions(+), 4 deletions(-) create mode 100644 pkg/arvo/neo/cod/std/src/imp/manx.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/mast.hoon create mode 100644 pkg/arvo/neo/cod/std/src/lib/mast.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/json.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/manx.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/ui-event.hoon diff --git a/pkg/arvo/app/neo.hoon b/pkg/arvo/app/neo.hoon index 0230f1331cf..b2a8fd78c03 100644 --- a/pkg/arvo/app/neo.hoon +++ b/pkg/arvo/app/neo.hoon @@ -257,6 +257,7 @@ %noun (on-noun q.vase) %neo-raw-poke (on-move (poke:harden !<(raw-poke:neo vase))) %handle-http-request (handle-http-request:sttp !<([@ta inbound-request:eyre] vase)) + %json (handle-eyre-chan-request:sttp !<(json vase)) == ++ on-noun |= non=* @@ -322,6 +323,7 @@ [%sync rest=*] (on-peer-sync (pave:neo rest.pole) stop) [%fetch rest=*] ?:(stop run (on-peer-fetch (pave:neo rest.pole))) [%http-response *] run + [%eyre-chan *] run == :: ++ on-peer-fetch @@ -2312,10 +2314,11 @@ ++ call |= [src=pith:neo dst=pith:neo =note:neo] ?> ?=(%poke -.note) :: XX: all shanes should be virtualised and hand deliver acks - ?+ p.pail.note ~|(bad-eyre-call/p.pail.note !!) - %ack run - %eyre-req (on-eyre-req !<(req:eyre:neo q.pail.note)) - %eyre-sign (on-eyre-sign src !<(sign:eyre:neo q.pail.note)) + ?+ p.pail.note ~|(bad-eyre-call/p.pail.note !!) + %ack run + %eyre-req (on-eyre-req !<(req:eyre:neo q.pail.note)) + %eyre-sign (on-eyre-sign src !<(sign:eyre:neo q.pail.note)) + %eyre-chan-gift (on-eyre-chan-gift !<(chan-gift:eyre:neo q.pail.note)) == +$ request-line $: [ext=(unit @ta) site=(list @t)] @@ -2348,6 +2351,13 @@ %data `http-response-data/!>(dat.gift) %done ~ == + :: + ++ on-eyre-chan-gift + |= [sub=path dat=json] + ^+ run + =/ cag=cage [%json !>(dat)] + (give %fact ~[sub] cag) + :: ++ match-binding =| test=(list @t) |= site=(list @t) @@ -2370,6 +2380,28 @@ =/ =card:neo [u.bin %poke eyre-task/!>(`task:eyre:neo`[eyre-id req])] =/ =move:neo [#/[p/our.bowl]/$/eyre card] (emit (do-move move)) + :: + ++ handle-eyre-chan-request + |= jon=json + ^+ run + =/ dst=(unit pith:neo) + ?. &(?=(^ jon) ?=(%o -.jon)) + ~ + =/ str=(unit json) (~(get by p.jon) 'path') + ?. &(?=(^ str) ?=(^ u.str) ?=(%s -.u.str)) + ~ + (rush p.u.str stap) + ?~ dst ~|(eyre-channel-req-missing-pith/jon !!) + =. u.dst [p/our.bowl u.dst] + =. jon + ?. &(?=(^ jon) ?=(%o -.jon)) + ~ + =/ dat=(unit json) (~(get by p.jon) 'data') + ?~ dat ~ + u.dat + =/ =card:neo [u.dst %poke eyre-chan-task/!>(`chan-task:eyre:neo`jon)] + =/ =move:neo [#/[p/our.bowl]/$/eyre card] + (emit (do-move move)) -- ++ cttp diff --git a/pkg/arvo/neo/cod/std/src/imp/manx.hoon b/pkg/arvo/neo/cod/std/src/imp/manx.hoon new file mode 100644 index 00000000000..54bcf304652 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/manx.hoon @@ -0,0 +1 @@ +~ diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon new file mode 100644 index 00000000000..a30ef07bf63 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon @@ -0,0 +1,147 @@ +/@ ui-event +/@ txt +/@ diary-diff +^- kook:neo +=< +|% +++ state pro/%manx +++ poke (sy %ui-event %rely %gift ~) +++ kids + *kids:neo + :: ^- kids:neo + :: :+ ~ %y + :: %- my + :: :~ [[&/%selection |] pro/%sig ~] + :: == +++ deps + ^- deps:neo + %- my + :~ :^ %diary & [pro/%diary (sy %diary-diff ~)] + :+ ~ %y + %- my + :~ [[|/%da |] only/%txt ~] + == + == +++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo =pail:neo] + :: + ++ init + |= pal=(unit pail:neo) + ^- (quip card:neo pail:neo) + =/ diary-entries + (get-diary-entries deps.bowl) + :- ~ + manx/!>((render [diary-entries ~])) + :: + ++ poke + |= [sud=stud:neo vaz=vase] + ^- (quip card:neo pail:neo) + ?+ sud ~|(bad-stud/sud !!) + :: + %ui-event + =/ eve !<(ui-event vaz) + ?+ path.eve ~|(missing-event-handler-for/path.eve !!) + :: + [%click %submit ~] + =/ dat=(unit @t) + (~(get by data.eve) '/diary-input/value') + ?~ dat + ~|(%diary-input-fail !!) + =/ dif=diary-diff + [%put-entry now.bowl u.dat] + :_ pail + :~ [#/[p/our.bowl]/mast-diary-demo %poke diary-diff/!>(dif)] + == + :: + == + :: + %rely + =/ diary-entries + (get-diary-entries deps.bowl) + `manx/!>((render [diary-entries ~])) + :: + == + :: + -- +-- +:: +|% +:: +++ render + |_ $: diary-entries=(list [date=@da =txt]) + selection=(unit @da) + == + ++ $ + ^- manx + ;html + ;head + ;meta(charset "utf-8"); + == + ;body + =style "margin: 0; width: 100%; display: grid; place-items: center;" + ;main + ;h1: Diary + ;+ diary-form + ;+ diary-items + == + == + == + :: + ++ diary-form + ^- manx + ;div + ;textarea(id "diary-input", style "height: 10rem; width: 25rem; margin-block: 1rem;"); + ;button + =event "/click/submit" + =return "/diary-input/value" + ;+ ;/ "Enter" + == + == + :: + ++ diary-items + ^- manx + ;div + ;* %+ turn diary-entries + |= [date=@da =txt] + ;div + ;p: {(pretty-date date)} + ;p: {(trip txt)} + == + == + :: + -- +:: :: :: :: :: :: :: :: :: :: +++ get-diary-entries + |= sam=(map term (pair pith:neo lore:neo)) + ^- (list [date=@da =txt]) + =/ dat=(unit (pair pith:neo lore:neo)) + (~(get by sam) %diary) + ?~ dat + ~|(%no-diary !!) + =/ pod=(list [date=@da =txt]) + %+ turn ~(tap by kid.q.u.dat) + |= (pair iota:neo (axal:neo idea:neo)) + ?> &(?=(^ p) ?=(%da -.p)) + ?> ?=(^ fil.q) + [+.p !<(txt q.pail.u.fil.q)] + %+ sort pod + |= (pair [date=@da =txt] [date=@da =txt]) + (gth date.p date.q) +:: +++ get-selection + |= sam=(axal:neo idea:neo) + ^- (unit @da) + =/ dat=(unit (axal:neo idea:neo)) + (~(get by kid.sam) %selection) + ?~ dat ~ + ?~ fil.u.dat ~ + [~ !<(@da q.pail.u.fil.u.dat)] +:: +++ pretty-date :: from diary-htmx + |= date=@da + ^- tape + =/ d (yore date) + "{(y-co:co y:d)}-{(y-co:co m:d)}-{(y-co:co d:t:d)}" +:: +-- diff --git a/pkg/arvo/neo/cod/std/src/imp/mast.hoon b/pkg/arvo/neo/cod/std/src/imp/mast.hoon new file mode 100644 index 00000000000..96836478379 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/mast.hoon @@ -0,0 +1,739 @@ +/- mast +=< +^- kook:neo +|% +++ state [%pro %sig] +++ poke (sy %eyre-task %eyre-chan-task %gift ~) +++ kids + :+ ~ %y + %- my + :~ [[|/%p &] [%pro %manx] (sy %sig ~)] :: fix + [[&/%aft |/%p |] [%pro %manx] ~] + == +++ deps *deps:neo +++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo =pail:neo] + :: + ++ init + |= pal=(unit pail:neo) + ^- (quip card:neo pail:neo) + ?> ?=(^ pal) + =+ !<(prow:mast q.u.pal) + =/ =pith:neo #/[p/our.bowl]/$/eyre + =/ =binding:eyre [~ url] + =/ =req:eyre:neo [%connect binding here.bowl] + =/ =rig:mast + :* urth + made + /eyre-chan/mast/(scot %ud ^-(@ud (mug here.bowl))) + ~ + == + :_ sig/!>(rig) + :~ [pith %poke eyre-req/!>(req)] + [(snoc here.bowl p/ship.src.bowl) %make made.rig] :: temporary + == + :: + ++ poke + |= [sud=stud:neo vaz=vase] + ^- (quip card:neo pail:neo) + ~& mast-poke/sud + =+ !<(=rig:mast q.pail) + ?+ sud ~|(bad-stud/sud !!) + :: + %eyre-task + =+ !<([rid=@ta req=inbound-request:eyre] vaz) + ?. authenticated.req + :_ pail + (~(make-auth-redirect res bowl) rid) + ?+ method.request.req + [(~(make-400 res bowl) rid) pail] + :: + %'GET' + ?. |(urth:rig =(our.bowl ship.src.bowl)) + :_ pail + (~(make-403 res bowl) rid) + =/ jig=(unit idea:neo) + =/ g (~(get by kid.kids.bowl) p/ship.src.bowl) + ?~ g ~ + fil.u.g + ?~ jig + =. open-http.rig + (~(put by open-http.rig) ship.src.bowl rid) + ~& open-http/rid + :_ sig/!>(rig) + :~ [(snoc here.bowl p/ship.src.bowl) %make made.rig] + == + =/ sail=manx + (hoist !<(manx q.pail.u.jig)) + :_ pail + (~(gale res bowl) rid sail rig) + :: + == + :: + %gift + =/ rum=(list [=pith:neo =loot:neo]) + ~(tap of:neo !<(gift:neo vaz)) + =^ cards rig + =| cards=(list card:neo) + |- ^- (quip card:neo rig:mast) + ?~ rum + [cards rig] + ?: ?=([%aft *] pith.i.rum) + $(rum t.rum) + =/ jig=(unit idea:neo) + (~(get of:neo kids.bowl) pith.i.rum) + ?~ jig + $(rum t.rum) + =/ sail=manx + (hoist !<(manx q.pail.u.jig)) + =/ boat=@p + =/ p (rear pith.i.rum) + ?> &(?=(^ p) ?=(%p -.p)) + +.p + =/ rid=(unit @ta) + (~(get by open-http.rig) boat) + ?^ rid + ~& close-http/u.rid + %= $ + open-http.rig (~(del by open-http.rig) boat) + rum t.rum + cards + %+ weld + cards + %+ snoc + (~(gale res bowl) u.rid sail rig) + [(welp here.bowl #/aft/[p/boat]) %make [%manx [~ manx/!>(sail)] ~]] + == + ?+ mode.loot.i.rum + $(rum t.rum) + :: + %add + ~& >>> missing-eyre-id/pith.i.rum + $(rum t.rum) + :: + %dif + =/ aft=manx + =/ ide=(unit idea:neo) + (~(get of:neo kids.bowl) #/aft/[p/boat]) + ?~ ide + ~& >>> missing-aft-sail/[here.bowl aft/boat] + (hoist [[%html ~] [[%head ~] ~] [[%body ~] ~] ~]) + !<(manx q.pail.u.ide) + %= $ + rum t.rum + cards + %+ weld + cards + %+ snoc + (~(gust res bowl) rig aft sail) + [(welp here.bowl #/aft/[p/boat]) %make [%manx [~ manx/!>(sail)] ~]] + == + :: + == + [cards sig/!>(rig)] + :: + %eyre-chan-task + =+ !<(jon=json vaz) + =/ dat + (parse-channel-data jon) + :_ pail + :~ [(snoc here.bowl p/ship.src.bowl) %poke ui-event/!>(dat)] + == + :: + == + :: + -- +-- +:: +|% +:: +++ res + |_ =bowl:neo + :: + ++ gale + |= [rid=@ta sail=manx =rig:mast] + ^- (list card:neo) + ?> ?=(^ c.sail) + %^ make-direct-http-cards + rid + [200 ['Content-Type' 'text/html'] ~] + :- ~ + ^- octs + %- as-octt:mimes:html + %- en-xml:html + %_ sail + a.g + ^- mart + :* [%pith <^-(path ?>(?=(^ here.bowl) (pout:neo t.here.bowl)))>] :: destination path to shrub from neo + [%app "neo"] + [%path <^-(path (snoc base-sub.rig (scot %p ship.src.bowl)))>] :: sub path, shrub to eyre + [%ship +:(scow %p our.bowl)] + a.g.sail + == + c.i.c + (marl [script-node c.i.c.sail]) + == + :: + ++ gust + |= [=rig:mast old=manx new=manx] + ^- (list card:neo) + ?~ c.old !! + ?~ c.new !! + ?~ t.c.old !! :: fix + ?~ t.c.new !! + ?~ a.g.new !! + :_ ~ + :- #/[p/our.bowl]/$/eyre + :- %poke + :- %eyre-chan-gift + !> + ^- chan-gift:eyre:neo + :- (snoc base-sub.rig (scot %p ship.src.bowl)) + %- tape:enjs:format + %- en-xml:html + :: ~& >> gust/(algo c.i.t.c.old c.i.t.c.new) + ^- manx + ;g + =url v.i.a.g.new + ;* %+ algo + c.i.t.c.old + c.i.t.c.new + == + :: + ++ make-400 + |= rid=@ta + ^- (list card:neo) + %^ make-direct-http-cards + rid + [400 ~] + ~ + :: + ++ make-403 + |= rid=@ta + ^- (list card:neo) + %^ make-direct-http-cards + rid + [403 ~] + ~ + :: + ++ make-auth-redirect + |= rid=@ta + ^- (list card:neo) + %^ make-direct-http-cards + rid + [307 ['Location' '/~/login?redirect='] ~] + ~ + :: + ++ make-direct-http-cards + |= [rid=@ta hed=response-header:http dat=(unit octs)] + ^- (list card:neo) + =/ eyre=pith:neo #/[p/our.bowl]/$/eyre + =/ head=sign:eyre:neo [rid %head hed] + =/ data=sign:eyre:neo [rid %data dat] + =/ done=sign:eyre:neo [rid %done ~] + :~ [eyre %poke eyre-sign/!>(head)] + [eyre %poke eyre-sign/!>(data)] + [eyre %poke eyre-sign/!>(done)] + == + :: + -- +:: +++ parse-channel-data + |= jon=json + ^- crow:mast + ((ot ~[path+pa data+(om so)]):dejs:format jon) +:: +++ algo + |= [old=marl new=marl] + ^- marl + =/ i=@ud 0 + =/ acc=marl ~ + |- + ?~ new + ?. =(~ old) + ?: =(%skip -.-.-.old) + $(old +.old) + :_ acc + :_ ~ + :- %d + =/ c=@ud 0 + |- ^- mart + ?~ old + ~ + :- :- (crip (weld "d" )) + (getv a.g.i.old %key) + $(old t.old, c +(c)) + acc + ?: &(?=(^ old) =(%skip -.-.-.old)) + $(old t.old) + ?: =(%m n.g.i.new) + $(new t.new, i +(i), acc (snoc acc i.new)) + =/ j=@ud 0 + =/ jold=marl old + =/ nkey=[n=mane k=tape] [n.g.i.new (getv a.g.i.new %key)] + |- + ?~ new + !! + ?~ jold + %= ^$ + new t.new + i +(i) + acc %+ snoc acc + ;n(id ) + ;+ i.new + == + == + ?~ old + !! + ?: =(%skip n.g.i.jold) + $(jold t.jold, j +(j)) + ?: .=(nkey [n.g.i.jold (getv a.g.i.jold %key)]) + ?. =(0 j) + =/ n=@ud 0 + =/ nnew=marl new + =/ okey=[n=mane k=tape] [n.g.i.old (getv a.g.i.old %key)] + |- + ?~ nnew + ^^$(old (snoc t.old i.old)) + ?: =(%m n.g.i.nnew) + $(nnew t.nnew, n +(n)) + =/ nnky=[n=mane k=tape] [n.g.i.nnew (getv a.g.i.nnew %key)] + ?. .=(okey nnky) + $(nnew t.nnew, n +(n)) + ?: (gte n j) + =/ aupd=mart (upda a.g.i.old a.g.i.nnew) + ?~ aupd + %= ^^$ + old c.i.old + new c.i.nnew + i 0 + acc + %= ^^$ + old t.old + new %^ newm new n + ;m(id <(add n i)>, key k.nnky); + == + == + %= ^^$ + old c.i.old + new c.i.nnew + i 0 + acc + %= ^^$ + old t.old + new %^ newm new n + ;m(id <(add n i)>, key k.nnky); + acc :_ acc + [[%c [[%key k.nnky] aupd]] ~] + == + == + =/ aupd=mart (upda a.g.i.jold a.g.i.new) + ?~ aupd + %= ^^$ + old c.i.jold + new c.i.new + i 0 + acc + %= ^^$ + old (newm old j ;skip;) + new t.new + i +(i) + acc %+ snoc acc + ;m(id , key k.nkey); + == + == + %= ^^$ + old c.i.jold + new c.i.new + i 0 + acc + %= ^^$ + old (newm old j ;skip;) + new t.new + i +(i) + acc :- [[%c [[%key k.nkey] aupd]] ~] + %+ snoc + acc + ;m(id , key k.nkey); + == + == + ?: =("text" (getv a.g.i.new %mast)) + ?: =(+.-.+.-.-.+.-.old +.-.+.-.-.+.-.new) + ^$(old t.old, new t.new, i +(i)) + %= ^$ + old t.old + new t.new + i +(i) + acc [i.new acc] + == + =/ aupd=mart (upda a.g.i.old a.g.i.new) + ?~ aupd + %= ^$ + old c.i.old + new c.i.new + i 0 + acc ^$(old t.old, new t.new, i +(i)) + == + %= ^$ + old c.i.old + new c.i.new + i 0 + acc + %= ^$ + old t.old + new t.new + i +(i) + acc :_ acc + [[%c [[%key k.nkey] aupd]] ~] + == + == + $(jold t.jold, j +(j)) +:: +++ hoist + |= root=manx + |^ ^- manx + (tanx root "0" "~") + ++ tanx + |= [m=manx key=tape pkey=tape] + =/ fkey=tape (getv a.g.m %key) + =/ nkey=tape ?~(fkey key fkey) + ?: =(%$ n.g.m) + ;span + =mast "text" + =key nkey + =pkey pkey + ;+ m + == + =: a.g.m %- mart + ?~ fkey + [[%key nkey] [[%pkey pkey] a.g.m]] + [[%pkey pkey] a.g.m] + c.m (tarl c.m nkey) + == + m + ++ tarl + |= [m=marl key=tape] + =/ i=@ud 0 + |- ^- marl + ?~ m + ~ + :- %^ tanx + (manx i.m) + (weld (scow %ud i) (weld "-" key)) + key + $(m t.m, i +(i)) + -- +:: +++ getv + |= [m=mart tag=@tas] + ^- tape + ?~ m + ~ + ?: =(n.i.m tag) + v.i.m + $(m t.m) +:: +++ upda + |= [om=mart nm=mart] + =/ acc=mart ~ + |- ^- mart + ?~ nm + ?~ om + acc + :_ acc + :- %rem + =/ omom=mart om + |- + ?~ omom + ~ + =/ nom=tape +: + |- + ?~ nom + [' ' ^$(omom t.omom)] + [i.nom $(nom t.nom)] + =/ i=@ud 0 + =/ com=mart om + |- + ?~ nm + !! + ?~ com + ^$(nm t.nm, acc [i.nm acc]) + ?~ om + !! + ?: =(n.i.com n.i.nm) + ?: =(v.i.com v.i.nm) + ^$(om (oust [i 1] (mart om)), nm t.nm) + %= ^$ + om (oust [i 1] (mart om)) + nm t.nm + acc [i.nm acc] + == + $(com t.com, i +(i)) +:: +++ newm + |= [ml=marl i=@ud mx=manx] + =/ j=@ud 0 + |- ^- marl + ?~ ml + ~ + :- ?: =(i j) + mx + i.ml + $(ml t.ml, j +(j)) +:: +++ paru + |= turl=tape + ^- path + =/ tacc=tape ~ + =/ pacc=path ~ + |- + ?~ turl + ?~ tacc + pacc + (snoc pacc (crip tacc)) + ?: =('/' i.turl) + ?~ tacc + $(turl t.turl) + %= $ + turl t.turl + tacc ~ + pacc (snoc pacc (crip tacc)) + == + $(turl t.turl, tacc (snoc tacc i.turl)) +:: +++ script-node + ^- manx + ;script + ;+ ;/ script + == +:: +++ script + ^~ + %- trip + ''' + let pith; + let ship; + let app; + let displayUpdatePath; + let channelMessageId = 0; + let eventSource; + const channelId = `${Date.now()}${Math.floor(Math.random() * 100)}`; + const channelPath = `${window.location.origin}/~/channel/${channelId}`; + addEventListener('DOMContentLoaded', async () => { + pith = document.documentElement.getAttribute('pith'); + ship = document.documentElement.getAttribute('ship'); + app = document.documentElement.getAttribute('app'); + displayUpdatePath = document.documentElement.getAttribute('path'); + await connectToShip(); + let eventElements = document.querySelectorAll('[event]'); + eventElements.forEach(el => setEventListeners(el)); + }); + function setEventListeners(el) { + const eventTags = el.getAttribute('event'); + const returnTags = el.getAttribute('return'); + eventTags.split(/\s+/).forEach(eventStr => { + const eventType = eventStr.split('/', 2)[1]; + el[`on${eventType}`] = (e) => pokeShip(e, eventStr, returnTags); + }); + }; + async function connectToShip() { + try { + const storageKey = `${ship}${app}${displayUpdatePath}`; + let storedId = localStorage.getItem(storageKey); + localStorage.setItem(storageKey, channelId); + if (storedId) { + const delPath = `${window.location.origin}/~/channel/${storedId}`; + await fetch(delPath, { + method: 'PUT', + body: JSON.stringify([{ + id: channelMessageId, + action: 'delete' + }]) + }); + }; + const body = JSON.stringify(makeSubscribeBody()); + await fetch(channelPath, { + method: 'PUT', + body + }); + eventSource = new EventSource(channelPath); + eventSource.addEventListener('message', handleChannelStream); + } catch (error) { + console.error(error); + }; + }; + function pokeShip(event, tagString, dataString) { + try { + let data = {}; + if (dataString) { + const dataToReturn = dataString.split(/\s+/); + dataToReturn.forEach(dataTag => { + let splitDataTag = dataTag.split('/'); + if (splitDataTag[0] === '') splitDataTag.shift(); + const kind = splitDataTag[0]; + const key = splitDataTag.pop(); + if (kind === 'event') { + if (!(key in event)) { + console.error(`Property: ${key} does not exist on the event object`); + return; + }; + data[dataTag] = String(event[key]); + } else if (kind === 'target') { + if (!(key in event.currentTarget)) { + console.error(`Property: ${key} does not exist on the target object`); + return; + }; + data[dataTag] = String(event.currentTarget[key]); + } else { + const elementId = splitDataTag.join('/'); + const linkedEl = document.getElementById(elementId); + if (!linkedEl) { + console.error(`No element found for id: ${kind}`); + return; + }; + if (!(key in linkedEl)) { + console.error(`Property: ${key} does not exist on the object with id: ${elementId}`); + return; + }; + data[dataTag] = String(linkedEl[key]); + }; + }); + }; + fetch(channelPath, { + method: 'PUT', + body: JSON.stringify(makePokeBody({ + path: tagString, + data + })) + }); + } catch (error) { + console.error(error); + }; + }; + function handleChannelStream(event) { + try { + const streamResponse = JSON.parse(event.data); + if (streamResponse.response !== 'diff') return; + fetch(channelPath, { + method: 'PUT', + body: JSON.stringify(makeAck(streamResponse.id)) + }); + const htmlData = streamResponse.json; + if (!htmlData) return; + let container = document.createElement('template'); + container.innerHTML = htmlData; + if (container.content.firstElementChild.childNodes.length === 0) return; + // const navUrl = container.content.firstElementChild.getAttribute('url'); + // if (navUrl && (navUrl !== window.location.pathname)) { + // history.pushState({}, '', navUrl); + // }; + while (container.content.firstElementChild.children.length > 0) { + let gustChild = container.content.firstElementChild.firstElementChild; + if (gustChild.tagName === 'D') { + for (const att of gustChild.attributes) { + const dkey = att.value; + document.querySelector(`[key="${dkey}"]`).remove(); + }; + gustChild.remove(); + } else if (gustChild.tagName === 'N') { + const nodeKey = gustChild.firstElementChild.getAttribute('key'); + const parentKey = gustChild.firstElementChild.getAttribute('pkey'); + const appendIndex = gustChild.id; + let domParent = document.querySelector(`[key="${parentKey}"]`); + domParent.insertBefore(gustChild.firstElementChild, domParent.children[appendIndex]); + let appendedChild = domParent.querySelector(`[key="${nodeKey}"]`); + if (appendedChild.getAttribute('event')) { + setEventListeners(appendedChild); + }; + if (appendedChild.childElementCount > 0) { + let needingListeners = appendedChild.querySelectorAll('[event]'); + needingListeners.forEach(child => setEventListeners(child)); + }; + appendedChild = appendedChild.nextElementSibling; + gustChild.remove(); + } else if (gustChild.tagName === 'M') { + const nodeKey = gustChild.getAttribute('key'); + const nodeIndex = gustChild.id; + let existentNode = document.querySelector(`[key="${nodeKey}"]`); + let childAtIndex = existentNode.parentElement.children[nodeIndex]; + if (existentNode.nextElementSibling + && (existentNode.nextElementSibling.getAttribute('key') + === childAtIndex.getAttribute('key'))) { + existentNode.parentElement.insertBefore(existentNode, childAtIndex.nextElementSibling); + } else { + existentNode.parentElement.insertBefore(existentNode, childAtIndex); + }; + gustChild.remove(); + } else if (gustChild.tagName === 'C') { + const nodeKey = gustChild.getAttribute('key'); + const attToRem = gustChild.getAttribute('rem')?.slice(0, -1).split(' ') ?? []; + let existentNode = document.querySelector(`[key="${nodeKey}"]`); + attToRem.forEach(att => { + if (att === 'event') { + const eventType = existentNode.getAttribute('event').split('/', 2)[1]; + existentNode[`on${eventType}`] = null; + }; + existentNode.removeAttribute(att); + }); + gustChild.removeAttribute('key'); + gustChild.removeAttribute('rem'); + for (const att of gustChild.attributes) { + existentNode.setAttribute(att.name, att.value); + if (att.name === 'event') { + const eventType = existentNode.getAttribute('event').split('/', 2)[1]; + existentNode[`on${eventType}`] = null; + setEventListeners(existentNode); + }; + }; + gustChild.remove(); + } else { + const nodeKey = gustChild.getAttribute('key'); + let existentNode = document.querySelector(`[key="${nodeKey}"]`); + existentNode.replaceWith(gustChild); + let replacedNode = document.querySelector(`[key="${nodeKey}"]`); + if (replacedNode.getAttribute('event')) { + setEventListeners(replacedNode); + }; + if (replacedNode.childElementCount > 0) { + let needingListeners = replacedNode.querySelectorAll('[event]'); + needingListeners.forEach(child => setEventListeners(child)); + }; + }; + }; + } catch (error) { + console.error(error); + }; + }; + function makeSubscribeBody() { + channelMessageId++; + return [{ + id: channelMessageId, + action: 'subscribe', + ship: ship, + app: app, + path: displayUpdatePath + }]; + }; + function makePokeBody(jsonData) { + channelMessageId++; + return [{ + id: channelMessageId, + action: 'poke', + ship: ship, + app: app, + mark: 'json', + json: { path: pith, data: jsonData } + }]; + }; + function makeAck(eventId) { + channelMessageId++; + return [{ + id: channelMessageId, + action: 'ack', + "event-id": eventId + }]; + }; + ''' +:: +-- diff --git a/pkg/arvo/neo/cod/std/src/lib/mast.hoon b/pkg/arvo/neo/cod/std/src/lib/mast.hoon new file mode 100644 index 00000000000..3708b6bc718 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/lib/mast.hoon @@ -0,0 +1,19 @@ +|% +:: ++$ crow :: :: :: mast event poke data + [=path data=(map @t @t)] +:: ++$ prow :: :: :: mast init data + $: urth=? :: > are you serving to earth? + url=path :: > the base url that mast will bind with eyre + =made:neo :: > the +$made mast uses to spawn your ui shrubs + == +:: ++$ rig :: :: :: mast state + $: urth=? + =made:neo + base-sub=path + open-http=(map @p @ta) :: eyre ids of http requests pending session creation + == +:: +-- diff --git a/pkg/arvo/neo/cod/std/src/pro/json.hoon b/pkg/arvo/neo/cod/std/src/pro/json.hoon new file mode 100644 index 00000000000..3c840093b75 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/json.hoon @@ -0,0 +1 @@ +json diff --git a/pkg/arvo/neo/cod/std/src/pro/manx.hoon b/pkg/arvo/neo/cod/std/src/pro/manx.hoon new file mode 100644 index 00000000000..387ae5ca69e --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/manx.hoon @@ -0,0 +1 @@ +manx diff --git a/pkg/arvo/neo/cod/std/src/pro/ui-event.hoon b/pkg/arvo/neo/cod/std/src/pro/ui-event.hoon new file mode 100644 index 00000000000..a23f07ffdd2 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/ui-event.hoon @@ -0,0 +1,3 @@ +$: =path + data=(map @t @t) +== diff --git a/pkg/arvo/sur/neo.hoon b/pkg/arvo/sur/neo.hoon index 5ae2dfb535b..9c7b16a2f60 100644 --- a/pkg/arvo/sur/neo.hoon +++ b/pkg/arvo/sur/neo.hoon @@ -1287,6 +1287,10 @@ == +$ task (pair @ta inbound-request:^eyre) + +$ chan-task + json + +$ chan-gift + [sub=path dat=json] -- :: +clay: Filesystem overlay :: From 869fcdc52bbcb6d7d8449e6e6e6509a95dbad971 Mon Sep 17 00:00:00 2001 From: Robin Guerrier Date: Thu, 25 Jul 2024 02:38:25 -0400 Subject: [PATCH 2/7] Split mast JS into separate file. Parse actual pith in handle-eyre-chan-request. Fix double rendering on first gust update. Expand diary example. --- pkg/arvo/app/neo.hoon | 13 +- pkg/arvo/neo/cod/std/src/fil/mast-js.js | 222 +++++++++++ .../neo/cod/std/src/imp/mast-diary-ui.hoon | 21 +- pkg/arvo/neo/cod/std/src/imp/mast.hoon | 354 +++--------------- 4 files changed, 308 insertions(+), 302 deletions(-) create mode 100644 pkg/arvo/neo/cod/std/src/fil/mast-js.js diff --git a/pkg/arvo/app/neo.hoon b/pkg/arvo/app/neo.hoon index b2a8fd78c03..7204abd7ce6 100644 --- a/pkg/arvo/app/neo.hoon +++ b/pkg/arvo/app/neo.hoon @@ -2387,12 +2387,19 @@ =/ dst=(unit pith:neo) ?. &(?=(^ jon) ?=(%o -.jon)) ~ - =/ str=(unit json) (~(get by p.jon) 'path') + =/ str=(unit json) (~(get by p.jon) 'pith') ?. &(?=(^ str) ?=(^ u.str) ?=(%s -.u.str)) ~ - (rush p.u.str stap) + :- ~ + %+ turn (stab p.u.str) + |= i=@ta + %- iota + =+ v=(slay i) + ?> &(?=(^ v) ?=(%$ -.u.v)) + ?: =(~.tas p.p.u.v) + q.p.u.v + p.u.v ?~ dst ~|(eyre-channel-req-missing-pith/jon !!) - =. u.dst [p/our.bowl u.dst] =. jon ?. &(?=(^ jon) ?=(%o -.jon)) ~ diff --git a/pkg/arvo/neo/cod/std/src/fil/mast-js.js b/pkg/arvo/neo/cod/std/src/fil/mast-js.js new file mode 100644 index 00000000000..d5951b6e64a --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/fil/mast-js.js @@ -0,0 +1,222 @@ +let pith; +let path; +let ship; +let app; +let channelMessageId = 0; +let eventSource; +const channelId = `${Date.now()}${Math.floor(Math.random() * 100)}`; +const channelPath = `${window.location.origin}/~/channel/${channelId}`; +addEventListener('DOMContentLoaded', async () => { + pith = document.documentElement.getAttribute('pith'); + path = document.documentElement.getAttribute('path'); + ship = document.documentElement.getAttribute('ship'); + app = document.documentElement.getAttribute('app'); + await connectToShip(); + let eventElements = document.querySelectorAll('[event]'); + eventElements.forEach(el => setEventListeners(el)); +}); +function setEventListeners(el) { + const eventTags = el.getAttribute('event'); + const returnTags = el.getAttribute('return'); + eventTags.split(/\s+/).forEach(eventStr => { + const eventType = eventStr.split('/', 2)[1]; + el[`on${eventType}`] = (e) => pokeShip(e, eventStr, returnTags); + }); +}; +async function connectToShip() { + try { + const storageKey = `${ship}${app}${path}`; + let storedId = localStorage.getItem(storageKey); + localStorage.setItem(storageKey, channelId); + if (storedId) { + const delPath = `${window.location.origin}/~/channel/${storedId}`; + await fetch(delPath, { + method: 'PUT', + body: JSON.stringify([{ + id: channelMessageId, + action: 'delete' + }]) + }); + }; + const body = JSON.stringify(makeSubscribeBody()); + await fetch(channelPath, { + method: 'PUT', + body + }); + eventSource = new EventSource(channelPath); + eventSource.addEventListener('message', handleChannelStream); + } catch (error) { + console.error(error); + }; +}; +function pokeShip(event, tagString, dataString) { + try { + let data = {}; + if (dataString) { + const dataToReturn = dataString.split(/\s+/); + dataToReturn.forEach(dataTag => { + let splitDataTag = dataTag.split('/'); + if (splitDataTag[0] === '') splitDataTag.shift(); + const kind = splitDataTag[0]; + const key = splitDataTag.pop(); + if (kind === 'event') { + if (!(key in event)) { + console.error(`Property: ${key} does not exist on the event object`); + return; + }; + data[dataTag] = String(event[key]); + } else if (kind === 'target') { + if (!(key in event.currentTarget)) { + console.error(`Property: ${key} does not exist on the target object`); + return; + }; + data[dataTag] = String(event.currentTarget[key]); + } else { + const elementId = splitDataTag.join('/'); + const linkedEl = document.getElementById(elementId); + if (!linkedEl) { + console.error(`No element found for id: ${kind}`); + return; + }; + if (!(key in linkedEl)) { + console.error(`Property: ${key} does not exist on the object with id: ${elementId}`); + return; + }; + data[dataTag] = String(linkedEl[key]); + }; + }); + }; + fetch(channelPath, { + method: 'PUT', + body: JSON.stringify(makePokeBody({ + path: tagString, + data + })) + }); + } catch (error) { + console.error(error); + }; +}; +function handleChannelStream(event) { + try { + const streamResponse = JSON.parse(event.data); + if (streamResponse.response !== 'diff') return; + fetch(channelPath, { + method: 'PUT', + body: JSON.stringify(makeAck(streamResponse.id)) + }); + const htmlData = streamResponse.json; + if (!htmlData) return; + let container = document.createElement('template'); + container.innerHTML = htmlData; + if (container.content.firstElementChild.childNodes.length === 0) return; + // const navUrl = container.content.firstElementChild.getAttribute('url'); + // if (navUrl && (navUrl !== window.location.pathname)) { + // history.pushState({}, '', navUrl); + // }; + while (container.content.firstElementChild.children.length > 0) { + let gustChild = container.content.firstElementChild.firstElementChild; + if (gustChild.tagName === 'D') { + for (const att of gustChild.attributes) { + const dkey = att.value; + document.querySelector(`[key="${dkey}"]`).remove(); + }; + gustChild.remove(); + } else if (gustChild.tagName === 'N') { + const nodeKey = gustChild.firstElementChild.getAttribute('key'); + const parentKey = gustChild.firstElementChild.getAttribute('pkey'); + const appendIndex = gustChild.id; + let domParent = document.querySelector(`[key="${parentKey}"]`); + domParent.insertBefore(gustChild.firstElementChild, domParent.children[appendIndex]); + let appendedChild = domParent.querySelector(`[key="${nodeKey}"]`); + if (appendedChild.getAttribute('event')) { + setEventListeners(appendedChild); + }; + if (appendedChild.childElementCount > 0) { + let needingListeners = appendedChild.querySelectorAll('[event]'); + needingListeners.forEach(child => setEventListeners(child)); + }; + appendedChild = appendedChild.nextElementSibling; + gustChild.remove(); + } else if (gustChild.tagName === 'M') { + const nodeKey = gustChild.getAttribute('key'); + const nodeIndex = gustChild.id; + let existentNode = document.querySelector(`[key="${nodeKey}"]`); + let childAtIndex = existentNode.parentElement.children[nodeIndex]; + if (existentNode.nextElementSibling + && (existentNode.nextElementSibling.getAttribute('key') + === childAtIndex.getAttribute('key'))) { + existentNode.parentElement.insertBefore(existentNode, childAtIndex.nextElementSibling); + } else { + existentNode.parentElement.insertBefore(existentNode, childAtIndex); + }; + gustChild.remove(); + } else if (gustChild.tagName === 'C') { + const nodeKey = gustChild.getAttribute('key'); + const attToRem = gustChild.getAttribute('rem')?.slice(0, -1).split(' ') ?? []; + let existentNode = document.querySelector(`[key="${nodeKey}"]`); + attToRem.forEach(att => { + if (att === 'event') { + const eventType = existentNode.getAttribute('event').split('/', 2)[1]; + existentNode[`on${eventType}`] = null; + }; + existentNode.removeAttribute(att); + }); + gustChild.removeAttribute('key'); + gustChild.removeAttribute('rem'); + for (const att of gustChild.attributes) { + existentNode.setAttribute(att.name, att.value); + if (att.name === 'event') { + const eventType = existentNode.getAttribute('event').split('/', 2)[1]; + existentNode[`on${eventType}`] = null; + setEventListeners(existentNode); + }; + }; + gustChild.remove(); + } else { + const nodeKey = gustChild.getAttribute('key'); + let existentNode = document.querySelector(`[key="${nodeKey}"]`); + existentNode.replaceWith(gustChild); + let replacedNode = document.querySelector(`[key="${nodeKey}"]`); + if (replacedNode.getAttribute('event')) { + setEventListeners(replacedNode); + }; + if (replacedNode.childElementCount > 0) { + let needingListeners = replacedNode.querySelectorAll('[event]'); + needingListeners.forEach(child => setEventListeners(child)); + }; + }; + }; + } catch (error) { + console.error(error); + }; +}; +function makeSubscribeBody() { + channelMessageId++; + return [{ + id: channelMessageId, + action: 'subscribe', + ship: ship, + app: app, + path: path + }]; +}; +function makePokeBody(jsonData) { + channelMessageId++; + return [{ + id: channelMessageId, + action: 'poke', + ship: ship, + app: app, + mark: 'json', + json: { pith: pith, data: jsonData } + }]; +}; +function makeAck(eventId) { + channelMessageId++; + return [{ + id: channelMessageId, + action: 'ack', + "event-id": eventId + }]; +}; diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon index a30ef07bf63..4f8f0948a99 100644 --- a/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon +++ b/pkg/arvo/neo/cod/std/src/imp/mast-diary-ui.hoon @@ -50,8 +50,21 @@ ~|(%diary-input-fail !!) =/ dif=diary-diff [%put-entry now.bowl u.dat] + =/ dst=pith:neo + p:(~(got by deps.bowl) %diary) :_ pail - :~ [#/[p/our.bowl]/mast-diary-demo %poke diary-diff/!>(dif)] + :~ [dst %poke diary-diff/!>(dif)] + == + :: + [%click %delete @ta ~] + =/ key=@da + (slav %da i.t.t.path.eve) + =/ dif=diary-diff + [%del-entry key] + =/ dst=pith:neo + p:(~(got by deps.bowl) %diary) + :_ pail + :~ [dst %poke diary-diff/!>(dif)] == :: == @@ -104,9 +117,15 @@ ;div ;* %+ turn diary-entries |= [date=@da =txt] + =/ key=tape ;div + =key key ;p: {(pretty-date date)} ;p: {(trip txt)} + ;button + =event "/click/delete/{key}" + ;+ ;/ "✖" + == == == :: diff --git a/pkg/arvo/neo/cod/std/src/imp/mast.hoon b/pkg/arvo/neo/cod/std/src/imp/mast.hoon index 96836478379..a295b3fa677 100644 --- a/pkg/arvo/neo/cod/std/src/imp/mast.hoon +++ b/pkg/arvo/neo/cod/std/src/imp/mast.hoon @@ -1,4 +1,5 @@ /- mast +/* mast-js =< ^- kook:neo |% @@ -7,7 +8,7 @@ ++ kids :+ ~ %y %- my - :~ [[|/%p &] [%pro %manx] (sy %sig ~)] :: fix + :~ [[|/%p |] [%pro %manx] (sy %sig ~)] :: fix [[&/%aft |/%p |] [%pro %manx] ~] == ++ deps *deps:neo @@ -31,13 +32,13 @@ == :_ sig/!>(rig) :~ [pith %poke eyre-req/!>(req)] - [(snoc here.bowl p/ship.src.bowl) %make made.rig] :: temporary + [(snoc here.bowl p/ship.src.bowl) %make made.rig] == :: ++ poke |= [sud=stud:neo vaz=vase] ^- (quip card:neo pail:neo) - ~& mast-poke/sud + :: ~& mast-poke/sud =+ !<(=rig:mast q.pail) ?+ sud ~|(bad-stud/sud !!) :: @@ -103,14 +104,16 @@ cards %+ snoc (~(gale res bowl) u.rid sail rig) - [(welp here.bowl #/aft/[p/boat]) %make [%manx [~ manx/!>(sail)] ~]] + (new-aft-sail sail boat here.bowl) == ?+ mode.loot.i.rum $(rum t.rum) :: %add - ~& >>> missing-eyre-id/pith.i.rum - $(rum t.rum) + %= $ + rum t.rum + cards (snoc cards (new-aft-sail sail boat here.bowl)) + == :: %dif =/ aft=manx @@ -127,7 +130,7 @@ cards %+ snoc (~(gust res bowl) rig aft sail) - [(welp here.bowl #/aft/[p/boat]) %make [%manx [~ manx/!>(sail)] ~]] + (new-aft-sail sail boat here.bowl) == :: == @@ -165,10 +168,10 @@ %_ sail a.g ^- mart - :* [%pith <^-(path ?>(?=(^ here.bowl) (pout:neo t.here.bowl)))>] :: destination path to shrub from neo - [%app "neo"] + :* [%pith <^-(path (pout:neo here.bowl))>] :: destination path to shrub from neo [%path <^-(path (snoc base-sub.rig (scot %p ship.src.bowl)))>] :: sub path, shrub to eyre [%ship +:(scow %p our.bowl)] + [%app "neo"] a.g.sail == c.i.c @@ -239,11 +242,55 @@ :: -- :: +++ new-aft-sail + |= [sail=manx boat=@p base=pith:neo] + ^- card:neo + [(welp base #/aft/[p/boat]) %make [%manx [~ manx/!>(sail)] ~]] +:: ++ parse-channel-data |= jon=json ^- crow:mast ((ot ~[path+pa data+(om so)]):dejs:format jon) :: +++ script-node + ^- manx + ;script: {(trip mast-js)} +:: +++ hoist + |= root=manx + |^ ^- manx + (tanx root "0" "~") + ++ tanx + |= [m=manx key=tape pkey=tape] + =/ fkey=tape (getv a.g.m %key) + =/ nkey=tape ?~(fkey key fkey) + ?: =(%$ n.g.m) + ;span + =mast "text" + =key nkey + =pkey pkey + ;+ m + == + =: a.g.m %- mart + ?~ fkey + [[%key nkey] [[%pkey pkey] a.g.m]] + [[%pkey pkey] a.g.m] + c.m (tarl c.m nkey) + == + m + ++ tarl + |= [m=marl key=tape] + =/ i=@ud 0 + |- ^- marl + ?~ m + ~ + :- %^ tanx + (manx i.m) + (weld (scow %ud i) (weld "-" key)) + key + $(m t.m, i +(i)) + -- +:: ++ algo |= [old=marl new=marl] ^- marl @@ -390,41 +437,6 @@ == $(jold t.jold, j +(j)) :: -++ hoist - |= root=manx - |^ ^- manx - (tanx root "0" "~") - ++ tanx - |= [m=manx key=tape pkey=tape] - =/ fkey=tape (getv a.g.m %key) - =/ nkey=tape ?~(fkey key fkey) - ?: =(%$ n.g.m) - ;span - =mast "text" - =key nkey - =pkey pkey - ;+ m - == - =: a.g.m %- mart - ?~ fkey - [[%key nkey] [[%pkey pkey] a.g.m]] - [[%pkey pkey] a.g.m] - c.m (tarl c.m nkey) - == - m - ++ tarl - |= [m=marl key=tape] - =/ i=@ud 0 - |- ^- marl - ?~ m - ~ - :- %^ tanx - (manx i.m) - (weld (scow %ud i) (weld "-" key)) - key - $(m t.m, i +(i)) - -- -:: ++ getv |= [m=mart tag=@tas] ^- tape @@ -482,258 +494,4 @@ i.ml $(ml t.ml, j +(j)) :: -++ paru - |= turl=tape - ^- path - =/ tacc=tape ~ - =/ pacc=path ~ - |- - ?~ turl - ?~ tacc - pacc - (snoc pacc (crip tacc)) - ?: =('/' i.turl) - ?~ tacc - $(turl t.turl) - %= $ - turl t.turl - tacc ~ - pacc (snoc pacc (crip tacc)) - == - $(turl t.turl, tacc (snoc tacc i.turl)) -:: -++ script-node - ^- manx - ;script - ;+ ;/ script - == -:: -++ script - ^~ - %- trip - ''' - let pith; - let ship; - let app; - let displayUpdatePath; - let channelMessageId = 0; - let eventSource; - const channelId = `${Date.now()}${Math.floor(Math.random() * 100)}`; - const channelPath = `${window.location.origin}/~/channel/${channelId}`; - addEventListener('DOMContentLoaded', async () => { - pith = document.documentElement.getAttribute('pith'); - ship = document.documentElement.getAttribute('ship'); - app = document.documentElement.getAttribute('app'); - displayUpdatePath = document.documentElement.getAttribute('path'); - await connectToShip(); - let eventElements = document.querySelectorAll('[event]'); - eventElements.forEach(el => setEventListeners(el)); - }); - function setEventListeners(el) { - const eventTags = el.getAttribute('event'); - const returnTags = el.getAttribute('return'); - eventTags.split(/\s+/).forEach(eventStr => { - const eventType = eventStr.split('/', 2)[1]; - el[`on${eventType}`] = (e) => pokeShip(e, eventStr, returnTags); - }); - }; - async function connectToShip() { - try { - const storageKey = `${ship}${app}${displayUpdatePath}`; - let storedId = localStorage.getItem(storageKey); - localStorage.setItem(storageKey, channelId); - if (storedId) { - const delPath = `${window.location.origin}/~/channel/${storedId}`; - await fetch(delPath, { - method: 'PUT', - body: JSON.stringify([{ - id: channelMessageId, - action: 'delete' - }]) - }); - }; - const body = JSON.stringify(makeSubscribeBody()); - await fetch(channelPath, { - method: 'PUT', - body - }); - eventSource = new EventSource(channelPath); - eventSource.addEventListener('message', handleChannelStream); - } catch (error) { - console.error(error); - }; - }; - function pokeShip(event, tagString, dataString) { - try { - let data = {}; - if (dataString) { - const dataToReturn = dataString.split(/\s+/); - dataToReturn.forEach(dataTag => { - let splitDataTag = dataTag.split('/'); - if (splitDataTag[0] === '') splitDataTag.shift(); - const kind = splitDataTag[0]; - const key = splitDataTag.pop(); - if (kind === 'event') { - if (!(key in event)) { - console.error(`Property: ${key} does not exist on the event object`); - return; - }; - data[dataTag] = String(event[key]); - } else if (kind === 'target') { - if (!(key in event.currentTarget)) { - console.error(`Property: ${key} does not exist on the target object`); - return; - }; - data[dataTag] = String(event.currentTarget[key]); - } else { - const elementId = splitDataTag.join('/'); - const linkedEl = document.getElementById(elementId); - if (!linkedEl) { - console.error(`No element found for id: ${kind}`); - return; - }; - if (!(key in linkedEl)) { - console.error(`Property: ${key} does not exist on the object with id: ${elementId}`); - return; - }; - data[dataTag] = String(linkedEl[key]); - }; - }); - }; - fetch(channelPath, { - method: 'PUT', - body: JSON.stringify(makePokeBody({ - path: tagString, - data - })) - }); - } catch (error) { - console.error(error); - }; - }; - function handleChannelStream(event) { - try { - const streamResponse = JSON.parse(event.data); - if (streamResponse.response !== 'diff') return; - fetch(channelPath, { - method: 'PUT', - body: JSON.stringify(makeAck(streamResponse.id)) - }); - const htmlData = streamResponse.json; - if (!htmlData) return; - let container = document.createElement('template'); - container.innerHTML = htmlData; - if (container.content.firstElementChild.childNodes.length === 0) return; - // const navUrl = container.content.firstElementChild.getAttribute('url'); - // if (navUrl && (navUrl !== window.location.pathname)) { - // history.pushState({}, '', navUrl); - // }; - while (container.content.firstElementChild.children.length > 0) { - let gustChild = container.content.firstElementChild.firstElementChild; - if (gustChild.tagName === 'D') { - for (const att of gustChild.attributes) { - const dkey = att.value; - document.querySelector(`[key="${dkey}"]`).remove(); - }; - gustChild.remove(); - } else if (gustChild.tagName === 'N') { - const nodeKey = gustChild.firstElementChild.getAttribute('key'); - const parentKey = gustChild.firstElementChild.getAttribute('pkey'); - const appendIndex = gustChild.id; - let domParent = document.querySelector(`[key="${parentKey}"]`); - domParent.insertBefore(gustChild.firstElementChild, domParent.children[appendIndex]); - let appendedChild = domParent.querySelector(`[key="${nodeKey}"]`); - if (appendedChild.getAttribute('event')) { - setEventListeners(appendedChild); - }; - if (appendedChild.childElementCount > 0) { - let needingListeners = appendedChild.querySelectorAll('[event]'); - needingListeners.forEach(child => setEventListeners(child)); - }; - appendedChild = appendedChild.nextElementSibling; - gustChild.remove(); - } else if (gustChild.tagName === 'M') { - const nodeKey = gustChild.getAttribute('key'); - const nodeIndex = gustChild.id; - let existentNode = document.querySelector(`[key="${nodeKey}"]`); - let childAtIndex = existentNode.parentElement.children[nodeIndex]; - if (existentNode.nextElementSibling - && (existentNode.nextElementSibling.getAttribute('key') - === childAtIndex.getAttribute('key'))) { - existentNode.parentElement.insertBefore(existentNode, childAtIndex.nextElementSibling); - } else { - existentNode.parentElement.insertBefore(existentNode, childAtIndex); - }; - gustChild.remove(); - } else if (gustChild.tagName === 'C') { - const nodeKey = gustChild.getAttribute('key'); - const attToRem = gustChild.getAttribute('rem')?.slice(0, -1).split(' ') ?? []; - let existentNode = document.querySelector(`[key="${nodeKey}"]`); - attToRem.forEach(att => { - if (att === 'event') { - const eventType = existentNode.getAttribute('event').split('/', 2)[1]; - existentNode[`on${eventType}`] = null; - }; - existentNode.removeAttribute(att); - }); - gustChild.removeAttribute('key'); - gustChild.removeAttribute('rem'); - for (const att of gustChild.attributes) { - existentNode.setAttribute(att.name, att.value); - if (att.name === 'event') { - const eventType = existentNode.getAttribute('event').split('/', 2)[1]; - existentNode[`on${eventType}`] = null; - setEventListeners(existentNode); - }; - }; - gustChild.remove(); - } else { - const nodeKey = gustChild.getAttribute('key'); - let existentNode = document.querySelector(`[key="${nodeKey}"]`); - existentNode.replaceWith(gustChild); - let replacedNode = document.querySelector(`[key="${nodeKey}"]`); - if (replacedNode.getAttribute('event')) { - setEventListeners(replacedNode); - }; - if (replacedNode.childElementCount > 0) { - let needingListeners = replacedNode.querySelectorAll('[event]'); - needingListeners.forEach(child => setEventListeners(child)); - }; - }; - }; - } catch (error) { - console.error(error); - }; - }; - function makeSubscribeBody() { - channelMessageId++; - return [{ - id: channelMessageId, - action: 'subscribe', - ship: ship, - app: app, - path: displayUpdatePath - }]; - }; - function makePokeBody(jsonData) { - channelMessageId++; - return [{ - id: channelMessageId, - action: 'poke', - ship: ship, - app: app, - mark: 'json', - json: { path: pith, data: jsonData } - }]; - }; - function makeAck(eventId) { - channelMessageId++; - return [{ - id: channelMessageId, - action: 'ack', - "event-id": eventId - }]; - }; - ''' -:: -- From 49decf526db1218dfb0c03b0d9928ad3b137b2dc Mon Sep 17 00:00:00 2001 From: hanfel-dovned Date: Wed, 24 Jul 2024 15:03:42 -0700 Subject: [PATCH 3/7] initial task UI using mast --- .../neo/cod/std/src/imp/mast-task-ui.hoon | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon new file mode 100644 index 00000000000..84d7702f6a5 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon @@ -0,0 +1,118 @@ +/@ ui-event +/@ task +/@ task-diff +^- kook:neo +=< +|% +++ state pro/%manx +++ poke (sy %ui-event %rely %gift ~) +++ kids + *kids:neo +++ deps + ^- deps:neo + %- my + :~ :- %src + :- req=& + :- [pro/%task (sy %task-diff %gift ~)] + :+ ~ %y + %- my + :~ [[|/%ud |] pro/%task (sy %task-diff %gift ~)] + == + == +++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo =pail:neo] + :: + ++ init + |= pal=(unit pail:neo) + ^- (quip card:neo pail:neo) + :- ~ + manx/!>((render (task-map bowl))) + :: + ++ poke + |= [=stud:neo =vase] + ^- (quip card:neo pail:neo) + ?+ stud ~|(bad-stud/stud !!) + :: + %ui-event + =/ event !<(ui-event vase) + ?+ path.event + ~|(missing-event-handler-for/path.event !!) + :: + [%click %submit ~] + =/ text=@t + ~| %task-input-fail + (~(got by data.event) '/task-input/value') + :_ pail + :~ :- p:(~(got by deps.bowl) %src) + :+ %poke + %task-diff + !>([%new [text %.y %.y ~] %.y]) + == + == + :: + %rely + :- ~ + manx/!>((render (task-map bowl))) + == + -- +-- +:: +|% +++ render + |_ tasks=(map pith task) + ++ $ + ^- manx + ;html + ;head + ;meta(charset "utf-8"); + == + ;body + =style "margin: 0; width: 100%; display: grid; place-items: center;" + ;main + ;h1: Tasks + ;div + ;p: {(trip text:(~(got by tasks) /))} + == + ;+ task-form + ;+ subtasks + == + == + == + :: + ++ task-form + ^- manx + ;div + ;textarea(id "task-input", style "height: 10rem; width: 25rem; margin-block: 1rem;"); + ;button + =event "/click/submit" + =return "/task-input/value" + ;+ ;/ "Enter" + == + == + :: + ++ subtasks + ^- manx + ;div + ;* %+ turn + %~ val by + (~(del by tasks) /) + |= [text=cord done=? kids-done=? order=(list pith)] + ;div + ;p: {(trip text)} + == + == + -- +:: +++ task-map + |= =bowl:neo + ^- (map pith task) + %- malt + %+ turn + %~ tap + of:neo + q:(~(got by deps.bowl) %src) + |= [=pith =idea:neo] + :- pith + !<(task q.pail.idea) +-- From 11862e00acdbb2edd94b927da7a51996eda46756 Mon Sep 17 00:00:00 2001 From: hanfel-dovned Date: Thu, 25 Jul 2024 09:56:56 -0700 Subject: [PATCH 4/7] tasks in mast --- .../neo/cod/std/src/imp/mast-task-ui.hoon | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon index 84d7702f6a5..72134bfe257 100644 --- a/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon +++ b/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon @@ -41,7 +41,6 @@ :: [%click %submit ~] =/ text=@t - ~| %task-input-fail (~(got by data.event) '/task-input/value') :_ pail :~ :- p:(~(got by deps.bowl) %src) @@ -49,6 +48,16 @@ %task-diff !>([%new [text %.y %.y ~] %.y]) == + :: + [%click %checkbox ~] + =/ t=pith #/placeholder + ::(~(got by data.event) '/task-input/value') :: XX encode this + :_ pail + :~ :- t + :+ %poke + %task-diff + !>([%edit 'placeholder' %.y]) :: XX get text from issue + == == :: %rely @@ -85,7 +94,7 @@ ;div ;textarea(id "task-input", style "height: 10rem; width: 25rem; margin-block: 1rem;"); ;button - =event "/click/submit" + =event "/click/new" =return "/task-input/value" ;+ ;/ "Enter" == @@ -95,11 +104,18 @@ ^- manx ;div ;* %+ turn - %~ val by + %~ tap by (~(del by tasks) /) - |= [text=cord done=? kids-done=? order=(list pith)] + |= [=pith =task] ;div - ;p: {(trip text)} + ;p: {(trip text.task)} + :: ;input + :: =type "checkbox" + :: =name "done" + :: =event "/click/checkbox" + :: =return :: XX what to return here? + :: ; + :: == == == -- From 4428cc5b582c791a239ef813097ffe6b8ab56e1c Mon Sep 17 00:00:00 2001 From: hanfel-dovned Date: Thu, 25 Jul 2024 10:30:50 -0700 Subject: [PATCH 5/7] fix textbox --- pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon index 72134bfe257..990bbd1a83e 100644 --- a/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon +++ b/pkg/arvo/neo/cod/std/src/imp/mast-task-ui.hoon @@ -39,7 +39,7 @@ ?+ path.event ~|(missing-event-handler-for/path.event !!) :: - [%click %submit ~] + [%click %new ~] =/ text=@t (~(got by data.event) '/task-input/value') :_ pail From 570e197bd96549b7c4618ca76d174fcf1667758d Mon Sep 17 00:00:00 2001 From: wispem-wantex Date: Sun, 28 Jul 2024 14:36:42 -0700 Subject: [PATCH 6/7] Recipe Book (2 build parties worth) --- pkg/arvo/neo/cod/std/src/imp/food.hoon | 1 + pkg/arvo/neo/cod/std/src/imp/foods.hoon | 46 +++++ .../neo/cod/std/src/imp/mast-foods-ui.hoon | 164 ++++++++++++++++++ pkg/arvo/neo/cod/std/src/imp/recipe-book.hoon | 50 ++++++ pkg/arvo/neo/cod/std/src/imp/recipe.hoon | 60 +++++++ pkg/arvo/neo/cod/std/src/pro/food.hoon | 25 +++ pkg/arvo/neo/cod/std/src/pro/foods-diff.hoon | 6 + pkg/arvo/neo/cod/std/src/pro/ingredient.hoon | 7 + pkg/arvo/neo/cod/std/src/pro/measure.hoon | 15 ++ pkg/arvo/neo/cod/std/src/pro/recipe-diff.hoon | 10 ++ pkg/arvo/neo/cod/std/src/pro/recipe.hoon | 16 ++ .../neo/cod/std/src/pro/recipes-diff.hoon | 6 + 12 files changed, 406 insertions(+) create mode 100644 pkg/arvo/neo/cod/std/src/imp/food.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/foods.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/recipe-book.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/recipe.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/food.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/foods-diff.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/ingredient.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/measure.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/recipe-diff.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/recipe.hoon create mode 100644 pkg/arvo/neo/cod/std/src/pro/recipes-diff.hoon diff --git a/pkg/arvo/neo/cod/std/src/imp/food.hoon b/pkg/arvo/neo/cod/std/src/imp/food.hoon new file mode 100644 index 00000000000..54bcf304652 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/food.hoon @@ -0,0 +1 @@ +~ diff --git a/pkg/arvo/neo/cod/std/src/imp/foods.hoon b/pkg/arvo/neo/cod/std/src/imp/foods.hoon new file mode 100644 index 00000000000..5e09d714034 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/foods.hoon @@ -0,0 +1,46 @@ +/@ food +/@ foods-diff +:: +^- kook:neo +|% + ++ state pro/%sig + ++ poke (sy %foods-diff %food ~) + ++ kids + :+ ~ %y + %- ~(gas by *lads:neo) + :~ :- [|/%ux |] [pro/%food ~] + == + ++ deps *deps:neo + ++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo state=pail:neo] + ++ init + |= old=(unit pail:neo) + ^- (quip card:neo pail:neo) + :- ~ sig/!>(~) + ++ poke + |= [=stud:neo vax=vase] + ^- (quip card:neo pail:neo) + :_ sig/!>(~) :: There is no state + :~ ?+ stud !! + %food + :- (snoc here.bowl [%ux (mod eny.bowl (pow 16 16))]) + [%make %food `food/vax ~] + %foods-diff + =/ poke !<(foods-diff vax) + ?> =(our.bowl ship.src.bowl) + ?- -.poke + %new-food + :- (snoc here.bowl [%ux (mod eny.bowl (pow 16 16))]) + [%make %food `food/!>(food.poke) ~] + %put-food + :- (snoc here.bowl [%ux id.poke]) + [%make %food `food/!>(food.poke) ~] + %del-food !! + :: :- (snoc here.bowl [%ux id.poke]) + :: [%tomb %food ~] + == + == + == + -- +-- diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon new file mode 100644 index 00000000000..bbb9c4b7c44 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon @@ -0,0 +1,164 @@ +/@ ui-event +/@ food +::/@ txt +/@ foods-diff +:: +^- kook:neo +=< +|% + ++ state pro/%manx + ++ poke (sy %ui-event %rely %gift ~) + ++ kids *kids:neo + ++ deps + ^- deps:neo + %- my + :~ :^ %src & [pro/%sig (sy %foods-diff %food ~)] + :+ ~ %y + %- ~(gas by *lads:neo) + :~ :- [|/%ux |] [pro/%food ~] + == + == + ++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo =pail:neo] + :: + ++ init + |= pal=(unit pail:neo) + ^- (quip card:neo pail:neo) + =/ the-foods (food-map bowl) + :- ~ + manx/!>((render the-foods)) + :: + ++ poke + |= [std=stud:neo vaz=vase] + ^- (quip card:neo pail:neo) + ?+ std ~|(bad-stud/std !!) + :: + %ui-event + =/ eve !<(ui-event vaz) + ?+ path.eve !! + [%click %submit ~] + =/ name (~(got by data.eve) '/name-input/value') + =/ calories (slav %rs (~(got by data.eve) '/calories-input/value')) + =/ carbs (slav %rs (~(got by data.eve) '/carbs-input/value')) + =/ protein (slav %rs (~(got by data.eve) '/protein-input/value')) + =/ the-diff=foods-diff + [%new-food %*(. *food name name, calories calories, carbs carbs, protein protein)] + =/ dst=pith:neo p:(~(got by deps.bowl) %src) + :_ pail + :~ [dst %poke foods-diff/!>(the-diff)] + == + == + :: + %rely + =/ the-foods (food-map bowl) + :- ~ + manx/!>((render the-foods)) + :: + :: ::?+ path.eve ~|(missing-event-handler-for/path.eve !!) + :: :: :: + :: :: [%click %submit ~] + :: :: =/ dat=(unit @t) + :: :: (~(get by data.eve) '/diary-input/value') + :: :: ?~ dat + :: :: ~|(%diary-input-fail !!) + :: :: =/ dif=diary-diff + :: :: [%put-entry now.bowl u.dat] + :: :: =/ dst=pith:neo + :: :: p:(~(got by deps.bowl) %diary) + :: :: :_ pail + :: :: :~ [dst %poke diary-diff/!>(dif)] + :: :: == + :: :: :: + :: :: [%click %delete @ta ~] + :: :: =/ key=@da + :: :: (slav %da i.t.t.path.eve) + :: :: =/ dif=diary-diff + :: :: [%del-entry key] + :: :: =/ dst=pith:neo + :: :: p:(~(got by deps.bowl) %diary) + :: :: :_ pail + :: :: :~ [dst %poke diary-diff/!>(dif)] + :: :: == + :: :: :: + :: ::== + :: :: + == + :: + -- +-- +:: +|% + :: + ++ render + |= [the-foods=(map pith food)] + |^ ^- manx + ;html + ;head + ;meta(charset "utf-8"); + == + ;body + =style "margin: 0; width: 100%; display: grid; place-items: center;" + ;main + ;h1: Foods database + ;+ food-form + ;+ food-items + == + == + == + :: + ++ food-items + ^- manx + ;div + ;* %+ turn ~(val by the-foods) + |= [=food] + ;div: {(trip name.food)} + == + :: + ++ food-form + ^- manx + ;div + ;div + ;label: Name: + ;input(id "name-input", name "name", type "text"); + == + ;div + ;label: Calories: + ;input(id "calories-input", name "calories", type "text"); + == + ;div + ;label: Carbs: + ;input(id "carbs-input", name "carbs", type "text"); + == + ;div + ;label: Protein: + ;input(id "protein-input", name "protein", type "text"); + == + ;button + =event "/click/submit" + =return + """ + /name-input/value + /calories-input/value + /carbs-input/value + /protein-input/value + """ + ; Enter + == + == + -- + :: :: :: :: :: :: :: :: :: :: + ++ food-map + |= =bowl:neo + ^- (map pith food) + %- malt + %+ turn + %~ tap + of:neo + %- %~ del of:neo + q:(~(got by deps.bowl) %src) + / + |= [=pith =idea:neo] + :- pith + !<(food q.pail.idea) +-- diff --git a/pkg/arvo/neo/cod/std/src/imp/recipe-book.hoon b/pkg/arvo/neo/cod/std/src/imp/recipe-book.hoon new file mode 100644 index 00000000000..62a4b8d7a55 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/recipe-book.hoon @@ -0,0 +1,50 @@ +/@ recipe +/@ recipes-diff +:: +^- kook:neo +|% + ++ state pro/%pith :: absolute path to the food database + ++ poke (sy %recipes-diff ~) + ++ kids + :+ ~ %y + %- ~(gas by *lads:neo) + :~ :- [|/%ux |] [pro/%recipe ~] + == + ++ deps *deps:neo + ++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo state-pail=pail:neo] + ++ init + |= old=(unit pail:neo) + ^- (quip card:neo pail:neo) + ?~ old !! + =/ the-pail (need old) + ?> =(%pith p.the-pail) + :- ~ the-pail + ++ poke + |= [=stud:neo vax=vase] + ^- (quip card:neo pail:neo) + :::_ sig/!>(~) :: There is no state + :_ state-pail + :~ ?+ stud !! + %recipes-diff + =/ poke !<(recipes-diff vax) + ?> =(our.bowl ship.src.bowl) + ?+ -.poke !! + %new-recipe + =/ the-recipe %*(. *recipe name name.poke) + :- (snoc here.bowl [%ux (mod eny.bowl (pow 16 16))]) + :* %make %recipe `recipe/!>(the-recipe) + %- molt ~[[%src !<(pith q.state-pail)]] + == + ::%put-food + :: :- (snoc here.bowl [%ux id.poke]) + :: [%make %food `food/!>(food.poke) ~] + ::%del-food !! + :: :- (snoc here.bowl [%ux id.poke]) + :: [%tomb %food ~] + == + == + == + -- +-- diff --git a/pkg/arvo/neo/cod/std/src/imp/recipe.hoon b/pkg/arvo/neo/cod/std/src/imp/recipe.hoon new file mode 100644 index 00000000000..1877ea9909c --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/recipe.hoon @@ -0,0 +1,60 @@ +/@ recipe +/@ recipe-diff +/@ ingredient +:: +^- kook:neo +|% + ++ state pro/%recipe + ++ poke (sy %recipe-diff ~) + ++ kids *kids:neo + :::+ ~ %y + ::%- ~(gas by *lads:neo) + :::~ :- [|/%ux |] [pro/%recipes ~] + ::== + ++ deps + ^- deps:neo + %- my + :~ :^ %src & [pro/%sig (sy %foods-diff %food ~)] + :+ ~ %y + %- ~(gas by *lads:neo) + :~ :- [|/%ux |] [pro/%food ~] + == + == + ++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo state=pail:neo] + ++ init + |= old=(unit pail:neo) + ^- (quip card:neo pail:neo) + :- ~ (need old) + ++ poke + |= [=stud:neo vax=vase] + ^- (quip card:neo pail:neo) + ?> =(stud %recipe) + =/ the-recipe !<(recipe vax) + ?+ stud !! + %recipe-diff + =/ poke !<(recipe-diff vax) + ?> =(our.bowl ship.src.bowl) + :- ~ :: No cards + :: + ?+ -.poke !! + %add-ingredient + :- %recipe + !> %_ the-recipe + ingredients (snoc ingredients.the-recipe ingredient.poke) + == + + ::%new-recipe + :: :- (snoc here.bowl [%ux (mod eny.bowl (pow 16 16))]) + :: [%make %recipe `recipe/!>(recipe.poke) ~] + ::%put-recipe + :: :- (snoc here.bowl [%ux id.poke]) + :: [%make %recipe `recipe/!>(recipe.poke) ~] + ::%del-recipe !! + :: :- (snoc here.bowl [%ux id.poke]) + :: [%tomb %recipe ~] + == + == + -- +-- diff --git a/pkg/arvo/neo/cod/std/src/pro/food.hoon b/pkg/arvo/neo/cod/std/src/pro/food.hoon new file mode 100644 index 00000000000..e6fb0c2d7dd --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/food.hoon @@ -0,0 +1,25 @@ +=> |% + +$ id @ + -- +$+ food-type +$: id=id + name=@t + calories=@rs + carbs=@rs + protein=@rs + fat=@rs + sugar=@rs + alcohol=@rs + water=@rs + potassium=@rs + sodium=@rs + calcium=@rs + magnesium=@rs + phosphorus=@rs + iron=@rs + zinc=@rs + mass=@rs + density=(unit @rs) + price=@rs + cook-ratio=@rs +== diff --git a/pkg/arvo/neo/cod/std/src/pro/foods-diff.hoon b/pkg/arvo/neo/cod/std/src/pro/foods-diff.hoon new file mode 100644 index 00000000000..28a6dd51718 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/foods-diff.hoon @@ -0,0 +1,6 @@ +/@ food +:: +$% [%new-food =food] + [%put-food =id:food =food] + [%del-food =id:food] +== diff --git a/pkg/arvo/neo/cod/std/src/pro/ingredient.hoon b/pkg/arvo/neo/cod/std/src/pro/ingredient.hoon new file mode 100644 index 00000000000..9d3cf947d6f --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/ingredient.hoon @@ -0,0 +1,7 @@ +/@ measure +:: +:: An `ingredient` is an amount of a food. +$+ ingredient-type +$: food-id=@ + amount=[=@rs =measure] :: e.g., [100 %g] => 100 grams; [3 %ct] => three of them +== diff --git a/pkg/arvo/neo/cod/std/src/pro/measure.hoon b/pkg/arvo/neo/cod/std/src/pro/measure.hoon new file mode 100644 index 00000000000..1e3948db3d0 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/measure.hoon @@ -0,0 +1,15 @@ +$+ measure +$? %ct :: servings / item count (e.g., "3 bananas") + :: + :: Mass + %g :: grams + %lbs :: pounds + %oz :: ounces + :: + :: Volume + %ml :: milliliters + %cups :: cups + %tsp :: teaspoons + %tbsp :: tablespoons + %fl-oz :: fluid ounces +== diff --git a/pkg/arvo/neo/cod/std/src/pro/recipe-diff.hoon b/pkg/arvo/neo/cod/std/src/pro/recipe-diff.hoon new file mode 100644 index 00000000000..787cdac3ac9 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/recipe-diff.hoon @@ -0,0 +1,10 @@ +/@ recipe +/@ ingredient +:: +$% [%add-instruction text=@t] + [%move-instruction from=@ to=@] + [%delete-instruction from=@] + [%add-ingredient =ingredient] + [%delete-ingredient from=@] + [%rename name=@t] +== diff --git a/pkg/arvo/neo/cod/std/src/pro/recipe.hoon b/pkg/arvo/neo/cod/std/src/pro/recipe.hoon new file mode 100644 index 00000000000..4497f1e596d --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/recipe.hoon @@ -0,0 +1,16 @@ +/@ ingredient +:: +=> |% + +$ id @ + -- +$+ recipe-type +$: id=id + name=@ta + created-at=@da + last-modified-at=(unit @da) + ingredients=(list ingredient) + blurb=@t + instructions=(list @t) + provenance=(unit $:(author=@p =id)) + comments=(list [author=@p posted-at=@da txt=@t]) +== diff --git a/pkg/arvo/neo/cod/std/src/pro/recipes-diff.hoon b/pkg/arvo/neo/cod/std/src/pro/recipes-diff.hoon new file mode 100644 index 00000000000..f514047eb22 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/pro/recipes-diff.hoon @@ -0,0 +1,6 @@ +/@ recipe +:: +$% [%new-recipe name=@t] + [%put-recipe =id:recipe =recipe] + [%del-recipe =id:recipe] +== From b156059e9eccb46192c1ffae86665cf303843831 Mon Sep 17 00:00:00 2001 From: Alessio Date: Fri, 16 Aug 2024 11:14:06 -0700 Subject: [PATCH 7/7] Build party, Aug 9 --- .../neo/cod/std/src/imp/mast-food-ui.hoon | 169 ++++++++++++++++++ .../neo/cod/std/src/imp/mast-foods-ui.hoon | 4 +- .../neo/cod/std/src/imp/mast-recipe-ui.hoon | 164 +++++++++++++++++ pkg/arvo/neo/cod/std/src/imp/recipe.hoon | 62 ++++--- pkg/arvo/neo/cod/std/src/pro/food.hoon | 2 +- 5 files changed, 372 insertions(+), 29 deletions(-) create mode 100644 pkg/arvo/neo/cod/std/src/imp/mast-food-ui.hoon create mode 100644 pkg/arvo/neo/cod/std/src/imp/mast-recipe-ui.hoon diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-food-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-food-ui.hoon new file mode 100644 index 00000000000..378750640df --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/mast-food-ui.hoon @@ -0,0 +1,169 @@ +/@ ui-event +/@ food +:: +^- kook:neo +=< +|% + ++ state pro/%manx + ++ poke (sy %ui-event %rely %gift ~) + ++ kids *kids:neo + ++ deps + ^- deps:neo + %- ~(gas by *deps:neo) + :~ [%src | [pro/%food (sy %food ~)] ~] + == + ::%- my + :::~ :^ %the-food | [pro/food (sy %food ~)] + + :: :::+ ~ %y + :: ::%- ~(gas by *lads:neo) + :: :::~ :- [|/%ux |] [pro/%food ~] + :: ::== + ::== + ++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo =pail:neo] + :: + ++ init + |= pal=(unit pail:neo) + ^- (quip card:neo pail:neo) + =/ the-food (get-food bowl) + :- ~ :: no cards + manx/!>((render the-food)) + :: + ++ poke + |= [std=stud:neo vaz=vase] + ^- (quip card:neo pail:neo) + ?+ std ~|(bad-stud/std !!) + :: + %ui-event + =/ eve !<(ui-event vaz) + ?+ path.eve !! + [%click %submit ~] + !! + ::=/ name (~(got by data.eve) '/name-input/value') + ::=/ calories (slav %rs (~(got by data.eve) '/calories-input/value')) + ::=/ carbs (slav %rs (~(got by data.eve) '/carbs-input/value')) + ::=/ protein (slav %rs (~(got by data.eve) '/protein-input/value')) + ::=/ the-diff=foods-diff + :: [%new-food %*(. *food name name, calories calories, carbs carbs, protein protein)] + ::=/ dst=pith:neo p:(~(got by deps.bowl) %src) + :::_ pail + :::~ [dst %poke foods-diff/!>(the-diff)] + ::== + == + :: + %rely + =/ the-food (get-food bowl) + :- ~ + manx/!>((render the-food)) + :: + :: ::?+ path.eve ~|(missing-event-handler-for/path.eve !!) + :: :: :: + :: :: [%click %submit ~] + :: :: =/ dat=(unit @t) + :: :: (~(get by data.eve) '/diary-input/value') + :: :: ?~ dat + :: :: ~|(%diary-input-fail !!) + :: :: =/ dif=diary-diff + :: :: [%put-entry now.bowl u.dat] + :: :: =/ dst=pith:neo + :: :: p:(~(got by deps.bowl) %diary) + :: :: :_ pail + :: :: :~ [dst %poke diary-diff/!>(dif)] + :: :: == + :: :: :: + :: :: [%click %delete @ta ~] + :: :: =/ key=@da + :: :: (slav %da i.t.t.path.eve) + :: :: =/ dif=diary-diff + :: :: [%del-entry key] + :: :: =/ dst=pith:neo + :: :: p:(~(got by deps.bowl) %diary) + :: :: :_ pail + :: :: :~ [dst %poke diary-diff/!>(dif)] + :: :: == + :: :: :: + :: ::== + :: :: + == + :: + -- +-- +:: +|% + :: + ++ render + |= [the-food=food] + ::|^ + ^- manx + ;html + ;head + ;meta(charset "utf-8"); + == + ;body + =style "margin: 0; width: 100%; display: grid; place-items: center;" + ;main + ;h1: {(trip name.the-food)} + ;div + ;label: Calories + ;span: {} + == + ::;+ food-form + ::;+ food-items + == + == + == + :: + ::++ food-items + :: ^- manx + :: ;div + :: ;* %+ turn ~(val by the-foods) + :: |= [=food] + :: ;div + :: ;a(href "mast/mast-food-ui/~met/foods/{}"): {(trip name.food)} + :: == + :: == + :::: + ::++ food-form + :: ^- manx + :: ;div + :: ;div + :: ;label: Name: + :: ;input(id "name-input", name "name", type "text"); + :: == + :: ;div + :: ;label: Calories: + :: ;input(id "calories-input", name "calories", type "text"); + :: == + :: ;div + :: ;label: Carbs: + :: ;input(id "carbs-input", name "carbs", type "text"); + :: == + :: ;div + :: ;label: Protein: + :: ;input(id "protein-input", name "protein", type "text"); + :: == + :: ;button + :: =event "/click/submit" + :: =return + :: """ + :: /name-input/value + :: /calories-input/value + :: /carbs-input/value + :: /protein-input/value + :: """ + :: ; Enter + :: == + :: == + ::-- + :: :: :: :: :: :: :: :: :: :: + ++ get-food + |= =bowl:neo + ^- food + ~& ~(key by deps.bowl) + =/ =lore:neo q:(~(got by deps.bowl) %src) :: lore is any tree + ~& lore + =/ =idea:neo (~(got of:neo lore) /) :: idea is an tree node + !<(food q.pail.idea) +-- diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon index bbb9c4b7c44..13f4c044e37 100644 --- a/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon +++ b/pkg/arvo/neo/cod/std/src/imp/mast-foods-ui.hoon @@ -112,7 +112,9 @@ ;div ;* %+ turn ~(val by the-foods) |= [=food] - ;div: {(trip name.food)} + ;div + ;a(href "/mast/mast-food-ui/~met/foods/{}"): {(trip name.food)} + == == :: ++ food-form diff --git a/pkg/arvo/neo/cod/std/src/imp/mast-recipe-ui.hoon b/pkg/arvo/neo/cod/std/src/imp/mast-recipe-ui.hoon new file mode 100644 index 00000000000..bbb9c4b7c44 --- /dev/null +++ b/pkg/arvo/neo/cod/std/src/imp/mast-recipe-ui.hoon @@ -0,0 +1,164 @@ +/@ ui-event +/@ food +::/@ txt +/@ foods-diff +:: +^- kook:neo +=< +|% + ++ state pro/%manx + ++ poke (sy %ui-event %rely %gift ~) + ++ kids *kids:neo + ++ deps + ^- deps:neo + %- my + :~ :^ %src & [pro/%sig (sy %foods-diff %food ~)] + :+ ~ %y + %- ~(gas by *lads:neo) + :~ :- [|/%ux |] [pro/%food ~] + == + == + ++ form + ^- form:neo + |_ [=bowl:neo =aeon:neo =pail:neo] + :: + ++ init + |= pal=(unit pail:neo) + ^- (quip card:neo pail:neo) + =/ the-foods (food-map bowl) + :- ~ + manx/!>((render the-foods)) + :: + ++ poke + |= [std=stud:neo vaz=vase] + ^- (quip card:neo pail:neo) + ?+ std ~|(bad-stud/std !!) + :: + %ui-event + =/ eve !<(ui-event vaz) + ?+ path.eve !! + [%click %submit ~] + =/ name (~(got by data.eve) '/name-input/value') + =/ calories (slav %rs (~(got by data.eve) '/calories-input/value')) + =/ carbs (slav %rs (~(got by data.eve) '/carbs-input/value')) + =/ protein (slav %rs (~(got by data.eve) '/protein-input/value')) + =/ the-diff=foods-diff + [%new-food %*(. *food name name, calories calories, carbs carbs, protein protein)] + =/ dst=pith:neo p:(~(got by deps.bowl) %src) + :_ pail + :~ [dst %poke foods-diff/!>(the-diff)] + == + == + :: + %rely + =/ the-foods (food-map bowl) + :- ~ + manx/!>((render the-foods)) + :: + :: ::?+ path.eve ~|(missing-event-handler-for/path.eve !!) + :: :: :: + :: :: [%click %submit ~] + :: :: =/ dat=(unit @t) + :: :: (~(get by data.eve) '/diary-input/value') + :: :: ?~ dat + :: :: ~|(%diary-input-fail !!) + :: :: =/ dif=diary-diff + :: :: [%put-entry now.bowl u.dat] + :: :: =/ dst=pith:neo + :: :: p:(~(got by deps.bowl) %diary) + :: :: :_ pail + :: :: :~ [dst %poke diary-diff/!>(dif)] + :: :: == + :: :: :: + :: :: [%click %delete @ta ~] + :: :: =/ key=@da + :: :: (slav %da i.t.t.path.eve) + :: :: =/ dif=diary-diff + :: :: [%del-entry key] + :: :: =/ dst=pith:neo + :: :: p:(~(got by deps.bowl) %diary) + :: :: :_ pail + :: :: :~ [dst %poke diary-diff/!>(dif)] + :: :: == + :: :: :: + :: ::== + :: :: + == + :: + -- +-- +:: +|% + :: + ++ render + |= [the-foods=(map pith food)] + |^ ^- manx + ;html + ;head + ;meta(charset "utf-8"); + == + ;body + =style "margin: 0; width: 100%; display: grid; place-items: center;" + ;main + ;h1: Foods database + ;+ food-form + ;+ food-items + == + == + == + :: + ++ food-items + ^- manx + ;div + ;* %+ turn ~(val by the-foods) + |= [=food] + ;div: {(trip name.food)} + == + :: + ++ food-form + ^- manx + ;div + ;div + ;label: Name: + ;input(id "name-input", name "name", type "text"); + == + ;div + ;label: Calories: + ;input(id "calories-input", name "calories", type "text"); + == + ;div + ;label: Carbs: + ;input(id "carbs-input", name "carbs", type "text"); + == + ;div + ;label: Protein: + ;input(id "protein-input", name "protein", type "text"); + == + ;button + =event "/click/submit" + =return + """ + /name-input/value + /calories-input/value + /carbs-input/value + /protein-input/value + """ + ; Enter + == + == + -- + :: :: :: :: :: :: :: :: :: :: + ++ food-map + |= =bowl:neo + ^- (map pith food) + %- malt + %+ turn + %~ tap + of:neo + %- %~ del of:neo + q:(~(got by deps.bowl) %src) + / + |= [=pith =idea:neo] + :- pith + !<(food q.pail.idea) +-- diff --git a/pkg/arvo/neo/cod/std/src/imp/recipe.hoon b/pkg/arvo/neo/cod/std/src/imp/recipe.hoon index 1877ea9909c..36df76dc6df 100644 --- a/pkg/arvo/neo/cod/std/src/imp/recipe.hoon +++ b/pkg/arvo/neo/cod/std/src/imp/recipe.hoon @@ -22,39 +22,47 @@ == ++ form ^- form:neo - |_ [=bowl:neo =aeon:neo state=pail:neo] + |_ [=bowl:neo =aeon:neo state-pail=pail:neo] ++ init |= old=(unit pail:neo) ^- (quip card:neo pail:neo) :- ~ (need old) ++ poke - |= [=stud:neo vax=vase] + |= [poke-stud=stud:neo poke-vase=vase] ^- (quip card:neo pail:neo) - ?> =(stud %recipe) - =/ the-recipe !<(recipe vax) - ?+ stud !! - %recipe-diff - =/ poke !<(recipe-diff vax) - ?> =(our.bowl ship.src.bowl) - :- ~ :: No cards - :: - ?+ -.poke !! - %add-ingredient - :- %recipe - !> %_ the-recipe - ingredients (snoc ingredients.the-recipe ingredient.poke) - == - - ::%new-recipe - :: :- (snoc here.bowl [%ux (mod eny.bowl (pow 16 16))]) - :: [%make %recipe `recipe/!>(recipe.poke) ~] - ::%put-recipe - :: :- (snoc here.bowl [%ux id.poke]) - :: [%make %recipe `recipe/!>(recipe.poke) ~] - ::%del-recipe !! - :: :- (snoc here.bowl [%ux id.poke]) - :: [%tomb %recipe ~] - == + ?> =(poke-stud %recipe-diff) + =/ the-recipe !<(recipe q.state-pail) + =/ poke !<(recipe-diff poke-vase) + ?> =(our.bowl ship.src.bowl) + :- ~ :: No cards + :: + ?+ -.poke !! + %rename + :- %recipe + !> %_ the-recipe + name name.poke + == + :: + %add-ingredient + :- %recipe + !> %_ the-recipe + ingredients (snoc ingredients.the-recipe ingredient.poke) + == + :: + %add-instruction + :- %recipe + !> %_ the-recipe + instructions (snoc instructions.the-recipe text.poke) + == + ::%new-recipe + :: :- (snoc here.bowl [%ux (mod eny.bowl (pow 16 16))]) + :: [%make %recipe `recipe/!>(recipe.poke) ~] + ::%put-recipe + :: :- (snoc here.bowl [%ux id.poke]) + :: [%make %recipe `recipe/!>(recipe.poke) ~] + ::%del-recipe !! + :: :- (snoc here.bowl [%ux id.poke]) + :: [%tomb %recipe ~] == -- -- diff --git a/pkg/arvo/neo/cod/std/src/pro/food.hoon b/pkg/arvo/neo/cod/std/src/pro/food.hoon index e6fb0c2d7dd..294f9a8202c 100644 --- a/pkg/arvo/neo/cod/std/src/pro/food.hoon +++ b/pkg/arvo/neo/cod/std/src/pro/food.hoon @@ -1,5 +1,5 @@ => |% - +$ id @ + +$ id @ux -- $+ food-type $: id=id