fix merge conflict and update task protocol with kids-done state

This commit is contained in:
hanfel-dovned 2024-06-07 15:15:19 -07:00
commit eb7fbda674
14 changed files with 1002 additions and 565 deletions

View File

@ -51,7 +51,6 @@
*manx
=. here.kid (snoc here.kid %in)
(!<(htmx q.pail.u.res) kid)
=/ out=manx
=/ out=manx
?~ res=(~(get of:neo kids.bowl) (snoc pax %out))
*manx
@ -63,7 +62,7 @@
=id "cell-{id}-{<x>}-{<y>}"
=hx-get "/neo/hawk{(en-tape:pith:neo here.bowl)}/{<x>}/{<y>}/in"
=hx-target "#dashboard-{id}"
=hx-select "form"
=hx-select ".trans-root"
=hx-swap "innerHTML"
=morph-retain "class"
=onclick "$('.cell-btn').removeClass('toggled');$(this).addClass('toggled');"

View File

@ -1,14 +1,25 @@
/@ diary
/@ diary :: name=@t
:: import /lib/feather-icons
/- feather-icons
:: declare that this is a conversion from diary to HTMX
:- [%diary %$ %htmx]
:: gate takes a diary and a bowl:neo,
:: so we can access here.bowl
|= dia=diary
|= =bowl:neo
:: returns a manx, in this case HTMX
^- manx
:: construct Sail using helper arms
|^
:: <div class="p2" label="Diary">
;div.p2
=label "Diary"
:: <div class="ma fc g2 mw-page">
;div.ma.fc.g2.mw-page
:: render the text input for new entries
;+ form-put-entry
:: compose several link-entry elements generated
:: by the +turn gate into one HTMX node
;*
%+ turn
%+ sort
@ -20,18 +31,38 @@
|= [a=[=pith *] b=[=pith *]]
(gth ->.pith.a ->.pith.b)
link-entry
==
==
== :: </div>
== :: </div>
::
:: text input for new entries
++ form-put-entry
::
:: <form
:: class="fc g2"
:: style="margin-bottom: 30px"
:: head="put-entry"
:: hx-post="/neo/hawk{(en-tape:pith:neo here.bowl)}?stud=diary-diff"
:: hx-on-submit="this.reset()"
:: hx-target="find .loading"
:: hx-swap="outerHTML"
:: >
;form.fc.g2
=style "margin-bottom: 30px;"
=hx-post "/neo/hawk{(en-tape:pith:neo here.bowl)}?stud=diary-diff"
=style "margin-bottom: 30px;"
=hx-post "/neo/hawk{(en-tape:pith:neo here.bowl)}?stud=diary-diff"
=hx-on-submit "this.reset()"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "put-entry"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "put-entry"
:: <date-now name="id" />
;date-now(name "id");
:: <textarea
:: class="p2 bd1 br1"
:: name="text"
:: placeholder="today, i ..."
:: oninput="this.setAttribute('value', this.value)"
:: rows="5"
:: required=""
:: autocomplete=""
:: />
;textarea.p2.bd1.br1
=name "text"
=placeholder "today, i ..."
@ -39,46 +70,68 @@
=rows "5"
=required ""
=autocomplete "off"
;
==
; :: XX unnecessary?
== :: </textarea>
:: <button class="p2 b1 br1 bd1 wfc hover loader">
;button.p2.b1.br1.bd1.wfc.hover.loader
:: <span class="loaded s2">create</span>
;span.loaded.s2: create
:: <span>
;span.loading
;+ loading.feather-icons
==
==
==
::
== :: </span>
== :: </button>
== :: </form>
::
:: entry box
++ link-entry
::
|= [pax=pith =idea:neo]
:: make sure the pail does in fact contain %txt
?. =(%txt p.pail.idea)
;div: error
:: extract information from the given pith and pail:neo
=/ tape (trip !<(@t q.pail.idea))
=/ subject-end (fall (find [10]~ tape) 56)
=/ subject (scag subject-end tape)
=/ id (trip (snag 0 (pout pax)))
:: <div class="fr g2">
;div.fr.g2
:: <a
:: class="p2 br1 grow b1 hover loader"
:: href="{(en-tape:pith:neo (weld /neo/hawk here.bowl))}/{id}"
:: >
;a.p2.br1.grow.b1.hover.loader
=href "{(en-tape:pith:neo (weld /neo/hawk here.bowl))}/{id}"
:: <div class="loaded fc g1 js as g2">
;div.loaded.fc.g1.js.as.g2
:: <span class="f3">{YYYY-MM-DD}</span>
;span.f3: {(pretty-date `@da`->:pax)}
:: <span class="bold">{subject}</span>
;span.bold: {subject}
==
== :: </div>
:: <span class="loading">
;span.loading
;+ loading.feather-icons
==
==
== :: </span>
== :: </a>
:: <button
:: class="p2 br1 fr g2 b1 hover fc ac jc loader"
:: onclick="alert('not yet implemented. no tombstoning?')"
:: >
;button.p2.br1.fr.g2.b1.hover.fc.ac.jc.loader
=onclick "alert('not yet implemented. no tombstoning?')"
:: <span class="loaded">
;span.loaded
;+ close.feather-icons
==
== :: </span>
:: <span class="loading">
;span.loading
;+ loading.feather-icons
==
==
==
== :: </span>
== :: </button>
== :: </div>
::
:: styled date string
++ pretty-date
|= date=@da
^- tape

View File

@ -1,11 +1,16 @@
/@ node
/@ diary-diff
/@ node :: manx
/@ diary-diff :: ?([%put-entry id=@da txt=@t] [%del-entry id=@da])
:: import /lib/manx-utils
/- manx-utils
:: declare that this is a conversion from a
:: dynamic XML node to diary-diff
:- [%node %$ %diary-diff]
|= nod=node
^- diary-diff
=/ mu ~(. manx-utils nod)
:: extract head, id, and text atttributes from XML node
=/ head (@tas (got:mu %head))
=/ id (slav %da (vol:mu "id"))
=/ text (vol:mu "text")
:: construct the diary-diff
[%put-entry id text]

View File

@ -28,8 +28,8 @@
=/ prepend (vol:mu "prepend")
?: =(prepend 'prepend')
:: construct the task-diff
[head [text | ~] &]
[head [text | ~] |]
[head [text | & ~] &]
[head [text | & ~] |]
::
%edit
:: extract text attribute from XML node

View File

