This commit is contained in:
Will Hanlen 2024-04-17 09:36:53 -05:00
parent 68d5f97200
commit e017dd7ee0
32 changed files with 2851 additions and 145 deletions

View File

@ -8,6 +8,14 @@
/* txt-ford-face %hoon /neo/src/std/imp/ford-face/hoon
/* txt-ford-face %hoon /neo/src/std/imp/ford-face/hoon
/* txt-ford-reef %hoon /neo/src/std/imp/ford-reef/hoon
/* date-now %js /web/date-now/js
/* error-tray %js /web/error-tray/js
/* atom-input %js /web/atom-input/js
/* multiline-input %js /web/multiline-input/js
/* ha-wk %js /web/ha-wk/js
/* s-k-y %js /web/s-k-y/js
/* a-i-r %js /web/a-i-r/js
/* style-css %css /web/style/css
|%
++ pave pave:neo
++ ford ford:neo
@ -629,173 +637,212 @@
^- (unit (unit cage))
[~ ~]
--
::
++ dove
|_ here=pith:neo
++ curr
?~ rom=(get:of-top here)
;summary: Nothing here
;div(class "w-1/3")
;+ (val u.rom)
;dl
;* (room-meta here u.rom)
;dt: Dependecies
;dd
;dl
;* %- zing
%+ turn ~(tap by conf.u.rom)
|= [=term dep=pith:neo]
:~
;dt: {(trip term)}
;dd: {(en-tape:pith:neo dep)}
==
==
::
=/ rom (get:of-top here)
=- -(a.g [[%here (en-tape:pith:neo here)] a.g.-])
^- manx
?~ rom
;div.wf.hf.fc.ac.jc(empty ""): nothing here
=/ =room:neo u.rom
=/ stud ^- @tas ?^(state.room mark.state.room state.room)
=- -(a.g [[%stud (trip stud)] a.g.-])
(val u.rom)
::
++ children
::
=/ dirs
^- (list iota)
%~ tap in
%- silt
%+ turn ~(tap by (kid:of-top here))
|= [=pith:neo *]
-.pith
;div.wf.hf.fc.g1.js.ac
=here (en-tape:pith:neo here)
=slot "tree"
;*
?~ (lent dirs)
;=
;div.wf.hf.fc.ac.jc: no children
==
%+ turn
:: alphabetical sort
^- (list iota)
(sort dirs aor)
|= =iota
;button.p2.br1.b1.hover.wf.fr.js
=hx-get (en-tape:pith:neo :(weld /neo/hawk here /[iota]))
=hx-target "closest ha-wk"
=hx-swap "innerHTML"
; {(trip ?@(iota iota (scot iota)))}
==
==
::
++ val
::
|= =room:neo
^- manx
=+ !<(grow=$-(pail:neo manx) (all-grow %node))
?^ man=(mole |.((grow [state.room state.icon.room])))
u.man
;code
{":: unable to render state as manx"}
{(text state.icon.room)}
=+ !<(grow=$-(pail:neo $-(=bowl:neo manx)) (all-grow %htmx))
^- manx
=/ res=(each $-(=bowl:neo manx) tang)
(mule |.((grow [state.room state.icon.room])))
?: ?=(%& -.res)
=+ man=[u=p.res ~]
%- u.man
=+ b=*bowl.neo :: manually constructing a bowl. this is ugly
%= b
here here
kids
%- ~(run by (kid:of-top here))
|= =room:neo
[state.room state.icon.room]
==
;div.p3.fc.g3
;div.f-error.fc.g2
;span: unable to render as %htmx
;span.bold
;+ ;/
?^ state.room
(trip mark.state.room)
(trip state.room)
==
==
;code.pre.scroll-x.flex.flex-col
;div: {(text state.icon.room)}
;*
%+ turn p.res
|= tan=tank
;div: {~(ram re tan)}
==
==
++ style
::
++ svg-wrapper
::
|= [color=tape viewbox=tape body=manx]
^- manx
;svg
=xmlns "http://www.w3.org/2000/svg"
=viewBox viewbox
=fill color
=style "height: 1em;"
;+ body
==
::
++ svg-square
::
|= color=(unit tape)
%^ svg-wrapper (fall color "currentColor")
"0 0 448 512"
;path(d "M0 96C0 60.7 28.7 32 64 32H384c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96z");
::
++ favicon
::
=-
;link
=rel "icon"
=type "image/svg+xml"
=href -
;
==
%+ weld "data:image/svg+xml;utf8,"
%- en-xml:html
(svg-square `"white")
::
++ html-enc-js
::
:: htmx extension which encodes the request
:: as the serialized HTML of the calling element
::
%- trip
'''
dl {
display: grid;
grid-gap: 4px 16px;
grid-template-columns: max-content;
}
dt {
font-weight: bold;
}
dd {
margin: 0;
grid-column-start: 2;
}
htmx.defineExtension('html-enc', {
onEvent: function (name, evt) {
if (name === "htmx:configRequest") {
evt.detail.headers['Content-Type'] = "text/html";
}
},
encodeParameters : function(xhr, parameters, elt) {
xhr.overrideMimeType('text/html');
let xmls = new XMLSerializer();
return (xmls.serializeToString(elt));
}
});
'''
::
::
++ lift
::
|= in=manx
^- manx
;html
;head
;title: {(en-tape:pith:neo here)}
;style: {style}
;script@"https://cdn.tailwindcss.com";
;meta(charset "UTF-8");
;title: s k y
;script(src "https://unpkg.com/htmx.org@1.9.11");
;script(src "https://unpkg.com/htmx.org@1.9.11/dist/ext/response-targets.js");
;script: {html-enc-js}
;meta
=name "viewport"
=content
"""
width=device-width,
initial-scale=1.0,
maximum-scale=1.0"
"""
;
==
;meta
=name "htmx-config"
=content (trip '{"ignoreTitle":"true"}')
;
==
;style
;+ ;/ %- trip
'''
@font-face {
font-family: 'Urbit Sans';
src: url("https://media.urbit.org/fonts/UrbitSans/UrbitSansVFWeb-Regular.woff2") format("woff2");
font-style: normal;
font-weight: 100 700;
}
/*
@font-face {
font-family: 'Urbit';
src: url('https://nyc3.digitaloceanspaces.com/drain/hawk/2024.4.10..21.47.28-urbit.ttf') format('truetype');
}
*/
'''
==
;script
;+ ;/
"""
const sharedStyles = new CSSStyleSheet();
sharedStyles.replaceSync(`{(trip style-css)}`);
document.adoptedStyleSheets = [sharedStyles];
"""
==
;script: {(trip date-now)}
;script: {(trip atom-input)}
;script: {(trip error-tray)}
;script: {(trip multiline-input)}
;script: {(trip ha-wk)}
;script: {(trip s-k-y)}
;script: {(trip a-i-r)}
;+ favicon
==
;body
=hx-ext "html-enc,response-targets"
=hx-swap "innerHTML"
=hx-boost "true"
=hx-history "false"
=hx-replace-url "/neo/sky"
=hx-target "closest ha-wk"
;+ in
==
==
++ stud-tape
|= s=stud:neo
^- tape
?@ s (trip s)
"{(scow %p ship.s)}/{(trip desk.s)}/{(trip mark.s)}"
++ pith-a
|= [=pith:neo in=manx]
=/ tap (en-tape:pith:neo pith)
^- manx
?. |(=(~ pith) (has:of-top pith))
;span
;+ in
==
;a.underline/"/neo/hawk{tap}.html"
;+ in
==
::
++ room-li
|= [=pith:neo =room:neo]
^- manx
;li
;dl
;dt: Pith
;dd
;+ (pith-a (welp here pith) ;/((en-tape:pith:neo pith)))
==
;* (room-meta pith room)
==
==
++ navbar-item
|= [=pith:neo in=manx]
^- manx
;li.p-2.border
;+ (pith-a pith in)
==
++ home
^- manx
;li.p-2.border
;a.underline/"/neo/hawk": Root
==
::
++ navbar
;nav.w-full.my-2
;ol.flex.align-center.justify-center.w-full.space-x-4
;*
:- home
=< q
^- (pair pith:neo (list manx))
%+ roll here
|= [=iota pit=pith:neo out=(list manx)]
^- (pair pith:neo (list manx))
=. pit (snoc pit iota)
:- pit
%+ snoc out
%+ navbar-item pit
;/(?@(iota "%{(trip iota)}" (scow iota)))
==
==
++ room-meta
|= [=pith:neo =room:neo]
^- (list manx)
:~
;dt: Code
;dd: {(stud-tape code.room)}
;dt: State
;dd: {(stud-tape state.room)}
==
--
++ is-sys
|= =pith:neo
^- ?
?. ?=([@ *] pith)
|
|(=('out' i.pith) =('pre' i.pith) =('src' i.pith))
++ hawk
|= req=inbound-request:eyre
^- simple-payload:http
=/ line=request-line:serv (parse-request-line:serv url.request.req)
?> ?=([@ @ *] site.line)
=/ =pith:neo (pave:neo t.t.site.line)
=? pith ?=([%$ ~] pith)
~
=/ kids (kid:of-top pith)
=/ par (parent:of-top pith)
=* dov ~(. dove pith)
%- manx-response:gen:serv
^- manx
%- lift:dov
;main.flex.flex-col.space-y-4
;+ navbar:dov
;div.flex
;* ?:((has:of-top pith) (limo curr:dov ~) *(list manx))
;div.flex.flex-col
;h3: Children
;ol.flex.flex-col
;* (turn (skip ~(tap by kids) |=([p=pith:neo *] (is-sys p))) room-li:dov)
==
==
;* ?^ par ~
~ :: (make-form)
==
==
::
++ srv
|_ eyre-id=@ta
@ -836,12 +883,137 @@
:: XX: revive when privacy
:: ?. authenticated.req
:: (login-redirect:app:serv request.req)
=/ line=request-line:serv (parse-request-line:serv url.request.req)
?> &(?=([@ @ *] site.line) =('neo' i.site.line))
?. =('scry' i.t.site.line)
=/ purl :: parsed url with query params
::
^- [pax=path pam=(map @t @t)]
=+ %+ rash url.request.req
;~ plug
;~(pfix fas (more fas smeg:de-purl:html))
yque:de-purl:html
==
[->+.- (molt +.-)]
::
=/ body :: body parsed to a manx
::
%+ fall
(de-xml:html q:(fall body.request.req [p=0 q='']))
*manx
::
=/ here=path (welp /[(scot %p our.bowl)] pax.purl)
=/ =pith (pave:neo pax.purl)
=* dov ~(. dove pith)
::
?: =('sky' i.t.site.line)
::
=/ here (pave:neo /sky)
?~ rum=(get:of-top here)
::
:: create default tree
=/ bootstrap
^- (list card:neo)
:~
[(weld #/[p/our.bowl] here) %make %sky `!>([%system ~ 0]) ~]
[#/[p/our.bowl]/home/diary %make %diary `!>('') ~]
[#/[p/our.bowl]/home/iframes/wiki %make %iframe `!>('https://en.wikipedia.org/wiki/Main_Page') ~]
==
|-
?~ bootstrap
%- send
%- manx-response:gen:serv
%- ~(lift dove pax.purl)
;div.wf.hf.fc.jc.ac
=hx-get "/neo/sky"
=hx-target "this"
=hx-swap "outerHTML"
=hx-trigger "load"
; initializing
==
=. run
%- poke-move
:- #/[p/our.bowl]/$/eyre/req/[eyre-id]
i.bootstrap
$(bootstrap t.bootstrap)
::
%- send
%- manx-response:gen:serv
%- ~(lift dove pax.purl)
=+ !<(grow=$-(pail:neo $-(=bowl:neo manx)) (all-grow %htmx))
?~ man=(mole |.((grow [%sky state.icon.u.rum])))
~& 'could not convert sky to htmx'
!!
%- u.man
=+ b=*bowl.neo
%= b
here here
kids
%- ~(run by (kid:of-top here))
|= =room:neo
[state.room state.icon.room]
==
?> =('hawk' i.t.site.line)
(send (hawk req))
::
?: =(%'POST' method.request.req) :: %poke
::
=/ stud (@tas (~(got by pam.purl) 'stud'))
=/ conv !<($-([@ manx] vase) (all-grab %node))
?@ vert=(mole |.((conv [stud body])))
(send (manx-response:gen:serv ;/("failed to convert")))
=/ =pail:neo [stud u.vert]
=. run
%- poke-move
:- #/[p/our.bowl]/$/eyre/req/[eyre-id]
[(pave here) %poke pail]
%- send
::
%- manx-response:gen:serv
=+ !<(grow=$-(pail:neo $-(=bowl:neo manx)) (all-grow %htmx))
?^ man=(mole |.((grow pail)))
%- u.man
=+ b=*bowl.neo :: manually constructing a bowl. this is ugly
%= b
here (pave pax.purl)
==
;div: some sorta error occured
::
?: =(%'PUT' method.request.req) :: %make
::
=/ stud (@tas (~(got by pam.purl) 'stud'))
=/ conv !<($-([@ manx] vase) (all-grab %node))
?@ vert=(mole |.((conv [stud body])))
(send (manx-response:gen:serv ;/("failed to convert")))
=/ =pail:neo [stud u.vert]
=. run
%- poke-move
:- #/[p/our.bowl]/$/eyre/req/[eyre-id]
[(pave here) %make p.pail `q.pail ~]
%- send
::
%- manx-response:gen:serv
=+ !<(grow=$-(pail:neo $-(=bowl:neo manx)) (all-grow %htmx))
?^ man=(mole |.((grow pail)))
%- u.man
=+ b=*bowl.neo :: manually constructing a bowl. this is ugly
%= b
here (pave pax.purl)
==
;div: some sorta error occured
::
::
?: =(%'DELETE' method.request.req) :: %tomb
::
!!
::
:: %'GET' :: "read"
::
%- send
%- manx-response:gen:serv
?: (~(has by pam.purl) 'tree')
children:dov
curr:dov
::
=/ =path t.t.site.line
?: =(%'POST' method.request.req)
:: ?> authenticated.req

View File

@ -0,0 +1,24 @@
/@ diary-diff
:- [%diary-diff %htmx]
|= =diary-diff
|= =bowl:neo
?- -.diary-diff
%put-entry
=/ tape (trip txt.diary-diff)
=/ subject-end (fall (find [10]~ tape) 56)
=/ subject (scag subject-end tape)
=/ id (scow %da id.diary-diff)
;div.fr.g2
;a.p2.br1.grow.fc.g1.js.as.g2.b1.hover
=href "{(en-tape:pith:neo (weld /neo/hawk here.bowl))}/{id}"
;span: {id}
;span.bold: {subject}
==
;button.p2.br1.fr.g2.b1.hover.fc.ac.jc
=onclick "alert('not yet implemented. no tombstoning?')"
; X
==
==
%del-entry
;div: deled
==

View File

@ -0,0 +1,63 @@
/@ diary
:- [%diary %htmx]
|= dia=diary
|= =bowl:neo
^- manx
|^ shell
++ form-put-entry
::
;form.fc.g2
=hx-post "{(en-tape:pith:neo :(weld /neo/hawk here.bowl))}?stud=diary-diff"
=hx-on-submit "this.reset()"
=hx-target "this"
=hx-swap "afterend"
=head "put-entry"
;date-now;
;textarea.p2.border.br1
=placeholder ". . . text"
=oninput "this.setAttribute('value', this.value)"
=rows "4"
=required ""
=autocomplete "off"
;
==
;button.p2.b1.br1.wfc.hover
; create
==
==
::
++ link-entry
::
|= [pax=pith =pail:neo]
=/ tape (trip !<(@t q.pail))
=/ subject-end (fall (find [10]~ tape) 56)
=/ subject (scag subject-end tape)
=/ id (trip (snag 0 (pout pax)))
;div.fr.g2
;a.p2.br1.grow.fc.g1.js.as.g2.b1.hover
=href "{(en-tape:pith:neo (weld /neo/hawk here.bowl))}/{id}"
;span: {id}
;span.bold: {subject}
==
;button.p2.br1.fr.g2.b1.hover.fc.ac.jc
=onclick "alert('not yet implemented. no tombstoning?')"
; X
==
==
::
++ shell
::
;div.p2
=label "Diary"
;div.ma.fc.g2
=style "max-width: 650px;"
;+ form-put-entry
;*
%+ turn
%+ sort ~(tap by kids.bowl)
|= [a=[=pith *] b=[=pith *]]
(gth ->.pith.a ->.pith.b)
link-entry
==
==
--

View File

@ -0,0 +1,17 @@
/@ diary
:- [%diary %node]
|= dia=diary
^- manx
;div.flex.flex-col.gap-3.bg-green-200.border.border-green-300.rounded.p-3
;h1.text-lg.font-bold: {(trip name.dia)}
;p: this node is a %diary stud
;p: its children are %txt studs
;div.flex.gap-2
;button.p-2.bg-slate-300.rounded.border
; new entry
==
;button.p-2.bg-slate-300.rounded.border
; delete entry by path
==
==
==

View File

@ -0,0 +1,682 @@
:- [%ford-out %htmx]
=/ debug |
|= fod=[cache=(unit vase) ~]
|= =bowl:neo
^- manx
=< apex
|%
++ dprint
=>
:: dprint-types
|%
:: $overview: an overview of all named things in the type.
::
:: each element in the overview list is either a documentation for a sublist
:: or an association betwen a term and documentation for it
+$ overview (list overview-item)
::
:: $overview-item: an element of an overview
+$ overview-item
$% [%header doc=what children=overview]
[%item name=tape doc=what]
==
::
:: $item: the part of a type being inspected
+$ item
$%
:: overview of a type
::
[%view items=overview]
:: inspecting a full core
$: %core
name=tape :: arm that built it
docs=what ::
sut=type :: [%core *]
children=(unit item) :: compiled against
==
:: inspecting a single arm on a core
$: %arm
name=tape :: arm name
adoc=what :: arm doc
pdoc=what :: product doc
cdoc=what :: $ arm/prod doc
gen=hoon :: arm hoon AST
sut=type :: subject of arm
==
:: inspecting a face and what's behind it
$: %face
name=tape :: name of face
docs=what ::
children=(unit item) :: face referent
==
:: inspecting a single chapter on a core
$: %chapter
name=tape :: name of chapter
docs=what ::
sut=type :: [%core *]
tom=tome :: tome of chapter
==
==
::
--
:: dprint
::
:: core containing doccords search and printing utilities
|%
:: contains arms used for looking for docs inside of a type
::
:: the entrypoint for finding docs within a type is +find-item-in-type.
+| %searching
:: +find-item-in-type: returns the item to print while searching through topic
::
:: this gate is a thin wrapper around _hunt for usability, since the only entry
:: point most users should care about is find-item:hunt
::
++ find-item-in-type
|= [topics=(list term) sut=type]
?~ topics !!
=/ top=(lest term) topics
~(find-item hunt [top sut])
::
:: +hunt: door used for refining the type while searching for doccords
::
++ hunt
=| gil=(set type)
|_ [topics=(lest term) sut=type]
+* this .
::
+| %find
::
++ find-item
~? >> debug %find-item
^- (unit item)
?- sut
%noun ~
%void ~
[%atom *] ~
[%cell *] find-cell
[%core *] find-core
[%face *] find-face
[%fork *] find-fork
[%hint *] find-hint
[%hold *] find-item:this(sut (~(play ut p.sut) q.sut))
==
::
++ find-cell
~? >> debug %find-cell
^- (unit item)
?> ?=([%cell *] sut)
=/ lhs find-item:this(sut p.sut)
?~ lhs
find-item:this(sut q.sut)
lhs
::
++ find-core
~? >> debug %find-core
^- (unit item)
?> ?=([%core *] sut)
?: check-arm
?: check-search
?: check-arm-core
return-arm-core
return-arm
recurse-arm-core
?: check-chap
?: check-search
return-chap
recurse-chap
recurse-core
::
++ find-face
~? >> debug %find-face
^- (unit item)
?> ?=([%face *] sut)
?. ?=(term p.sut)
::TODO: handle $tune case
find-item:this(sut q.sut)
?. =(i.topics p.sut)
~
?~ t.topics
return-face
find-item:this(sut q.sut, topics t.topics)
::
++ find-fork
~? >> debug %find-fork
^- (unit item)
?> ?=([%fork *] sut)
=/ types=(list type) ~(tap in p.sut)
|-
?~ types ~
=+ res=find-item:this(sut i.types)
?~ res
$(types t.types)
res
::
++ find-hint
~? >> debug %find-hint
^- (unit item)
|^
?> ?=([%hint *] sut)
?. ?=([%help *] q.p.sut)
find-item:this(sut q.sut)
?+ q.sut ~
[%cell *] find-cell:this(sut q.sut)
[%core *] find-hint-core
[%face *] find-hint-face
[%fork *] find-fork:this(sut q.sut)
[%hint *] find-hint:this(sut q.sut)
[%hold *] find-hint:this(q.sut (~(play ut p.q.sut) q.q.sut))
==
::
++ find-hint-core
~? >> debug %find-hint-core
^- (unit item)
?> &(?=([%hint *] sut) ?=([%help *] q.p.sut) ?=([%core *] q.sut))
::
?. ?& ((sane %tas) summary.crib.p.q.p.sut)
=(summary.crib.p.q.p.sut i.topics)
==
find-core:this(sut q.sut)
?~ t.topics
return-hint-core
find-item:this(sut q.sut, topics t.topics)
::
++ find-hint-face
~? >> debug %find-hint-face
^- (unit item)
?> &(?=([%hint *] sut) ?=([%help *] q.p.sut) ?=([%face *] q.sut))
?: check-face:this(sut q.sut)
?~ t.topics
return-hint-face
find-item:this(sut q.q.sut, topics t.topics)
find-item:this(sut q.q.sut)
--
::
::+| %recurse
++ recurse-core
~? >> debug %recurse-core
^- (unit item)
?> ?=([%core *] sut)
find-item:this(sut p.sut)
++ recurse-chap
~? >> debug %recurse-chap
^- (unit item)
?> ?=([%core *] sut)
?~ t.topics !!
find-item:this(topics t.topics)
++ recurse-arm-core
~? >> debug %recurse-arm-core
^- (unit item)
?> ?=([%core *] sut)
?~ t.topics !!
find-item:this(sut arm-type, topics t.topics)
::
+| %check
::
++ check-arm
~? >> debug %recurse-core
^- ?
!=(~ (find ~[i.topics] (sloe sut)))
++ check-chap
~? >> debug %check-chap
^- ?
?> ?=([%core *] sut)
(~(has by q.r.q.sut) i.topics)
++ check-face
~? >> debug %check-face
^- ?
?> ?=([%face *] sut)
?. ?=(term p.sut)
::TODO: handle $tune case
%.n
=(p.sut i.topics)
++ check-search
~? >> debug %check-search
^- ?
=(~ t.topics)
++ check-arm-core
~? >> debug %check-arm-core
^- ?
=+ arm-list=(sloe (~(play ut sut) arm-hoon))
&(!=(arm-list ~) !=(arm-list ~[%$]) ?=([%core *] arm-type))
::
+| %return
::
++ return-cell
~? >>> debug %return-cell
^- (unit item)
?> ?=([%cell *] sut)
(join-items return-item:this(sut p.sut) return-item:this(sut q.sut))
::
++ return-core
~? >>> debug %return-core
^- (unit item)
?> ?=([%core *] sut)
=* compiled-against return-item:this(sut p.sut)
`[%core (trip i.topics) *what sut compiled-against]
::
++ return-face
~? >>> debug %return-face
^- (unit item)
?> ?=([%face *] sut)
:: TODO: handle tune case
?. ?=(term p.sut)
return-item:this(sut q.sut)
=* compiled-against return-item:this(sut q.sut)
`[%face (trip p.sut) *what compiled-against]
::
++ return-fork
~? >>> debug %return-fork
^- (unit item)
?> ?=([%fork *] sut)
=* types ~(tap in p.sut)
=* items (turn types |=(a=type return-item:this(sut a)))
(roll items join-items)
::
++ return-hint
~? >>> debug %return-hint
^- (unit item)
?> ?=([%hint *] sut)
=* res return-item:this(sut q.sut)
?. ?=([%help *] q.p.sut)
~
?: ?=([%core *] q.sut)
return-hint-core
?: ?=([%face *] q.sut)
return-hint-face
`[%view [%header `crib.p.q.p.sut (item-as-overview res)]~]
::
++ return-arm
~? >>> debug %return-arm
^- (unit item)
?> ?=([%core *] sut)
=+ [adoc pdoc cdoc]=(arm-docs i.topics sut)
::TODO: should this p.sut be sut? or the compiled type of the arm?
`[%arm (trip i.topics) adoc pdoc cdoc arm-hoon sut]
::
++ return-chap
~? >>> debug %return-chap
^- (unit item)
?> ?=([%core *] sut)
=/ tom=tome (~(got by q.r.q.sut) i.topics)
`[%chapter (trip i.topics) p.tom sut (~(got by q.r.q.sut) i.topics)]
::
++ return-arm-core
~? >>> debug %return-arm-core
^- (unit item)
?> ?=([%core *] sut)
=+ [adoc pdoc cdoc]=(arm-docs i.topics sut)
=/ dox=what ?~(adoc ?~(pdoc ~ pdoc) adoc)
=/ at arm-type
?> ?=([%core *] at)
=* compiled-against return-item:this(sut p.sut)
`[%core (trip i.topics) dox at compiled-against]
::
++ return-item
~? >>> debug %return-item
^- (unit item)
?- sut
%noun ~
%void ~
[%atom *] ~
[%cell *] return-cell
[%core *] return-core
[%face *] return-face
[%fork *] return-fork
[%hint *] return-hint
[%hold *]
?: (~(has in gil) sut)
~
=< return-item
%= this
gil (~(put in gil) sut)
sut (~(play ut p.sut) q.sut)
==
==
::
++ return-hint-core
~? >>> debug %return-hint-core
^- (unit item)
?> &(?=([%hint *] sut) ?=([%core *] q.sut))
(apply-hint return-core:this(sut q.sut))
::
++ return-hint-face
~? >>> debug %return-hint-face
^- (unit item)
?> &(?=([%hint *] sut) ?=([%face *] q.sut))
(apply-hint return-face:this(sut q.sut))
::
++ apply-hint
~? >> debug %apply-hint
|= uit=(unit item)
^- (unit item)
?~ uit ~
?> &(?=([%hint *] sut) ?=([%help *] q.p.sut))
?+ u.uit ~
?([%core *] [%face *]) (some u.uit(docs `crib.p.q.p.sut))
==
::
+| %misc
++ arm-hoon
^- hoon
?> ?=([%core *] sut)
(^arm-hoon i.topics sut)
::
++ arm-type
^- type
?> ?=([%core *] sut)
(^arm-type i.topics sut)
--
::
:: +arm-hoon: looks for an arm in a core type and returns its hoon
++ arm-hoon
|= [nom=term sut=type]
^- hoon
?> ?=([%core *] sut)
=/ tomes=(list [p=term q=tome]) ~(tap by q.r.q.sut)
|-
?~ tomes !!
=+ gen=(~(get by q.q.i.tomes) nom)
?~ gen
$(tomes t.tomes)
u.gen
::
:: +arm-type: looks for an arm in a core type and returns its type
++ arm-type
|= [nom=term sut=type]
^- type
?> ?=([%core *] sut)
(~(play ut sut) (arm-hoon nom sut))
::
:: +hint-doc: returns docs if type is %help $hint w/ matching cuff
++ hint-doc
|= [=cuff sut=type]
^- what
?. &(?=([%hint *] sut) ?=([%help *] q.p.sut) =(cuff cuff.p.q.p.sut))
~
`crib.p.q.p.sut
::
:: +arm-doc: returns arm doc of an arm
::
:: we just check if the $cuff is from a ++ or +$ arm but this will
:: probably need to be revisited once more sophisticated cuffs are used
++ arm-doc
|= [nom=term sut=type]
^- what
?~ (hint-doc [%funk nom]~ sut)
(hint-doc [%plan nom]~ sut)
(hint-doc [%funk nom]~ sut)
::
:: +prod-doc: wrapper for +hint-doc with empty cuff
++ prod-doc
|= sut=type
^- what
(hint-doc ~ sut)
::
:: +buc-doc: checks if type is core and returns docs on $ arm if it exists
++ buc-doc
|= sut=type
^- what
?. ?=([%core *] sut)
~
?~ (find [%$]~ (sloe sut))
~
=/ sat=type (arm-type %$ sut)
?~ (arm-doc %$ sat)
(prod-doc sat)
(arm-doc %$ sat)
::
:: +arm-docs: grabs the docs for an arm.
::
:: there are three possible places with relevant docs for an arm:
:: docs for the arm itself, docs for the product of the arm, and
:: if the arm builds a core, docs for the default arm of that core.
::
:: .adoc: docs written above the the arm
:: .pdoc: docs for the product of the arm
:: .cdoc: docs for the default arm of the core produced by the arm
++ arm-docs
|= [nom=term sut=type]
^- [what what what]
?> ?=([%core *] sut)
=/ sat=type (~(play ut sut) (arm-hoon nom sut))
=/ adoc=what (arm-doc nom sat)
=/ pdoc=what
?~ adoc
(prod-doc sat)
?> ?=([%hint *] sat)
(prod-doc q.sat)
=/ cdoc=what
?~ adoc
?~ pdoc
(buc-doc sat)
?> ?=([%hint *] sat)
(buc-doc q.sat)
?~ pdoc
?> ?=([%hint *] sat)
(buc-doc q.sat)
?> &(?=([%hint *] sat) ?=([%hint *] q.sat))
(buc-doc q.q.sat)
[adoc pdoc cdoc]
::
:: +arm-and-chapter-overviews: returns an overview of a core's contents
::
:: returns an overview for arms which are part of unnamed chapters, and
:: an overview of the named chapters
::
++ arm-and-chapter-overviews
|= =item
^- [overview overview]
?> &(?=([%core *] item) ?=([%core *] sut.item))
=| [adocs=overview cdocs=overview]
=/ tomes ~(tap by q.r.q.sut.item)
|-
?~ tomes
[(sort-overview adocs) (sort-overview cdocs)]
?~ p.i.tomes
:: chapter has no name. add documentation for its arms to arm-docs
=. adocs (weld adocs (tome-as-overview q.i.tomes sut.item))
$(tomes t.tomes)
:: chapter has a name. add to list of chapters
=. cdocs
%+ weld cdocs
^- overview
[%item :(weld "^" name.item "|" (trip -.i.tomes)) p.q.i.tomes]~
$(tomes t.tomes)
::
:: +arms-in-chapter: returns an overview of the arms in a specific chapter
++ arms-in-chapter
|= [sut=type tom=tome]
^- overview
(sort-overview (tome-as-overview tom sut))
::
:: +sort-overview: sort items in an overview in alphabetical order
++ sort-overview
|= ovr=overview
^- overview
%+ sort ovr
|= [lhs=overview-item rhs=overview-item]
(aor (get-overview-name lhs) (get-overview-name rhs))
::
:: +get-overview-name: returns the name of an overview
++ get-overview-name
|= ovr=overview-item
?- ovr
[%header *] ""
[%item *] name.ovr
==
::
:: +tome-as-overview: translate a tome into an overview
++ tome-as-overview
|= [tom=tome sut=type]
^- overview
%+ turn ~(tap by q.tom)
|= ar=(pair term hoon)
:* %item
::TODO make this distinguish between ++ and +$ arms
(weld "+" (trip p.ar))
=/ adoc (arm-doc p.ar (~(play ut sut) q.ar))
=/ pdoc (prod-doc (~(play ut sut) q.ar))
?~ adoc
pdoc
adoc
==
::
:: +item-as-overview: changes an item into an overview
++ item-as-overview
|= uit=(unit item)
~? >> debug %item-as-overview
^- overview
?~ uit ~
=+ itm=(need uit)
?- itm
[%view *] items.itm
::
[%core *]
?~ name.itm
(item-as-overview children.itm)
:- [%item (weld "^" name.itm) docs.itm]
(item-as-overview children.itm)
::
[%arm *]
:_ ~
::TODO make this distinguish between ++ and +$ arms
:* %item (weld "+" name.itm)
?~ adoc.itm
?~ pdoc.itm
cdoc.itm
pdoc.itm
adoc.itm
==
::
[%chapter *]
[%item (weld "|" name.itm) docs.itm]~
::
[%face *]
?~ name.itm
~
[%item (weld "." name.itm) docs.itm]~
==
::
:: +join-items: combines two (unit items) together
++ join-items
|= [lhs=(unit item) rhs=(unit item)]
^- (unit item)
?~ lhs rhs
?~ rhs lhs
`[%view (weld (item-as-overview lhs) (item-as-overview rhs))]
--
:: XX: non-std
++ post-href
|= =post:neo
^- path
?> ?=(@ q.post)
=/ dsk
/neo/hawk/out/std
%+ welp dsk
/[p.post]/[q.post]
::
++ apex
;div
;+
?~ cache.fod
empty
(have u.cache.fod)
==
++ empty
;h4: No build result here
++ fallback
|= vax=vase
;div
;*
=/ arms (sloe p.vax)
%+ turn arms
|= a=term
^- manx
(desc-arm a p.vax)
==
++ over-to-manx
|= ove=overview:dprint
^- manx
;div
;*
%+ turn ove
|= tem=overview-item:dprint
^- manx
(over-item-to-manx tem)
==
++ what-to-manx
|= wat=what
^- manx
?~ wat
;div: Nothing here
;div.what
;h6: {(trip p.u.wat)}
;div
;* ^- (list manx)
%- zing
^- (list (list manx))
%+ turn q.u.wat
|= sec=sect
^- (list manx)
%+ turn sec
|= pic=pica
^- manx
?: p.pic
;div: {(trip q.pic)}
;code.pre: {(trip q.pic)}
==
==
++ over-item-to-manx
|= tem=overview-item:dprint
^- manx
?- -.tem
%header
;div.over-item-head
;+ (what-to-manx doc.tem)
;+ (over-to-manx children.tem)
==
::
%item
;div.over-item-item
;h6: {name.tem}
;+ (what-to-manx doc.tem)
==
==
++ desc-arm
|= [arm=term sut=type]
^- manx
=/ tem (find-item-in-type:dprint ~[arm] sut)
~& tem/tem
(over-to-manx (item-as-overview:dprint tem))
++ have
|= vax=vase
=/ fim=(unit firm:neo)
(mole |.(!<(firm:neo vax)))
?> ?=(^ fim)
;div.p2.fc.g2
;h4: Shrub implementation
;h5: State
;button.b1.br1.p2.hover.wfc
=hx-get (spud (post-href %pro state:u.fim))
=hx-target "closest ha-wk"
=hx-swap "innerHTML"
; {<state:u.fim>}
==
;h5: Pokes
;div.frw.g2
;*
%+ turn ~(tap in poke:u.fim)
|= =stud:neo
^- manx
;button.p2.br1.b1.hover
=hx-get (spud (post-href %pro stud))
=hx-target "closest ha-wk"
=hx-swap "innerHTML"
; {<stud>}
==
==
==
--

View File

@ -0,0 +1,70 @@
/@ htmx
:- [%hoon %htmx]
|= hon=@t
|= =bowl:neo
=/ =name:neo
[our here]:bowl
=/ =file:ford:neo
(scan (trip hon) (rein:ford:neo name))
=/ src=wain
(to-wain:format hon)
^- manx
=< apex
|%
:: XX: non-std
++ post-href
|= =post:neo
^- path
?> ?=(@ q.post)
=/ dsk
/neo/hawk/src/std
%+ welp dsk
/[p.post]/[q.post]
::
++ apex
^- manx
;div.scroll-x.p3
;+ imports
;+ contents
==
++ imports
;div.p2
;div.p2.border.br1.fc.g2
;h4.bold: Protocols
;div.frw.g2
;*
%+ turn pro.file
|= =pro:ford:neo
^- manx
;a.p2.br1.b1.hover
=hx-get (spud (post-href %pro stud.pro))
=hx-target "closest ha-wk"
=hx-swap "innerHTML"
; {<stud.pro>}
==
==
==
==
++ contents
;form.fc.g2.wf.relative.p2
=hx-put "{(en-tape:pith:neo (welp /neo/hawk here.bowl))}?stud=hoon"
;div.p2.wf.z1
=style "position: sticky; top: 0; right: 0;"
;button.p2.br1.b1.hover.loader.wf
;span.loaded: save
;span.loading: ...
==
==
;textarea.p2.border.br1.ma.scroll-x.pre.mono.wf
=style "max-width: 650px;"
=autocomplete "off"
=rows "4"
=name "text"
=oninput "this.setAttribute('value', this.value); this.rows = this.value.split('\\n')"
;*
%+ turn src
|= lin=@t
;/ "{(trip lin)}\0a"
==
==
--

View File

@ -0,0 +1,36 @@
/@ iframe
:- [%iframe %htmx]
|= =iframe
|= =bowl:neo
^- manx
|^ shell
::
++ shell
::
=/ url (trip iframe)
;div.wf.hf.fc
=label "iframe"
=here (en-tape:pith:neo here.bowl)
;form.fr
=hx-post "/neo/hawk{(en-tape:pith:neo here.bowl)}?stud=iframe"
=hx-swap "none"
=style "border-bottom: 1px solid #777;"
;input.p2.grow
=type "text"
=value url
=is "atom-input"
=oninput "this.parentNode.nextElementSibling.src = this.value;"
;
==
;button.p2.b1
; Save
==
==
;iframe.wf.hf
=style "background-color: #eee;"
=id "iframe-{url}"
=src url
;
==
==
--

View File

@ -0,0 +1,8 @@
/@ node
/@ chat-diff
:- [%node %chat-diff]
|= nod=node
^- chat-diff
=/ title-input (snag 0 c.nod)
=/ title (crip (~(got by (malt a.g.title-input)) 'value'))
[%title title]

View File

@ -0,0 +1,14 @@
/@ node
/@ diary-diff
:- [%node %diary-diff]
|= nod=node
^- diary-diff
=/ head (@tas (crip (~(got by (malt a.g.nod)) %head)))
=/ id-el (snag 0 c.nod)
=/ text-el (snag 1 c.nod)
=/ id
%+ slav %da
(crip (~(got by (malt a.g.id-el)) %value))
=/ text
(crip (~(got by (malt a.g.text-el)) %value))
[%put-entry id text]

View File

@ -0,0 +1,8 @@
/@ hoon
/@ node
:- [%node %hoon]
|= nod=node
^- hoon
=/ text (snag 1 c.nod)
%- crip
(~(got by (malt a.g.text)) %value)

View File

@ -0,0 +1,7 @@
/@ node
/@ iframe
:- [%node %iframe]
|= nod=node
^- iframe
=/ el (snag 0 c.nod)
(crip (~(got by (malt a.g.el)) %value))

View File

@ -0,0 +1,11 @@
/@ sky
/@ node
:- [%node %sky]
|= nod=node
^- sky
:+ %system
%+ turn c.nod
|= =manx
%- pave:neo
(stab (crip (~(gut by (malt a.g.manx)) %here "/home")))
(fall (slaw %ud (crip (~(gut by (malt a.g.nod)) %slots "0"))) 0)

View File

@ -0,0 +1,7 @@
/@ node
/@ txt
:- [%node %txt]
|= nod=node
^- txt
=/ el (snag 0 c.nod)
(crip (~(got by (malt a.g.el)) %value))

View File

@ -0,0 +1,91 @@
/@ sky
:- [%sky %htmx]
|= =sky
|= =bowl:neo
^- manx
|^
shell
::
++ theme-dark
::
%- trip
'''
:root {
--b0: #222;
--b1: #333;
--b2: #444;
--b3: #555;
--be: #752;
--b-success: #351;
--f1: #ccc;
--f2: #999;
--f3: #777;
--f4: #555;
--f-error: orange;
--f-success: lightgreen;
--link: lightblue;
--hover: 115%;
}
'''
::
++ theme-light
::
%- trip
'''
:root {
--f1: #333;
--f2: #555;
--f3: #777;
--f4: #999;
--f-error: #953;
--f-success: #351;
--b0: #eee;
--b1: #ccc;
--b2: #bbb;
--b3: #888;
--b-error: #ca8;
--b-success: #8c8;
--link: blue;
--hover: 87%;
}
'''
::
++ theme-system
::
"""
{theme-light}
@media (prefers-color-scheme: dark) \{
{theme-dark}
}
"""
::
++ shell
=/ theme theme.sky
=/ colors
?: =(theme %light) theme-light
?: =(theme %dark) theme-dark
theme-system
::
;s-k-y.wf.hf(open "", hawks "{<slots.sky>}")
;*
=< p
%^ spin hawks.sky
1
|= [=pith a=@]
:_ +(a)
;ha-wk
=slot "s{<a>}"
=here (en-tape:pith:neo pith)
;div
=hx-get "/neo/hawk{(en-tape:pith:neo pith)}"
=hx-trigger "load"
=hx-target "this"
=hx-swap "outerHTML"
;div.wf.hf.fc.jc.ac.f2
; loading . . .
==
==
==
;style: {colors}
==
--

View File

@ -0,0 +1,19 @@
/@ txt
:- [%txt %htmx]
|= =txt
|= =bowl:neo
;form.fc.p2
=here (en-tape:pith:neo here.bowl)
=hx-post "/neo/hawk{(en-tape:pith:neo here.bowl)}?stud=txt"
=hx-trigger "input changed delay:0.4s from:find textarea"
=hx-swap "none"
;textarea.wf.p2.border.br1.ma
=style "max-width: 650px;"
=autocomplete "off"
=rows "4"
=name "text"
=oninput "this.setAttribute('value', this.value);"
::=is "multiline-input"
; {(trip (@t txt))}
==
==

View File

@ -0,0 +1,41 @@
/@ txt
/@ diary
/@ diary-diff
::
^- firm:neo
|%
++ state %diary
++ poke (sy %diary-diff ~)
++ kids
%- ~(gas by *kids:neo)
:~ :- ~[|/%da]
[%txt %sig]
==
++ deps *deps:neo
++ form
^- form:neo
|_ [=bowl:neo =ever:neo state-vase=vase *]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo vase)
?> =(%diary-diff stud)
=/ poke (diary-diff !<(diary-diff vax))
=/ sta (diary !<(diary state-vase))
?> =(our ship.src):bowl
=^ cards=(list card:neo) sta
?- -.poke
%put-entry
:_ sta
:~
:- (welp here.bowl ~[da/id.poke])
^- note:neo
[%make %txt `!>(txt.poke) ~]
==
%del-entry `sta
==
[cards !>(sta)]
++ init
|= old=(unit vase)
`(need old)
--
--

View File

@ -0,0 +1,20 @@
/@ iframe
|%
++ state %iframe
++ poke (sy %iframe ~)
++ kids *kids:neo
++ deps *deps:neo
++ form
^- form:neo
|_ [=bowl:neo =ever:neo state-vase=vase *]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo vase)
?> =(%iframe stud)
=/ new (iframe !<(iframe vax))
`!>(new)
++ init
|= vas=(unit vase)
`(need vas)
--
--

View File

@ -0,0 +1,20 @@
/@ sky
|%
++ state %sky
++ poke (sy %sky ~)
++ kids *kids:neo
++ deps *deps:neo
++ form
^- form:neo
|_ [=bowl:neo =ever:neo state-vase=vase *]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo vase)
?> =(%sky stud)
=/ new (sky !<(sky vax))
`!>(new)
++ init
|= vas=(unit vase)
`(need vas)
--
--

View File

@ -0,0 +1,20 @@
/@ txt
|%
++ state %txt
++ poke (sy %txt ~)
++ kids *kids:neo
++ deps *deps:neo
++ form
^- form:neo
|_ [=bowl:neo =ever:neo state-vase=vase *]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo vase)
?> =(%txt stud)
=/ new (txt !<(txt vax))
`!>(new)
++ init
|= vas=(unit vase)
`(need vas)
--
--

View File

@ -0,0 +1,4 @@
/@ txt
$% [%put-entry id=@da =txt]
[%del-entry id=@da]
==

View File

@ -0,0 +1 @@
,[name=@t]

View File

@ -0,0 +1 @@
cord

View File

@ -0,0 +1,5 @@
$:
theme=?(%light %dark %system)
hawks=(list pith)
slots=@
==

View File

@ -0,0 +1 @@
cord

236
pkg/arvo/web/a-i-r.js Normal file
View File

@ -0,0 +1,236 @@
customElements.define('a-i-r',
class extends HTMLElement {
static get observedAttributes() {
//
return ["closed", "hawks"];
}
constructor() {
//
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
* {
box-sizing: border-box;
}
::slotted(*) {
overflow: auto;
}
button {
border: none;
}
:host {
display: grid;
width: 100%;
height: 100%;
max-height: 100%;
overflow: hidden;
margin: 0;
grid-template-columns: 230px auto;
grid-template-rows: 50px auto;
grid-template-areas:
"btn main"
"nav main";
}
:host(.closed) {
grid-template-columns: 50px auto;
grid-template-rows: 50px auto;
grid-template-areas:
"btn main"
". main";
}
:host(.closed) #nav {
display: none;
}
#nav {
grid-area: nav;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
gap: 12px;
overflow: auto;
}
main.s0 {
grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-template-areas:
".";
}
main.s0 #s1,
main.s0 #s2,
main.s0 #s3,
main.s0 #s4 {
display: none;
}
main.s1 {
grid-template-columns: 1fr;
grid-template-rows: 1fr;
grid-template-areas:
"s1";
}
main.s1 #s1 {
display: block;
}
main.s1 #s2,
main.s1 #s3,
main.s1 #s4 {
display: none;
}
main.s2 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
grid-template-areas:
"s1 s2";
}
main.s2 #s1,
main.s2 #s2 {
display: block;
}
main.s2 #s3,
main.s2 #s4 {
display: none;
}
main.s3 {
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
grid-template-areas:
"s1 s2"
"s1 s3";
}
main.s3 #s1,
main.s3 #s2,
main.s3 #s3 {
display: block;
}
main.s3 #s4 {
display: none;
}
main.s4 {
grid-template-columns: 2fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
grid-template-areas:
"s1 s2 s2"
"s1 s3 s4";
}
main.s4 #s1,
main.s4 #s2,
main.s4 #s3,
main.s4 #s4 {
display: block;
}
button {
grid-area: btn;
}
main {
display: grid;
grid-area: main;
overflow: hidden;
}
#s1, #s2, #s3, #s4 {
overflow: auto;
}
#s1 {
grid-area: s1;
}
#s2 {
grid-area: s2;
}
#s3 {
grid-area: s3;
}
#s4 {
grid-area: s4;
}
button {
background-color: var(--b1);
color: var(--f1);
margin: 8px;
border: 1px solid var(--f4);
border-radius: 4px;
}
@media (max-width: 900px) {
:host {
grid-template-columns: auto;
grid-template-rows: auto 50px;
grid-template-areas:
"main"
"btn";
}
:host(.closed) {
grid-template-columns: auto;
grid-template-rows: auto 50px;
grid-template-areas:
"nav"
"btn";
}
:host(.closed) main {
display: none;
}
:host(:not(.closed)) main {
display: grid;
grid-template-columns: auto;
grid-template-rows: auto;
grid-template-areas:
"s1";
}
#s1 {
display: block;
}
main #s2 {
display: none !important;
}
main #s3 {
display: none !important;
}
main #s4 {
display: none !important;
}
:host(.closed) #nav {
display: flex;
}
:host(:not(.closed)) #nav {
display: none;
}
}
</style>
<slot name="button">
<button
onclick="this.getRootNode().host.dispatchEvent(new CustomEvent('sky-open', {bubbles:true, composed: true}))"
>
~
</button>
</slot>
<slot id="nav" name="nav"></slot>
<main>
<slot name="s1" id="s1"></slot>
<slot name="s2" id="s2"></slot>
<slot name="s3" id="s3"></slot>
<slot name="s4" id="s4"></slot>
</main>
<slot id="default" style="display: none;"></slot>
`
}
get hawks() {
return parseInt(this.getAttribute("hawks") || "0");
}
qs(sel) {
return this.shadowRoot.querySelector(sel);
}
gid(id) {
return this.shadowRoot.getElementById(id);
}
connectedCallback() {
this.addEventListener("sky-open", (e) => {
this.toggleAttribute("closed");
})
}
attributeChangedCallback(name, oldValue, newValue) {
//
if (name === "closed") {
this.classList.toggle("closed");
} else if (name === "hawks") {
this.qs("main").className = `s${this.hawks}`;
}
}
});

View File

@ -0,0 +1,26 @@
customElements.define('atom-input',
class extends HTMLInputElement {
static get observedAttributes() {
return ['value'];
}
constructor() {
super();
}
connectedCallback() {
this.addEventListener("input", (e) => {
this.mirrorValue();
});
this.mirrorValue();
}
mirrorValue() {
if (this.type === 'date') {
let d = this.value || "2000-01-01";
let year = d.slice(0,4);
let month = parseInt(d.slice(5,7));
let date = parseInt(d.slice(8,10));
this.setAttribute("value", `~${year}.${month}.${date}`);
} else {
this.setAttribute("value", this.value.trim());
}
}
}, { extends: "input" })

24
pkg/arvo/web/date-now.js Normal file
View File

@ -0,0 +1,24 @@
customElements.define('date-now',
class extends HTMLElement {
static get observedAttributes() {
return ['value'];
}
constructor() {
super();
}
connectedCallback() {
setInterval(() => {
let now = new Date();
let year = now.getFullYear();
let month = now.getMonth() + 1;
let date = now.getDate();
let hour = String(now.getHours()).padStart(2, '0');
let min = String(now.getMinutes()).padStart(2, '0');
let sec = String(now.getSeconds()).padStart(2, '0');
this.setAttribute(
"value",
`~${year}.${month}.${date}..${hour}.${min}.${sec}`
)
}, 1000);
}
});

View File

@ -0,0 +1,62 @@
customElements.define('error-tray',
class extends HTMLElement {
static get observedAttributes() {
return ["open"];
}
constructor() {
//
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.adoptedStyleSheets = [sharedStyles];
this.shadowRoot.innerHTML = `
<style>
:host {
display: none;
flex-flow: column nowrap;
width: 100%;
height: 100%;
background-color: var(--b1);
align-items: center;
justify-content: center;
top: 0;
left: 0;
opacity: 0.95;
}
</style>
<div
class="p3 wf hf fc g2 scroll-y"
style="max-width: 400px; max-height: 400px;"
>
<slot id="slot"></slot>
<button
class="b2 br1 p2 hover"
onclick="this.getRootNode().host.removeAttribute('open')"
>
close
</button>
</div>
`;
}
connectedCallback() {
this.gid("slot").addEventListener("slotchange", (e) => {
if (this.slotted("slot")) {
this.setAttribute("open", "");
} else {
this.removeAttribute("open");
}
})
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "open") {
this.style.display = newValue === null ? 'none' : 'flex';
}
}
slotted(id) {
//
return (this.gid(id)?.assignedElements() || [null])[0];
}
gid(id) {
//
return this.shadowRoot.getElementById(id);
}
});

291
pkg/arvo/web/ha-wk.js Normal file
View File

@ -0,0 +1,291 @@
customElements.define('ha-wk',
class extends HTMLElement {
static get observedAttributes() {
return ["stud", "here", "label", "tree-open"];
}
constructor() {
//
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.adoptedStyleSheets = [sharedStyles];
this.shadowRoot.innerHTML = `
<style>
:host {
display: flex;
height: 100%;
overflow: hidden;
flex-direction: column;
position: relative;
}
section {
position: sticky;
z-index: 10;
top: 0;
left: 0;
border: 2px solid var(--b3);
padding: 8px;
background-color: var(--b2);
}
#breadcrumbs {
display: flex;
flex-wrap: wrap;
gap: 4px;
flex-grow: 1;
background-color: var(--b2);
}
@media (max-width: 900px) {
#actions {
display: none;
}
}
</style>
<template id="button-template">
<button
class="b2 hover p1 br1 mono"
hx-target="closest ha-wk"
hx-swap="innerHTML"
slot="crumbs"
>
</button>
</template>
<section id="section" class="fc g2">
<nav class="fr g1">
<button
id="tree-btn"
class="b2 hover p1 br1 mono f2"
onclick="this.getRootNode().host.toggleAttribute('tree-open')"
slot="crumbs"
>
z
</button>
<button
class="b2 hover p1 br1 mono f2"
onclick="this.getRootNode().host.toggleMore(event)"
slot="crumbs"
>
#
</button>
<div id="breadcrumbs">
<slot id="btns" name="crumbs"></slot>
</div>
<div id="actions" class="f2">
<button
class="b2 hover p1 br1 mono"
onclick="this.getRootNode().host.clone(event)"
slot="crumbs"
>
c
</button>
<button
class="b2 hover p1 br1 mono"
onclick="this.getRootNode().host.raise(event)"
slot="crumbs"
>
&lt;
</button>
<button
class="b2 hover p1 br1 mono"
onclick="this.getRootNode().host.drop(event)"
slot="crumbs"
>
&gt;
</button>
<button
class="b2 hover p1 br1 mono"
onclick="this.getRootNode().host.burry(event)"
slot="crumbs"
>
_
</button>
<button
class="b2 hover p1 br1 mono"
onclick="this.getRootNode().host.suicide()"
slot="crumbs"
>
X
</button>
</div>
</nav>
<aside id="aside" class="hidden p3 b3 fc g3 br1">
<div class="frw g2">
<button
onclick="this.getRootNode().host.inspect()"
class="b3 hover p1 br1 mono"
id="stud"
></button>
</div>
</aside>
</div>
</section>
<div id="tree" class="hidden grow scroll-y scroll-x p2 b2">
<slot name="tree" id="tree-slot">Tree view</slot>
</div>
<slot id="slot">Nothing here</slot>
`;
}
connectedCallback() {
//
let treed = false;
this.gid("slot").addEventListener("slotchange", (e) => {
let nodes = e.target.assignedNodes();
let btns = this.gid("breadcrumbs");
this.gid("btns").assignedNodes().forEach(n => n.remove())
if (nodes.length) {
let wrapped = nodes[0];
if (wrapped.hasAttribute("empty")) {
this.setAttribute("tree-open", "")
} else {
this.removeAttribute("tree-open");
}
this.setAttribute("stud", wrapped.getAttribute("stud"));
if (wrapped.hasAttribute("here")) {
let here = wrapped.getAttribute("here");
here = here === "/" ? "" : here;
let label = wrapped.getAttribute("label");
this.setAttribute("here", here);
if (label) {
this.setAttribute("label", label)
} else {
this.removeAttribute("label");
}
//
let segments = here.split("/");
segments.forEach((s, i) => {
let btn = this.gid('button-template').content.cloneNode(true);
btn = btn.querySelector('button');
let dest = segments.slice(0, i+1).join("/")
btn.setAttribute("hx-get", `/neo/hawk${dest}`)
btn.textContent = s + "/";
this.appendChild(btn);
})
let tree = document.createElement("div");
let treeStub = document.createElement("div");
treeStub.setAttribute("hx-get", `/neo/hawk${here}?tree`);
treeStub.setAttribute("hx-target", `this`);
treeStub.setAttribute("hx-trigger", `load`);
treeStub.setAttribute("hx-swap", `outerHTML`);
tree.setAttribute("slot", "tree");
tree.appendChild(treeStub);
this.tree?.remove();
this.appendChild(tree);
htmx.process(document.body);
} else {
this.removeAttribute("here");
this.removeAttribute("label");
}
} else {
this.setAttribute("tree-open", "");
}
const event = new CustomEvent('here-change', {composed: true});
this.dispatchEvent(event);
})
}
attributeChangedCallback(name, oldValue, newValue) {
//
if (name === "stud") {
this.gid("stud").textContent = `%${newValue}`
} else if (name === "here") {
const event = new CustomEvent('true', {
bubbles: true,
composed: true,
});
this.dispatchEvent(event);
} else if (name === "label") {
const event = new CustomEvent('true', {
bubbles: true,
composed: true,
});
this.dispatchEvent(event);
} else if (name === "tree-open") {
let open = newValue !== null;
if (open) {
this.slotted.classList.add("hidden");
/* this.gid("section").classList.add("grow"); */
this.gid("tree").classList.remove("hidden");
this.gid("tree-btn").classList.add("b3");
} else {
this.slotted.classList.remove("hidden");
/* this.gid("section").classList.remove("grow"); */
this.gid("tree").classList.add("hidden");
this.gid("tree-btn").classList.remove("b3");
}
}
}
gid(id) {
//
return this.shadowRoot.getElementById(id);
}
get slotted() {
return (this.gid("slot").assignedNodes() || [null])[0]
}
get tree() {
return (this.gid("tree-slot").assignedNodes() || [null])[0]
}
inspect() {
const event = new CustomEvent('inspect-hawk', {
bubbles: true,
composed: true,
detail: {
stud: this.getAttribute("stud")
}
});
this.dispatchEvent(event);
}
toggleMore(e) {
this.gid("aside").classList.toggle("hidden");
e.target.classList.toggle("b3");
}
clone() {
const event = new CustomEvent('clone-hawk', {
bubbles: true,
composed: true,
detail: {
here: this.getAttribute("here"),
slot: this.getAttribute("slot"),
}
});
this.dispatchEvent(event);
}
burry() {
this.parentNode.insertAdjacentElement("beforeend", this);
const event = new CustomEvent('cull', {
bubbles: true,
composed: true,
});
this.dispatchEvent(event);
}
drop() {
this.nextElementSibling?.insertAdjacentElement("afterend", this);
const event = new CustomEvent('true', {
bubbles: true,
composed: true,
});
this.dispatchEvent(event);
}
raise() {
this.previousElementSibling?.insertAdjacentElement("beforebegin", this);
const event = new CustomEvent('true', {
bubbles: true,
composed: true,
});
this.dispatchEvent(event);
}
shoot() {
this.parentNode.insertAdjacentElement("afterbegin", this);
const event = new CustomEvent('true', {
bubbles: true,
composed: true,
});
this.dispatchEvent(event);
}
suicide() {
let par = this.parentNode;
this.remove();
const event = new CustomEvent('true', {
bubbles: true,
composed: true,
});
par.dispatchEvent(event);
}
})

View File

@ -0,0 +1,30 @@
customElements.define('multiline-input',
class extends HTMLTextAreaElement {
static get observedAttributes() {
return ['value'];
}
constructor() {
super();
}
connectedCallback() {
this.autocomplete = "off";
this.style.resize = 'none';
this.style.display = 'flex';
this.mirrorValue();
this.adjustSize();
this.addEventListener("input", (e) => {
this.mirrorValue();
this.adjustSize();
});
setTimeout(() => {this.adjustSize()}, 100);
}
mirrorValue() {
let v = this.value.trimEnd();
this.setAttribute("value", v);
}
adjustSize() {
this.style.height = 'min-content';
this.style.height = (this.scrollHeight+3)+'px';
}
}, { extends: "textarea" })

279
pkg/arvo/web/s-k-y.js Normal file
View File

@ -0,0 +1,279 @@
customElements.define('s-k-y',
class extends HTMLElement {
static get observedAttributes() {
//
return ["hawks"];
}
constructor() {
//
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.adoptedStyleSheets = [sharedStyles];
this.shadowRoot.innerHTML = `
<style>
:host {
width: 100%;
height: 100%;
}
</style>
<a-i-r class="wf hf">
<nav slot="nav" class="wf hf scroll-y fc g2 p2">
<button
class="p2 tc hover br1 b1"
onclick="this.getRootNode().host.newTab()"
>
+
</button>
<div class="fr g2">
<button
class="p1 br1 b1 grow hover"
onclick="this.getRootNode().host.cull()"
>
-
</button>
<button
class="p1 br1 b1 grow hover"
onclick="this.getRootNode().host.grow()"
>
+
</button>
</div>
<div id="tabs" class="fc g1 wf">
<template id="tab-template">
<div class="mono fr b1 br1" style="cursor: pointer;">
<button
class="action p2 b1 hover br1 hidden"
>+</button>
<button
class="action p2 b1 hover grow tl br1"
></button>
<button
class="action p2 hover br1"
>x</button>
</div>
</template>
</div>
</nav>
<slot slot="s1" name="s1"></slot>
<slot slot="s2" name="s2"></slot>
<slot slot="s3" name="s3"></slot>
<slot slot="s4" name="s4"></slot>
<slot style="display: none;"></slot>
</a-i-r>
`;
}
connectedCallback() {
//
if (this.isMobile) {
this.className = "fc grow wf af"
} else {
this.className = "fr wf hf"
}
this.gid("s0")?.addEventListener("slotchange", () => {this.reactSlot("s0")});
this.gid("s1")?.addEventListener("slotchange", () => {this.reactSlot("s1")});
this.gid("s2")?.addEventListener("slotchange", () => {this.reactSlot("s2")});
this.gid("s3")?.addEventListener("slotchange", () => {this.reactSlot("s3")});
this.gid("default")?.addEventListener("slotchange", (e) => this.reactDefault(e));
this.addEventListener("here-change", (e) => {
this.updateTabs();
this.syncTabs();
})
this.addEventListener("cull", (e) => {
this.cull();
this.updateTabs();
});
this.addEventListener("true", (e) => {
this.trueSlots();
this.updateTabs();
this.syncTabs();
});
this.addEventListener("grow", (e) => {
this.grow();
this.updateTabs();
});
this.addEventListener("clone-hawk", (e) => {
let here = e.detail.here;
let slot = e.detail.slot;
this.newTab(`/neo/hawk${here}`, slot);
this.updateTabs();
})
this.addEventListener("inspect-hawk", (e) => {
let stud = e.detail.stud
if (stud) {
this.newTab(`/neo/hawk/src/std/imp/${stud}`);
}
this.updateTabs();
})
}
attributeChangedCallback(name, oldValue, newValue) {
//
/* if (name === "open") { */
/* this.gid("aside").classList.toggle("hidden"); */
/* if (this.isMobile) { */
/* if (newValue === null) { */
/* this.gid("cs0").classList.remove("hidden"); */
/* } else { */
/* this.gid("cs0").classList.add("hidden"); */
/* } */
/* } else { */
/* this.gid("cs0").classList.remove("hidden"); */
/* } */
/* } else if (name === "hawks") { */
if (name === "hawks") {
this.qs("a-i-r").setAttribute("hawks", newValue);
this.trueSlots();
this.updateTabs();
}
}
get isMobile() {
return (window.innerWidth < 900)
}
get hawks() {
return parseInt(this.getAttribute("hawks") || "0");
}
get slottedHawks() {
return [...this.childNodes].filter(c => c.nodeName === 'HA-WK');
}
qs(sel) {
return this.shadowRoot.querySelector(sel);
}
gid(id) {
//
return this.shadowRoot.getElementById(id);
}
slotted(id) {
//
return (this.gid(id)?.assignedElements() || [null])[0];
}
trueSlots() {
//
let kids = [...this.childNodes].filter(c => c.nodeName === 'HA-WK');
let hawks = parseInt(this.getAttribute("hawks") || "1");
kids.forEach((k, i) => {
if (i < hawks) {
k.setAttribute("slot", `s${i+1}`)
} else {
k.removeAttribute("slot");
}
})
this.updateTabs();
}
reactSlot(id) {
//
let s = this.slotted(id);
let c = this.gid("c"+id);
let slot = parseInt(id.slice(1));
if (s && slot <= this.hawks) {
// display hawk
s.classList.add("b0");
s.classList.add("fc");
s.classList.add("js");
s.classList.add("scroll-y");
//
s.classList.add("grow");
c.classList.add("grow");
//
s.classList.add("basis-half");
c.classList.add("basis-half");
//
s.classList.remove("basis-none");
c.classList.remove("basis-none");
//
s.classList.remove('hidden');
c.classList.remove('hidden');
} else {
// hide hawk
if (s) {
s.classList.remove("grow");
s.classList.add("basis-none");
s.classList.remove("basis-half");
s.classList.add('hidden');
}
c.classList.remove("grow");
c.classList.add("basis-none");
c.classList.remove("basis-half");
c.classList.add('hidden');
}
this.updateTabs();
}
reactDefault(e) {
//
this.updateTabs();
}
updateTabs() {
//
let frames = [...this.childNodes].filter(c => c.nodeName === 'HA-WK');
let tabs = this.gid("tabs");
tabs.querySelectorAll('div').forEach(x => x.remove());
frames.forEach((f, i) => {
let here = f.getAttribute('here');
let label = f.getAttribute('label');
let open = f.hasAttribute('slot');
let tab = this.gid('tab-template').content.cloneNode(true);
tab = tab.querySelector('div');
if (open) {
tab.classList.add('active');
}
let add = tab?.firstElementChild;
let sel = add.nextElementSibling;
let del = sel.nextElementSibling;
sel.textContent = label || here || "/";
sel.addEventListener('click', (e) => {
e.preventDefault();
this.insertAdjacentElement("afterbegin", f);
this.trueSlots();
})
del.addEventListener('click', (e) => {
e.preventDefault();
f.suicide();
})
tabs.appendChild(tab);
})
}
newTab(here, slot) {
//
let hawk = document.createElement("ha-wk");
let stub = document.createElement("div");
stub.setAttribute("hx-get", here || "/neo/hawk/home");
stub.setAttribute("hx-target", "this");
stub.setAttribute("hx-swap", "outerHTML");
stub.setAttribute("hx-trigger", "load");
hawk.appendChild(stub);
hawk.setAttribute("here", "");
hawk.setAttribute("stud", "txt");
hawk.setAttribute("label", "Home");
let s = this.slotted(slot)
if (s) {
s.insertAdjacentElement("beforebegin", hawk);
} else {
this.insertAdjacentElement("afterbegin", hawk);
}
this.grow();
htmx.process(document.body);
this.updateTabs();
this.syncTabs();
}
cull() {
this.setAttribute("hawks", Math.max(0, this.hawks - 1));
this.trueSlots();
this.syncTabs();
}
grow() {
this.setAttribute("hawks", Math.min(4, this.slottedHawks.length, this.hawks + 1));
this.trueSlots();
this.syncTabs();
}
async syncTabs() {
let frames = [...this.childNodes].filter(c => c.nodeName === 'HA-WK');
let forms = frames.map(f => {
return `
<p here="${f.getAttribute('here')}"></p>
`
})
await fetch(`/neo/hawk/sky?stud=sky`, {
method: 'POST',
headers: {'content-type': 'text/html'},
body: `<div slots="${this.hawks}">${forms.join("")}</div>`
})
}
});

416
pkg/arvo/web/style.css Normal file
View File

@ -0,0 +1,416 @@
/** style.css
* ~2024.3.5
*
* styling resets
* and
* utility classes
*
**/
:root {
--x: 4px;
}
* {
box-sizing: border-box;
font: inherit;
color: inherit;
}
form, p, li, ul, ol, div, button, h1, h2, h3, h4, h5, h6 {
margin: 0;
}
html {
height: 100%;
font-size: 21px;
}
body {
margin: 0;
font-family: 'Urbit Sans';
font-feature-settings: normal;
font-variation-settings: normal;
letter-spacing: 0.024em;
line-height: 1;
background-color: var(--b0);
color: var(--f1);
min-height: 100%;
}
a {
cursor: pointer;
text-decoration: none;
}
button, input, textarea, iframe {
border: unset;
}
.break {
word-break: break-word;
}
.action {
touch-action: manipulation;
}
.hidden,
.folded {
display: none !important;
}
.jc {
justify-content: center;
}
.jb {
justify-content: space-between;
}
.ja {
justify-content: space-around;
}
.js {
justify-content: start;
}
.je {
justify-content: end;
}
.js {
justify-content: start;
}
.as {
align-items: start;
}
.af {
align-items: stretch;
}
.ae {
align-items: end;
}
.ac {
align-items: center;
}
.wfc {
width: fit-content;
}
.wf {
width: 100%;
}
.hf {
height: 100%;
}
.hfc {
height: fit-content;
}
.mono {
font-variation-settings: "xtab" 500;
font-size: 0.8em;
}
.bold {
font-weight: bold;
}
.pre {
white-space: pre;
}
.pre-line {
white-space: pre-line;
}
.tl {
text-align: left;
}
.tc {
text-align: center;
}
.tr {
text-align: right;
}
.block {
display: block;
}
.fc {
display: flex;
flex-direction: column;
}
.fcr {
display: flex;
flex-direction: column-reverse;
}
.fr {
display: flex;
flex-direction: row;
}
.frw {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.basis-full {
flex-basis: 100%;
}
.basis-half {
flex-basis: 50%;
flex-shrink: 0;
}
.basis-none {
flex-basis: 0%;
flex-shrink: 1;
}
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.fixed {
position: fixed;
}
.z-2 {
z-index: -20;
}
.z-1 {
z-index: -10;
}
.z0 {
z-index: 0;
}
.z1 {
z-index: 10;
}
.z2 {
z-index: 20;
}
.grow {
flex-grow: 1;
}
.g1 {
gap: 4px;
}
.g2 {
gap: 8px;
}
.g3 {
gap: 12px;
}
.g4 {
gap: 16px;
}
.g5 {
gap: 20px;
}
.g6 {
gap: 24px;
}
.g7 {
gap: 28px;
}
.p-1 {
padding: 0px 4px;
}
.p0 {
padding: 0;
}
.p1 {
padding: 4px;
}
.p2 {
padding: 8px;
}
.p3 {
padding: 12px;
}
.p4 {
padding: 16px;
}
.p-page {
padding-top: 30px;
padding-bottom: 200px;
padding-left: 12px;
padding-right: 12px;
}
.ma {
margin: auto;
}
.scroll-y {
overflow-y: auto;
}
.scroll-x {
overflow-x: auto;
}
.loader {
position: relative;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.loader .loading {
opacity: 0;
transition: opacity 300ms;
}
.htmx-request .loader .loading,
.loader.htmx-request .loading {
opacity: 1;
}
.loader .loaded {
opacity: 1;
transition: opacity 300ms;
}
.htmx-request .loader .loaded,
.loader.htmx-request .loaded {
opacity: 0;
}
.f1 {
color: var(--f1);
}
.f2 {
color: var(--f2);
}
.f3 {
color: var(--f3);
}
.f-success {
color: var(--f-success);
}
.f-error {
color: var(--f-error);
}
.b0 {
background-color: var(--b0);
}
.b1 {
background-color: var(--b1);
}
.b2 {
background-color: var(--b2);
}
.b3 {
background-color: var(--b3);
}
.b-success {
background-color: var(--b-success);
}
.b-error {
background-color: var(--b-error);
}
.s-2 {
font-size: 0.7em;
}
.s-1 {
font-size: 0.85em;
}
.s0 {
font-size: 1em;
}
.s1 {
font-size: 1.15em;
}
.s2 {
font-size: 1.3em;
}
.s3 {
font-size: 1.45em;
}
.s4 {
font-size: 1.6em;
}
.border {
border: 1.2px solid var(--f3);
}
.border-2 {
border: 1px solid var(--f4);
}
.br1 {
border-radius: 3px;
}
.br2 {
border-radius: 6px;
}
input,
select {
outline: none;
border-radius: 0;
background-color: inherit;
color: inherit;
}
textarea {
display: block;
resize: none;
outline: none;
border-radius: 0;
border: 0;
background-color: inherit;
color: inherit;
}
button {
cursor: pointer;
background-color: inherit;
color: inherit;
}
.hover:hover {
filter: invert(20%);
}
.toggled {
filter: invert(100%);
}
.hover.toggled:hover {
filter: invert(100%);
}
.active,
.selected {
filter: invert(10%);
}
.hover.active:hover,
.hover.selected:hover {
filter: invert(25%);
}
.prose h1 {
font-size: 1.45rem;
margin: 1rem 0;
}
.prose h2 {
font-size: 1.3rem;
margin: 1rem 0;
}
.prose h3 {
font-size: 1.15rem;
margin: 1rem 0;
}
.prose h1, .prose h2, .prose h3 {
font-weight: bold;
}
.prose p {
margin-bottom: 1rem;
line-height: 1.3;
}
.prose img {
max-width: 100%;
display: block;
max-height: 350px;
}
.prose details {
margin-bottom: 1rem;
}
.prose mono {
font-size: 1em;
}
.prose code {
white-space: pre;
overflow-x: auto;
width: 100%;
display: block;
padding: 8px;
}
.prose ul,
.prose ol {
padding-left: 19px;
line-height: 1.2;
margin-bottom: 1rem;
}
.prose ul p,
.prose ol p {
margin-bottom: 0;
line-height: 1.4;
}
.prose ul ul,
.prose ol ul,
.prose ul ol,
.prose ol ol {
margin-bottom: 0;
}
summary {
user-select: none;
cursor: pointer;
}