@ -1,4 +1,4 @@
/@ task :: [text=cord done=? order=(list pith)]
/@ task :: [text=cord done=? kids-done=? order=(list pith)]
:: import /lib/feather-icons
/- feather-icons
:: declare that this is a conversion from task to HTMX
@ -108,14 +108,14 @@
++ kids-check
::
:: check if all subtasks are completed
|= [parent-pith=pith order=(list pith)]
|= =pith
~& > pith
^- ?
%+ levy order
|= p=pith
=/ =task
!< task
q.pail:(need (~(get by ~(tar of:neo kids.bowl)) (weld parent-pith p)))
done.task
=/ t
!< task
q.pail:(~(got of:neo kids.bowl) pith)
~& >> t
kids-done.t
::
::
++ form-ordered-kids
@ -131,14 +131,15 @@
::
:: iterates over the list of piths,
:: turning through the kids' data
%+ turn order.t
|= =pith
:: extract kid information at pith from kids.bowl
:: and runs +part-kid on pith and kid data
=/ kid (~(get of:neo kids.bowl) pith)
?~ kid
;div: does not exist {(pith-tape pith)}
(part-kid [pith (need kid)])
%+ turn
order.t
|= =pith
:: extract kid information at pith from kids.bowl
:: and runs +part-kid on pith and kid data
=/ kid (~(get of:neo kids.bowl) pith)
?~ kid
;div: does not exist {(pith-tape pith)}
(part-kid [pith (need kid)])
==
::
++ part-kid
@ -187,9 +188,8 @@
:: a task from being marked as done if it
:: has untoggled kids
::
:: combining m named noun and
:: attribute logic with manx below
=- =/ m -
:: combining attribute logic with manx below
=; m
:: checks if the task toggled as done
?. done.t
:: if it's not done, does it have kids?
@ -201,7 +201,8 @@
:: the rest of its data
m(a.g [class a.g.m])
=/ kc
(kids-check pith order.t)
(kids-check pith)
~& >>> kc
?: kc
:: assigning class attribute to
:: the rest of manx data
@ -233,10 +234,9 @@
;
==
::
:: combining that named noun and class name
:: logic with manx below and make it in to an XML node
;+ =-
=/ that -
:: combining class logic with
:: manx below and make it in to an XML node
;+ =; that
=/ classes
%+ weld
"grow p2 br2 text bold"

View File

@ -0,0 +1,302 @@
# Tutorial 2: Diary
Now that you understand the structure of a shrub, the natural next step is to look at a shrub with kids.
Unlike the Counter example, there is no equivalent way to implement `/imp/diary` in Gall. Were now going to work directly with Urbits programmable, global, referentially transparent namespace — which is a lot of words to say [`+axal`](https://docs.urbit.org/language/hoon/reference/arvo#axal) — as a tool to read and write data.
## /imp/diary
Diary is an app that lets you write timestamped diary entries that the frontend will show in a chronological feed. Heres a succinct version of the apps backend logic, which clocks in at 34 lines of code.
```hoon
:: /imp/diary.hoon
/@ txt :: @t
/@ diary :: name=@t
/@ diary-diff :: ?([%put-entry id=@da =txt] [%del-entry id=@da])
^- kook:neo
|%
++ state only/%diary
++ poke (sy %diary-diff ~)
++ kids `y/(~(gas by *lads:neo) ~[[[|/%da |] [only/%txt ~]]])
++ deps *deps:neo
++ form
^- form:neo
|_ [=bowl:neo =aeon:neo =pail:neo]
++ init
|= old=(unit pail:neo)
^- (quip card:neo pail:neo)
[~ ?~(old diary/!>(*diary) u.old)]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
?> =(%diary-diff stud)
=/ state !<(diary q.pail)
=/ act !<(diary-diff vax)
?> =(our ship.src):bowl
?- -.act
%put-entry
:_ diary/!>(state)
~[[(welp here.bowl ~[da/id.act]) [%make %txt `txt/!>(txt.act) ~]]]
%del-entry
:_ diary/!>(state)
~[[(welp here.bowl ~[da/id.act]) [%tomb ~]]]
==
--
--
```
Most of this should be legible after the first tutorial. The only new ideas are the `+kids` arm, the use of `bowl`, and `%tomb`.
## +kids
Every shrub is a node in one tree, with one “root shrub” at the top of that tree. Every shrub below the root shrub is either one of its immediate children or one of its descendants.
In its `+kids` arm, every shrub can define constraints for the shrubs below it in the namespace, whether thats constraining the types of their state or the pokes theyll accept from other shrubs.
Lets expand on diarys `+kids` arm.
```hoon
:: /imp/diary.hoon
::
:: constrain shrubs below diary in the namespace
:: by defining the types of their state and pokes
++ kids
::
:: kids:neo is a (unit port:neo)
^- kids:neo
%- some
::
:: port:neo is (pair dare:neo lads:neo)
:: dare:neo is ?(%y %z)
:: if %y, only constrain our immediate children
:: if %z, recursively constrain all descendants
:- %y
:: lads:neo is (map pish:neo lash:neo)
%- ~(gas by *lads:neo)
:~ :- ::
:: pish:neo
:: to simplify: [%.n @da] means the kid's
:: path contains any @da, and %.n is there
:: to signify that the pith can not have more
:: fields afterwards
[[%.n %da] %.n]
::
:: lash:neo is (pair curb:neo (set stud:neo))
:: curb:neo defines the kids' state
:: (set stud:neo) defines the kids' pokes
[[%only %txt] ~]
==
```
In this case, Diary only constrains its immediate children. Each of its children lives at `/path/to/diary/<@da>`, where `<@da>` is used as an ID for the entry, and those children are just `%txt`s (cords) which store the text of the diary entry.
Theres no need for Diary to recursively constrain every single path that nests under `/path/to/diary`, since the app only needs to reserve the first “generation” of shrubs below it in order to work. In this case, the developer has forbidden any shrubs being created under the diary entries by ending the `pish` in a `%.n`. If that were `%.y`, any app could make a shrub at `/path/to/diary/<@da>/comments`. or `/path/to/diary/<@da>/backlinks`.
To define the shrubs kids, we create an empty map which is the bunt of `lads:neo`, then use `+gas:by` to populate the map with our own data, which will be a `(list (pair pish:neo lash:neo))`.
The `pish:neo` statically types the paths which well allow to be created beneath this shrub, and the `lash:neo` defines the kids state and pokes. In this case, the kids take no pokes and their state can only be `%txt`.
(Its worth flagging that `$curb:neo` contains several combinatorial rules about state types. For example `[%pro %txt]` would mean “the state of the child shrub can be any type which is readily convertible into a `%txt` with a conversion we have available in the `/con` folder”. This gives us a clue as to how we could handle state transitions and interoperability over the lifetime of our shrub, but its outside the remit of this tutorial.)
## bowl:neo
Notice that the `src` in `bowl:neo` differs from `bowl:gall`. Heres the new type in full.
```hoon
:: /sur/neo.hoon
+$ bowl
$: src=[=ship =pith] :: a request's source ship and shrub
our=@p :: our ship
here=pith :: path to this shrub
now=@da :: current datetime
eny=@uvJ :: entropy
deps=(map term (pair pith lore)) :: dependencies
kids=lore :: our kids' state
==
```
## Generating cards, tombstoning shrubs
We covered `card:neo` in the Counter tutorial, but this is the first time were seeing one generated within a shrub. Diary takes two pokes: `%put-entry`, to create a new diary entry, and `%del-entry` to tombstone one.
Heres the `+poke` arm of the Diary shrub, expanded with comments.
```hoon
:: /imp/diary.hoon
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
?> =(%diary p.pail)
?> =(%diary-diff stud)
=/ state !<(diary q.pail)
=/ act !<(diary-diff vax)
::
:: assert the poke comes from our ship
:: src.bowl:neo is (pair ship pith)
?> =(our ship.src):bowl
?- -.act
%put-entry
:: return unchanged state
:_ [%diary !>(state)]
::
:: create list of one card:neo
:: card:neo is (pair pith:neo note:neo)
:~ :- %+ welp
::
:: here.bowl is the path of this shrub
:: /path/to/diary
here.bowl
::
:: append post id
:: /path/to/diary/~2024.6.3..14.07.15..7098
~[[%da id.act]]
::
:: this note will %make a new shrub
:: at the pith we defined above
^- note:neo
:: [%make stud:neo (unit pail:neo) conf:neo]
:* %make
::
:: new shrub has the implementation %txt
:: see /imp/txt.hoon, a stub that allows
:: you to create a %txt in the namespace
%txt
::
:: new shrub's initial state
:: is the text from the poke
`[%txt !>(txt.act)]
::
:: conf:neo is (map term pith:neo)
:: declare this new shrub's dependencies
:: which are also shrubs; diary has none
~
==
==
::
%del-entry
:: return unchanged state
:_ [%diary !>(state)]
::
:: send a %tomb note to /path/to/diary/<id>
:: this will tombstone the diary entry,
:: effectively deleting it from the namespace
:~ :- %+ welp
here.bowl
~[[%da id.act]]
^- note:neo
[%tomb ~]
==
==
```
The above is mostly self-explanatory, but its worth emphasizing a few points.
Diarys state never changes. Its state is just its name, a `@t`. It stores all its entries in the namespace. This makes every one of them amenable to being scried out by other apps, and it leaves room for other apps to make shrubs beneath `/path/to/diary/<@da>` at paths like `/<@da>/comments`, `/<@da>/reacts`, or `/<@da>/backlinks`.
The location of this shrub, `here.bowl`, is just a list. So its easy to `+welp` paths like `/<@da>` or `/<@da>/comments` onto the end. In this context, just keep in mind that they need to be `iota`s like `[%da now.bowl]`.
The `%make` card has two mysteries: it initializes diary entries with an empty `%txt` implementation and an empty map of dependencies. Well punt on dependencies until the next tutorial. If you look at `/imp/txt.hoon`, its just a `~`. This is an *elegant short-term workaround* that allows us to shove `%txt`s into the namespace; if the `/imp/<foo>` file is just a `~`, `/app/neo` will notice this and substitute in a shrub that takes the state of the type defined in `/pro/<foo>`. Like everything else in these tutorials this is subject to change!
## Diary frontend
Like Counter, the Diary shrub just has two `/con` files to convert to and from an HTMX frontend within the Sky browser.
```hoon
:: /con/diary-htmx.hoon
/@ diary :: name=@t
/- feather-icons
:- [%diary %$ %htmx]
|= dia=diary
|= =bowl:neo
^- manx
|^
;div.p2
=label "Diary"
;div.ma.fc.g2
=style "max-width: 650px;"
;+ form-put-entry
;*
%+ turn
~
link-entry
==
==
++ form-put-entry
;form.fc.g2
=style "margin-bottom: 30px;"
=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(name "id");
;textarea.p2.bd1.br1
=name "text"
=placeholder "today, i ..."
=oninput "this.setAttribute('value', this.value)"
=rows "5"
=required ""
=autocomplete "off"
;
==
;button.p2.b1.br1.bd1.wfc.hover.loader
;span.loaded.s2: create
;span.loading
;+ loading.feather-icons
==
==
==
++ 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.b1.hover.loader
=href "{(en-tape:pith:neo (weld /neo/hawk here.bowl))}/{id}"
;div.loaded.fc.g1.js.as.g2
;span.f3: {(pretty-date `@da`->:pax)}
;span.bold: {subject}
==
;span.loading
;+ loading.feather-icons
==
==
;button.p2.br1.fr.g2.b1.hover.fc.ac.jc.loader
=onclick "alert('not yet implemented. no tombstoning?')"
;span.loaded
;+ close.feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
==
++ pretty-date
|= date=@da
^- tape
=/ d (yore date)
"{(y-co:co y:d)}-{(y-co:co m:d)}-{(y-co:co d:t:d)}"
--
```
Theres only a bit more going on here than in Counters `/con/number-htmx.hoon` file. The main thing is that we have `+link-entry` and `+pretty-date` helper arms which are being used like functional frontend components.
The Diary frontend is a text box at the top with a list of entries generated from a list of the Diary shrubs child `pith`s. Nowhere is a list of children in the state being passed from the backend to the frontend, like you might see with a Gall agent passing converting a `(list item)` to a JSON array through a mark in the event that a frontend has scried or subscribed on a path, all of which would require painstaking specification by the developer.
```hoon
:: /con/node-diary-diff.hoon
/@ node :: manx
/@ diary-diff :: ?([%put-entry id=@da txt=@t] [%del-entry id=@da])
/- manx-utils
:- [%node %$ %diary-diff]
|= nod=node
^- diary-diff
=/ mu ~(. manx-utils nod)
=/ head (@tas (got:mu %head))
=/ id (slav %da (vol:mu "id"))
=/ text (vol:mu "text")
[%put-entry id text]
```
Just like the `/con/node-counter-diff.hoon` in the Counter tutorial, all this does is extract attributes from an XML node, having been converted into a `manx`, and manually constructs the poke expected by the shrub.
## Building on Diary
* Amend the `+kids` arm so that other apps could create shrubs like `/path/to/diary/<@da>/comments`.
* Diary takes two pokes: `%put-entry` and `%del-entry`, but only one of these is supported on the frontend. Implement a `%del-entry` poke from the frontend.

View File

@ -1,45 +1,135 @@
/@ txt
/@ diary
/@ diary-diff
/@ txt :: @t
/@ diary :: name=@t
/@ diary-diff :: ?([%put-entry id=@da =txt] [%del-entry id=@da])
::
:: outer core of a shrub: define state, pokes,
:: dependencies, and kids
^- kook:neo
|%
++ state pro/%diary
++ poke (sy %diary-diff ~)
::
:: diary's state is a %diary, just a @t
++ state
^- curb:neo
[%pro %diary]
::
:: diary takes pokes with stud %diary-diff
++ poke
^- (set stud:neo)
(sy %diary-diff ~)
::
:: constrain shrubs below diary in the namespace
:: by defining the types of their state and pokes
++ kids
:+ ~ %y
::
:: kids:neo is a (unit port:neo)
^- kids:neo
%- some
::
:: port:neo is (pair dare:neo lads:neo)
:: dare:neo is ?(%y %z)
:: if %y, only constrain our immediate children
:: if %z, recursively constrain all descendants
:- %y
:: lads:neo is (map pish:neo lash:neo)
%- ~(gas by *lads:neo)
:~ :- [|/%da |]
[pro/%txt ~]
:~ :- ::
:: pish:neo
:: to simplify: [%.n @da] means the kid's
:: path contains any @da, and %.n is there
:: to signify that the pith can not have more
:: fields afterwards
[[%.n %da] %.n]
::
:: lash:neo is (pair curb:neo (set stud:neo))
:: curb:neo defines the kids' state
:: (set stud:neo) defines the kids' pokes
[[%only %txt] ~]
==
++ deps *deps:neo
::
:: diary has no other shrubs as dependencies
++ deps
^- deps:neo
*deps:neo
::
:: inner core, business logic
++ form
^- form:neo
|_ [=bowl:neo =aeon:neo state=pail:neo]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
?> =(%diary-diff stud)
=/ poke !<(diary-diff vax)
=/ sta !<(diary q.state)
?> =(our ship.src):bowl
=^ cards=(list card:neo) sta
?- -.poke
%put-entry
:_ sta
:~
:- (welp here.bowl ~[da/id.poke])
^- note:neo
[%make %txt `txt/!>(txt.poke) ~]
==
%del-entry `sta
==
[cards diary/!>(sta)]
|_ [=bowl:neo =aeon:neo =pail:neo]
::
:: if diary has no existing state, return
:: the bunt of the state type, otherwise
:: return the existing state
++ init
|= old=(unit pail:neo)
^- (quip card:neo pail:neo)
:- ~
?^ old u.old
diary/!>(*diary)
?~ old
[%diary !>(*diary)]
u.old
::
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
?> =(%diary p.pail)
?> =(%diary-diff stud)
=/ state !<(diary q.pail)
=/ act !<(diary-diff vax)
::
:: assert the poke comes from our ship
:: src.bowl:neo is (pair ship pith)
?> =(our ship.src):bowl
?- -.act
%put-entry
:: return unchanged state
:_ [%diary !>(state)]
::
:: create list of one card:neo
:: card:neo is (pair pith:neo note:neo)
:~ :- %+ welp
::
:: here.bowl is the path of this shrub
:: /path/to/diary
here.bowl
::
:: append post id
:: /path/to/diary/~2024.6.3..14.07.15..7098
~[[%da id.act]]
::
:: this note will %make a new shrub
:: at the pith we defined above
^- note:neo
:: [%make stud:neo (unit pail:neo) conf:neo]
:* %make
::
:: new shrub has the implementation %txt
:: see /imp/txt.hoon, a stub that allows
:: you to create a %txt in the namespace
%txt
::
:: new shrub's initial state
:: is the text from the poke
`[%txt !>(txt.act)]
::
:: conf:neo is (map term pith:neo)
:: declare this new shrub's dependencies
:: which are also shrubs; diary has none
~
==
==
::
%del-entry
:: return unchanged state
:_ [%diary !>(state)]
::
:: send a %tomb note to /path/to/diary/<id>
:: this will tombstone the diary entry,
:: effectively deleting it from the namespace
:~ :- %+ welp
here.bowl
~[[%da id.act]]
^- note:neo
[%tomb ~]
==
==
--
--

View File

@ -3,8 +3,6 @@
|%
++ state pro/%ship :: who I'm chatting with
++ poke (sy %dm-diff ~)
++ state pro/%ship :: who I'm chatting with
++ poke (sy %dm-diff ~)
++ kids
:+ ~ %y
%- ~(gas by *lads:neo)
@ -13,12 +11,6 @@
:- [|/%mine |]
[pro/%message-sub (sy %sig ~)]
==
++ deps *deps:neo
:~ :- [|/%theirs |]
[pro/%message-pub (sy %sig ~)]
:- [|/%mine |]
[pro/%message-sub (sy %sig ~)]
==
++ deps *deps:neo
++ form
^- form:neo

View File

@ -1,221 +1,189 @@
/@ htmx-type=htmx
/@ htmx-type=htmx
/@ htmx-type=htmx
/- feather-icons
/- serv=server
/- serv=sky-server
/> htmx
/< node
=>
|%
++ main
^- curb:neo
::[%or rol/[%ui-main pro/%htmx] pro/%htmx ~]
:: rol/[%ui-main pro/%htmx]
pro/%htmx
++ kids-curb
^- curb:neo
::[%or rol/[%ui-cell pro/%htmx] pro/%htmx ~]
pro/%htmx
:: rol/[%ui-list pro/%htmx]
++ manx-to-octs
|= man=manx
(as-octt:mimes:html (en-xml:html man))
::
++ render
|= [main=manx kids=marl]
;div
;+ main
;div
;* kids
==
==
++ parse-url
|= =request:http
^- [pax=path pam=(map @t @t)]
=/ parsed
%+ rash url.request
;~ plug
;~(pfix fas (more fas smeg:de-purl:html))
yque:de-purl:html
==
:- (slag 2 -.parsed) :: strip first 2 segments (/neo/hawk)
(malt +.parsed)
++ parse-body
|= =request:http
|%
++ manx-to-octs
|= man=manx
(as-octt:mimes:html (en-xml:html man))
++ eyre-cards
|= [eyre-id=@ta =bowl:neo status=@ud =manx]
^- (list card:neo)
=/ =pith:neo #/[p/our.bowl]/$/eyre
=/ head=sign:eyre:neo [eyre-id %head [status [['content-type' 'text/html'] ~]]]
=/ data=sign:eyre:neo [eyre-id %data `(manx-to-octs manx)]
=/ done=sign:eyre:neo [eyre-id %done ~]
:~ [pith %poke eyre-sign/!>(head)]
[pith %poke eyre-sign/!>(data)]
[pith %poke eyre-sign/!>(done)]
[here.bowl %cull ~]
==
++ default-refresher
|= =pith
=/ tath (en-tape:pith:neo pith)
;div
=hx-get "/neo/hawk{tath}"
=hx-target "closest .hawk"
=hx-select ".hawk"
=hx-trigger "load once"
=hx-swap "outerHTML"
;
==
++ hawk
|_ [here=pith main=manx raw=manx has-app=?]
++ id *@da
++ our-tape
=/ f (snag 0 here)
?@(f (trip f) (scow f))
++ idt `tape`(zing (scan +:(scow %da id) (most dot (star ;~(less dot prn)))))
++ slot 0 :: XX fix sky positional saving
++ lift
^- manx
%+ fall
(de-xml:html q:(fall body.request [p=0 q='']))
*manx
++ eyre-cards
|= [eyre-id=@ta =bowl:neo status=@ud =manx]
^- (list card:neo)
=/ =pith:neo #/[p/our.bowl]/$/eyre
=/ head=sign:eyre:neo [eyre-id %head [status [['content-type' 'text/html'] ~]]]
=/ data=sign:eyre:neo [eyre-id %data `(manx-to-octs manx)]
=/ done=sign:eyre:neo [eyre-id %done ~]
:~ [pith %poke eyre-sign/!>(head)]
[pith %poke eyre-sign/!>(data)]
[pith %poke eyre-sign/!>(done)]
[here.bowl %cull ~]
==
++ default-refresher
|= =pith
=/ tath (en-tape:pith:neo pith)
;div
=hx-get "/neo/hawk{tath}"
=hx-target "closest .hawk"
=hx-select ".hawk"
=hx-trigger "load once"
=hx-swap "outerHTML"
;
==
++ hawk
|_ [here=pith main=manx raw=manx]
++ id *@da
++ idt `tape`(zing (scan +:(scow %da id) (most dot (star ;~(less dot prn)))))
++ has-app %.y :: XX : switch on it. make it real. etc
++ slot 0 :: XX fix sky positional saving
++ lift
;div.hawk.fc.wf.hf
=id "hawk-{idt}"
=hx-params "id,slot"
=hx-vals "\{\"id\": \"{<id>}\", \"slot\": \"{<slot>}\"}"
;+ header
::;+ raw
;div
=class "rendered wf hf b0 scroll-y scroll-x {(trip ?:(has-app '' 'hidden'))}"
=id "hawk-rendered-{idt}"
=morph-retain "class"
;+ main
==
;div.hawk.fc.wf.hf
=id "hawk-{idt}"
=hx-params "id,slot"
=hx-vals "\{\"id\": \"{<id>}\", \"slot\": \"{<slot>}\"}"
;+ header
;div
=class "raw p-page wf hf b0 scroll-y scroll-x {(trip ?:(has-app 'hidden' ''))}"
;+ raw
==
++ header
;header.b2.p1.frw.g1.ac
=id "hawk-header-{idt}"
=style "border: 2px solid var(--b2);"
;button
=class "p1 hover b2 br1 bd0 {(trip ?:(has-app '' 'toggled'))}"
=onclick
"""
$(this).toggleClass('toggled');
$(this).closest('.hawk').find('.raw').toggleClass('hidden');
$(this).closest('.hawk').find('.rendered').toggleClass('hidden');
$(this).closest('header').children('.hawk-tog').toggleClass('hidden');
"""
;+ outline:feather-icons
==
;div
=class "hawk-tog frw g1 ac grow {(trip ?:(has-app '' 'hidden'))}"
;*
=< p
%^ spin here
0
|= [=iota a=@]
:_ +(a)
;div.fr.ac.g1
;div
=class "rendered wf hf b0 scroll-y scroll-x {(trip ?:(has-app '' 'hidden'))}"
=id "hawk-rendered-{idt}"
=morph-retain "class"
;+ main
==
==
++ header
;header.b2.p1.frw.g1.ac
=id "hawk-header-{idt}"
=style "border: 2px solid var(--b2);"
;button
=class "p1 hover b2 br1 bd0 {(trip ?:(has-app '' 'toggled'))}"
=onclick
"""
$(this).toggleClass('toggled');
$(this).closest('.hawk').find('.raw').toggleClass('hidden');
$(this).closest('.hawk').find('.rendered').toggleClass('hidden');
$(this).closest('header').children('.hawk-tog').toggleClass('hidden');
"""
;+ outline:feather-icons
==
;div
=class "hawk-tog frw g1 ac grow {(trip ?:(has-app '' 'hidden'))}"
;*
=< p
%^ spin here
0
|= [=iota a=@]
:_ +(a)
;div.fr.ac.g1
=style "height: 2rem;"
;div.f4.s-1: >
;a.hover.b2.br1.p-1.s0.loader.fc.ac.jc
=style "height: 2rem;"
;div.f4.s-1: >
;a.hover.b2.br1.p-1.s0.loader.fc.ac.jc
=style "height: 2rem;"
=hx-vals "\{\"id\": \"{<id>}\", \"slot\": \"{<slot>}\"}"
=href "/neo/hawk{(en-tape:pith:neo (scag +(a) here))}"
;span.loaded
;+ ;/
?: =(a 0) "/"
(trip ?@(iota iota (scot iota)))
==
;span.loading
;+ loading.feather-icons
==
=hx-vals "\{\"id\": \"{<id>}\", \"slot\": \"{<slot>}\"}"
=href "/neo/hawk{(en-tape:pith:neo (scag +(a) here))}"
;span.loaded
;+ ;/
?: =(a 0) "/"
(trip ?@(iota iota (scot iota)))
==
==
;div.grow;
==
;form
=class "hawk-tog grow fr m0 relative {(trip ?:(has-app 'hidden' ''))}"
=style "height: 2rem;"
=hx-get "/neo/hawk"
=hx-target "closest .hawk"
;div.absolute
=style "top: 0.5rem; right: 0.5rem;"
;div.loader
;div.loaded(style "opacity: 0"): ---
;div.loading
;+ loading:feather-icons
;span.loading
;+ loading.feather-icons
==
==
==
;input.p-1.br1.b1.wf.s0.loaded.grow.bd0
=style "margin-left: 5px;"
=type "text"
=value (en-tape:pith:neo here)
=oninput
"""
$(this).attr('value', this.value);
$(this).parent().attr('hx-get', '/neo/hawk'+this.value);
htmx.process(document.body);
"""
;
;div.grow;
==
;form
=class "hawk-tog grow fr m0 relative {(trip ?:(has-app 'hidden' ''))}"
=style "height: 2rem;"
=hx-get "/neo/hawk"
=hx-target "closest .hawk"
;div.absolute
=style "top: 0.5rem; right: 0.5rem;"
;div.loader
;div.loaded(style "opacity: 0"): ---
;div.loading
;+ loading:feather-icons
==
==
==
;div.fr.ac.jc.g1.hawk-actions
=id "hawk-actions-{idt}"
;button.p1.hover.b2.br1.loader.s-1
=id "hawk-slide-up-{idt}"
=hx-post "/neo/hawk/sky?stud=sky-diff"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "slide-up"
=hawk-slot "{<slot>}"
;span.loaded
;+ chevron-left:feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
;button.p1.hover.b2.br1.loader.s-1
=id "hawk-slide-down-{idt}"
=hx-post "/neo/hawk/sky?stud=sky-diff"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "slide-down"
=hawk-slot "{<slot>}"
;span.loaded
;+ chevron-right:feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
;button.p1.hover.b2.br1.loader.s-1
=id "hawk-close-{idt}"
=hx-post "/neo/hawk/sky?stud=sky-diff"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "minimize"
=hawk-slot "{<slot>}"
;span.loaded
;+ minimize:feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
;style
;+ ;/ %- trip
'''
@media(max-width: 900px) {
.hawk-actions {
display: none !important;
}
}
'''
==
;input.p-1.br1.b1.wf.s0.loaded.grow.bd0
=style "margin-left: 5px;"
=type "text"
=value (en-tape:pith:neo here)
=oninput
"""
$(this).attr('value', this.value);
$(this).parent().attr('hx-get', '/neo/hawk'+this.value);
htmx.process(document.body);
"""
;
==
==
--
;div.fr.ac.jc.g1.hawk-actions
=id "hawk-actions-{idt}"
;button.p1.hover.b2.br1.loader.s-1
=id "hawk-slide-up-{idt}"
=hx-post "/neo/hawk/{our-tape}/sky?stud=sky-diff"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "slide-up"
=hawk-slot "{<slot>}"
;span.loaded
;+ chevron-left:feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
;button.p1.hover.b2.br1.loader.s-1
=id "hawk-slide-down-{idt}"
=hx-post "/neo/hawk/{our-tape}/sky?stud=sky-diff"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "slide-down"
=hawk-slot "{<slot>}"
;span.loaded
;+ chevron-right:feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
;button.p1.hover.b2.br1.loader.s-1
=id "hawk-close-{idt}"
=hx-post "/neo/hawk/{our-tape}/sky?stud=sky-diff"
=hx-target "find .loading"
=hx-swap "outerHTML"
=head "minimize"
=hawk-slot "{<slot>}"
;span.loaded
;+ minimize:feather-icons
==
;span.loading
;+ loading.feather-icons
==
==
;style
;+ ;/ %- trip
'''
@media(max-width: 900px) {
.hawk-actions {
display: none !important;
}
}
'''
==
==
==
--
--
^- kook:neo
|%
++ state pro/%eyre-task
@ -230,12 +198,14 @@
^- fief:neo
:- req=|
^- quay:neo
:- [[%or rol/[%ui-main pro/%htmx] pro/%htmx ~] ~]
:- [pro/%htmx ~]
:::- [[%or rol/[%ui-main pro/%htmx] pro/%htmx ~] ~]
^- (unit port:neo)
:+ ~ %z
%- ~(gas by *lads:neo)
:~ :- &
`lash:neo`[any/~ ~]
::`lash:neo`[[%or rol/[%link pro/%htmx] pro/%htmx any/~ ~] ~]
==
==
::
@ -251,19 +221,24 @@
=/ [=stud:neo =vase] (need pal)
=+ !<([eyre-id=@ta req=inbound-request:eyre] vase)
:_ [stud vase]
?. authenticated.req
%: eyre-cards
eyre-id
bowl
403
;div: 40
==
::XX revive when auth
::?. authenticated.req
:: %: eyre-cards
:: eyre-id
:: bowl
:: 403
:: ;div: 403
:: ==
?~ src=(~(get by deps.bowl) %src)
=/ main=manx
;div.wf.hf.fc.jc.ac: nothing here
=/ raw
;div.wf.hf.fc.jc.ac: raw view
%: eyre-cards
eyre-id
bowl
404
;div: 404
200
~(lift hawk #/[p/our.bowl] main raw |)
==
=/ here p.u.src
^- (list card:neo)
@ -279,38 +254,61 @@
=. kids.bol q.u.src
:: XX src.bowl
=/ main (!<(htmx-type q.pail.root) bol)
=/ raw *manx
=/ raw
;div.wf.hf.fc.jc.ac: raw view
%: eyre-cards
eyre-id
bowl
200
~(lift hawk here.bol main raw)
~(lift hawk here.bol main raw &)
==
::
%'POST'
=/ purl (parse-url request.req)
=/ body (parse-body request.req)
=/ purl (parse-url:serv request.req)
=/ body (parse-body:serv request.req)
=/ poke-stud
^- stud:neo
~| %no-stud-specified
(~(got by pam.purl) 'stud')
=/ =pail:neo [poke-stud (node [poke-stud body])]
=/ bol *bowl:neo
=. here.bol here
=. our.bol our.bowl
=. now.bol now.bowl
=. eny.bol eny.bowl
=/ =manx
?~ converter=(mole |.((htmx pail)))
(default-refresher here)
:: XX virtualize
(u.converter bol)
:- [here %poke pail]
%: eyre-cards
eyre-id
bowl
200
manx
=/ mul (mule |.((node [poke-stud body])))
?- -.mul
%.n
%: eyre-cards
eyre-id
bowl
400
;div
;*
%+ turn (tang p.mul)
|= =tank
;div: {(of-wall:format (~(win re tank) 0 55))}
==
==
::
%.y
=/ =pail:neo [poke-stud p.mul]
=/ bol *bowl:neo
=. here.bol here
=. our.bol our.bowl
=. now.bol now.bowl
=. eny.bol eny.bowl
=/ =manx
?~ converter=(mole |.((htmx pail)))
(default-refresher here)
=/ mul
%- mule
|.((u.converter bol))
?- -.mul
%.y p.mul
%.n ;div: error
==
:- [here %poke pail]
%: eyre-cards
eyre-id
bowl
200
manx
==
==
==
--

View File

@ -1,6 +1,5 @@
/@ eyre-reqs
/- serv=sky-server
/- serv=sky-server
^- kook:neo
|%
++ state pro/%sig
@ -26,10 +25,8 @@
=/ inner=pith:neo (pave:neo pax.purl)
=/ =crew:neo (~(gas by *crew:neo) src/inner ~)
=/ =made:neo [%hawk-eyre-handler `[stud vax] crew]
=/ =made:neo [%hawk-eyre-handler `[stud vax] crew]
:_ sig/!>(~)
:~ [(welp here.bowl #/[uv/eny.bowl]) %make made]
:~ [(welp here.bowl #/[uv/eny.bowl]) %make made]
==
==
++ init

View File

@ -13,7 +13,7 @@
^- (quip card:neo pail:neo)
:_ home/!>(*home)
:~ [#/[p/our.bowl]/home/diary %make %diary ~ ~]
[#/[p/our.bowl]/home/tasks %make %task `task/!>(['' | ~]) ~]
[#/[p/our.bowl]/home/tasks %make %task `task/!>(['' | & ~]) ~]
[#/[p/our.bowl]/home/sail %make %sail `sail/!>(['' 'prose p3' ~]) ~]
[#/[p/our.bowl]/home/accel %make %accel ~ ~]
[#/[p/our.bowl]/home/circle %make %circle ~ ~]

View File

@ -12,241 +12,242 @@
=<
^- kook:neo
|%
++ main
^- curb:neo
pro/%htmx
:: [%or rol/[%ui-main pro/%htmx] pro/%htmx ~]
:: rol/[%ui-main pro/%htmx]
++ kids-curb
^- curb:neo
pro/%htmx
:: rol/[%ui-list pro/%htmx]
++ manx-to-octs
|= man=manx
%- as-octt:mimes:html
%+ welp "<!DOCTYPE html>"
(en-xml:html man)
++ state pro/%eyre-task
++ poke *(set stud:neo)
++ kids
:+ ~ %y
%- ~(gas by *lads:neo)
~
++ deps
%- ~(gas by *band:neo)
:~ :- %src
^- fief:neo
:- req=|
^- quay:neo
:- [pro/%htmx ~]
^- (unit port:neo)
:+ ~ %y
%- ~(gas by *lads:neo)
:~ :- &
`lash:neo`[any/~ ~]
==
==
::
++ render
|= [main=manx kids=marl]
;div
;+ main
;div
;* kids
==
==
++ icon-url
^~
%- trip
%^ cat
3
'data:image/png;base64,'
%- ~(en base64:mimes:html & |)
(as-octs:mimes:html hawk-icon)
++ favicon
^~
=; m m(a.g [[%href icon-url] a.g.m])
^- manx
;link
=rel "icon"
=type "image/png"
;
==
++ manifest-url
^~
%- trip
%^ cat
3
'data:application/json;utf-8,'
%- en:json:html
%- pairs:enjs:format
:~
['name' s+'sky']
['description' s+'an urbit namespace viewer']
['start_url' s+'http://localhost/neo/sky'] :: XX
['display' s+'standalone']
['background_color' s+'black']
:+ 'icons' %a
:~
%- pairs:enjs:format
:~
['src' s+(crip icon-url)]
['sizes' s+'196x196']
['type' s+'image/png']
++ form
|_ [=bowl:neo =aeon:neo =pail:neo]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
`pail
++ init
|= pal=(unit pail:neo)
=/ [=stud:neo =vase] (need pal)
=+ !<([eyre-id=@ta req=inbound-request:eyre] vase)
:_ [stud vase]
=/ =pith:neo #/[p/our.bowl]/$/eyre
=; =manx
=/ head=sign:eyre:neo [eyre-id %head [200 [['content-type' 'text/html'] ~]]]
=/ data=sign:eyre:neo [eyre-id %data `(manx-to-octs manx)]
=/ done=sign:eyre:neo [eyre-id %done ~]
:~ [pith %poke eyre-sign/!>(head)]
[pith %poke eyre-sign/!>(data)]
[pith %poke eyre-sign/!>(done)]
[here.bowl %cull ~]
==
==
==
++ manifest
^~
=; m m(a.g [[%href manifest-url] a.g.m])
^- manx
;link
=rel "manifest"
;
==
++ htmx-extensions
:: htmx extension which encodes the request
:: as the serialized HTML of the calling element
%- trip
'''
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));
}
});
Idiomorph.defaults.ignoreActive = true;
Idiomorph.defaults.callbacks.beforeAttributeUpdated = (name, node, type) => {
if (node.hasAttribute('morph-retain')) {
let ribs = node.getAttribute('morph-retain').split(',').map(t => t.trim());
if (ribs.includes(name)) {
return false;
}
}
}
Idiomorph.defaults.callbacks.beforeNodeMorphed = (oldNode, newNode) => {
if (oldNode?.nodeName !== "#text") {
if (oldNode.hasAttribute('morph-no-swap') && oldNode.id === newNode.id) {
return false;
}
else if (
newNode.hasAttribute('morph-if-class') &&
!oldNode.classList.contains(newNode.getAttribute('morph-if-class'))
) {
return false;
}
}
}
'''
::
++ lift
|= in=manx
^- manx
;html
;head
;meta(charset "UTF-8");
;title: s k y
;script: {(trip jquery)}
;script: {(trip htmx-js)}
;script: {(trip htmx-response-targets)}
;script: {(trip htmx-idiomorph)}
;script: {htmx-extensions}
;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;
:: }
:: '''
::==
;style: {(trip reset)}
;style: {(trip feather)}
;script
;+ ;/
"""
window.log=function()\{if(this.console)\{console.log(Array.prototype.slice.call(arguments));}};
jQuery.fn.log=function (msg)\{console.log(msg, this); return this;};
jQuery.fn.emit=function (name)\{(this[0]).dispatchEvent(new Event(name, \{ bubbles: true, cancelable: true, composed: true })); return this;};
"""
==
;script: {(trip a-i-r)}
;script: {(trip date-now)}
;+ favicon
;+ manifest
==
;body
=hx-ext "html-enc,response-targets,morph"
=hx-swap "innerHTML"
=hx-boost "true"
=hx-history "false"
=hx-replace-url "/neo/sky"
=hx-target "closest .hawk"
=style
"""
background-color: var(--b1);
background-image: var(--sky-bg-url);
background-size: var(--sky-bg-size);
background-repeat: var(--sky-bg-repeat);
"""
;+ in
==
==
?~ src=(~(get by deps.bowl) %src)
;div: 404
=/ root=idea:neo (~(got of:neo q.u.src) /)
?> =(%htmx p.pail.root)
=/ bol *bowl:neo
=. here.bol p.u.src
=. our.bol our.bowl
=. now.bol now.bowl
=. eny.bol eny.bowl
=. kids.bol q.u.src
:: XX src.bowl
(lift (!<(htmx q.pail.root) bol))
--
--
^- kook:neo
|%
++ state pro/%eyre-task
++ poke *(set stud:neo)
++ kids
:+ ~ %y
%- ~(gas by *lads:neo)
~
++ deps
%- ~(gas by *band:neo)
:~ :- %src
^- fief:neo
:- req=|
^- quay:neo
:- [main ~]
^- (unit port:neo)
:+ ~ %z
%- ~(gas by *lads:neo)
:~ :- &
`lash:neo`[kids-curb ~]
==
==
++ manx-to-octs
|= man=manx
%- as-octt:mimes:html
%+ welp "<!DOCTYPE html>"
(en-xml:html man)
::
++ form
|_ [=bowl:neo =aeon:neo =pail:neo]
++ poke
|= [=stud:neo vax=vase]
^- (quip card:neo pail:neo)
`pail
++ init
|= pal=(unit pail:neo)
=/ [=stud:neo =vase] (need pal)
=+ !<([eyre-id=@ta req=inbound-request:eyre] vase)
:_ [stud vase]
=/ =pith:neo #/[p/our.bowl]/$/eyre
=; =manx
%+ snoc
(~(respond neo:serv pith) [eyre-id (manx-response:gen:serv manx)])
[here.bowl %cull ~]
?~ src=(~(get by deps.bowl) %src)
;div: 404
=/ root=idea:neo (~(got of:neo q.u.src) /)
?> =(%htmx p.pail.root)
=/ bol *bowl:neo
=. here.bol p.u.src
=. our.bol our.bowl
=. now.bol now.bowl
=. eny.bol eny.bowl
=. kids.bol q.u.src
~& kids/~(key by ~(tar of:neo kids.bol))
:: XX src.bowl
(lift (!<(htmx q.pail.root) bol))
--
++ icon-url
^~
%- trip
%^ cat
3
'data:image/png;base64,'
%- ~(en base64:mimes:html & |)
(as-octs:mimes:html hawk-icon)
++ favicon
^~
=; m m(a.g [[%href icon-url] a.g.m])
^- manx
;link
=rel "icon"
=type "image/png"
;
==
++ manifest-url
^~
%- trip
%^ cat
3
'data:application/json;utf-8,'
%- en:json:html
%- pairs:enjs:format
:~
['name' s+'sky']
['description' s+'an urbit namespace viewer']
['start_url' s+'http://localhost/neo/sky'] :: XX
['display' s+'standalone']
['background_color' s+'black']
:+ 'icons' %a
:~
%- pairs:enjs:format
:~
['src' s+(crip icon-url)]
['sizes' s+'196x196']
['type' s+'image/png']
==
==
==
++ manifest
^~
=; m m(a.g [[%href manifest-url] a.g.m])
^- manx
;link
=rel "manifest"
;
==
++ htmx-extensions
:: htmx extension which encodes the request
:: as the serialized HTML of the calling element
%- trip
'''
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));
}
});
Idiomorph.defaults.ignoreActive = true;
Idiomorph.defaults.callbacks.beforeAttributeUpdated = (name, node, type) => {
if (node.hasAttribute('morph-retain')) {
let ribs = node.getAttribute('morph-retain').split(',').map(t => t.trim());
if (ribs.includes(name)) {
return false;
}
}
}
Idiomorph.defaults.callbacks.beforeNodeMorphed = (oldNode, newNode) => {
if (oldNode?.nodeName !== "#text") {
if (oldNode.hasAttribute('morph-no-swap') && oldNode.id === newNode.id) {
return false;
}
else if (
newNode.hasAttribute('morph-if-class') &&
!oldNode.classList.contains(newNode.getAttribute('morph-if-class'))
) {
return false;
}
}
}
'''
::
++ lift
|= in=manx
^- manx
;html
;head
;meta(charset "UTF-8");
;title: s k y
;script: {(trip jquery)}
;script: {(trip htmx-js)}
;script: {(trip htmx-response-targets)}
;script: {(trip htmx-idiomorph)}
;script: {htmx-extensions}
;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;
:: }
:: '''
::==
;style: {(trip reset)}
;style: {(trip feather)}
;script
;+ ;/ %- trip
'''
window.log = function() {
if (this.console) {
console.log(Array.prototype.slice.call(arguments));
}
};
jQuery.fn.log = function (msg) {
console.log(msg, this);
return this;
};
jQuery.fn.emit = function (name) {
(this[0]).dispatchEvent(
new Event(
name,
{ bubbles: true, cancelable: true, composed: true }
)
);
return this;
};
'''
==
;script: {(trip a-i-r)}
;script: {(trip date-now)}
;+ favicon
;+ manifest
==
;body
=hx-ext "html-enc,response-targets,morph"
=hx-swap "innerHTML"
=hx-boost "true"
=hx-history "false"
=hx-replace-url "/neo/sky"
=hx-target "closest .hawk"
=style
"""
background-color: var(--b1);
background-image: var(--sky-bg-url);
background-size: var(--sky-bg-size);
background-repeat: var(--sky-bg-repeat);
"""
;+ in
==
==
--

View File

@ -57,9 +57,8 @@
?+ stud !!
%gift
::check if all kid tasks are done
?: (check-kids bowl)
[~ task/!>(this(done %.y))]
[~ task/!>(this(done %.n))]
=/ dun (check-kids bowl)
[~ task/!>(this(done dun, kids-done dun))]
::
%task-diff
=/ diff !<(task-diff vax)
@ -71,6 +70,7 @@
`(list pith)`[~[ud/name] order.this]
`(list pith)`(snoc order.this `pith`[ud/name ~])
=. done.this |
=. kids-done.this |
:_ task/!>(this)
:~ :- (welp here.bowl ~[ud/name])
[%make %task `task/!>(task.diff) ~]
@ -82,7 +82,7 @@
!>
%= this
text text.diff
done ?: (check-kids bowl)
done ?: kids-done.this
done.diff
%.n
==

View File

@ -1 +1 @@
,[text=cord done=? order=(list pith)]
,[text=cord done=? kids-done=? order=(list pith)]