diff options
author | polwex <polwex@sortug.com> | 2025-08-28 20:22:07 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-08-28 20:22:07 +0700 |
commit | bdb1eceba469206381a8fc50a0b62df41c19af8d (patch) | |
tree | 79da08bff7115298a3c2381dbf8c29e22f65bff7 | |
parent | 7c0681a06e0358887d2f64001dc43125554766c9 (diff) |
m
-rw-r--r-- | desk/NOTES.md | 31 | ||||
-rw-r--r-- | desk/app/nostril.hoon | 4 | ||||
-rw-r--r-- | desk/desk.docket-0 | 11 | ||||
-rw-r--r-- | desk/lib/docket.hoon | 223 | ||||
-rw-r--r-- | desk/lib/markdown.hoon | 1711 | ||||
-rw-r--r-- | desk/lib/nostril/mutations.hoon | 83 | ||||
-rw-r--r-- | desk/lib/nostril/post.hoon | 330 | ||||
-rw-r--r-- | desk/lib/shim.hoon | 2 | ||||
-rw-r--r-- | desk/mar/docket-0.hoon | 25 | ||||
-rw-r--r-- | desk/sur/docket.hoon | 82 | ||||
-rw-r--r-- | desk/sur/feed.hoon | 16 | ||||
-rw-r--r-- | desk/sur/markdown.hoon | 157 | ||||
-rw-r--r-- | desk/sur/nostril.hoon | 18 | ||||
-rw-r--r-- | desk/sur/post.hoon | 1 | ||||
m--------- | front | 0 |
15 files changed, 2690 insertions, 4 deletions
diff --git a/desk/NOTES.md b/desk/NOTES.md new file mode 100644 index 0000000..078ccf6 --- /dev/null +++ b/desk/NOTES.md @@ -0,0 +1,31 @@ +# NIPS to implement + + +- NIP-2 for follow lists +- NIP-25 for reactions + +- NIP-17 for private DMs +https://github.com/nostr-protocol/nips/blob/master/17.md + +- NIP-10 for mentions and replies + +- NIP-18 for Quotes and RTs + +- NIP-57 lightning zaps + + +# Relay discovery + +https://nostr.watch/ + +# Tag specs +https://nostr-nips.com/#standardized-tags + +- 'e' for event, +- 'p' for people, +- 't' for hashtag +- 'r' for relays +- 'd' for identifier # wtf? +- 'm' for mime type +- 'a' for event coordinates +- 'g' for geohash diff --git a/desk/app/nostril.hoon b/desk/app/nostril.hoon index f941493..0ecd1ec 100644 --- a/desk/app/nostril.hoon +++ b/desk/app/nostril.hoon @@ -1,5 +1,5 @@ /- sur=nostril -/+ lib=nostril, shim, dbug +/+ lib=nostril, shim, dbug, muta=nostril-mutations /= web /web/router |% +$ versioned-state $%(state-0:sur) @@ -12,6 +12,7 @@ +* this . rout ~(. router:web [state bowl]) cards ~(. cards:lib bowl) + mutat ~(. muta [state bowl]) ++ on-init ^- (quip card:agent:gall agent:gall) :_ this(state default:sur) @@ -46,6 +47,7 @@ :: ~& request.req.order ?: .=(url.request.req.order '/nostr-shim') =/ event (handle:shim order) + ~& >> ev=event ?~ event `this =/ nevents=(list event:sur) [u.event events] =/ nevents2=(list event:sur) (scag 100 nevents) diff --git a/desk/desk.docket-0 b/desk/desk.docket-0 new file mode 100644 index 0000000..ddcd90b --- /dev/null +++ b/desk/desk.docket-0 @@ -0,0 +1,11 @@ +:~ + title+'Nostrill' + info+'Pater Nostr' + color+0xff.d400 + version+[0 1 0] + website+'https://sortug.com' + license+'© 2025 ~sortug. All rights reserved.' + image+'https://s3.sortug.com/img/nostril-icon.png' + base+'nostril' + glob-http+['https://s3.sortug.com/globs/glob-0v3.m2pdj.28e11.338vk.ordar.nn5d6.glob' 0v3.m2pdj.28e11.338vk.ordar.nn5d6] +== diff --git a/desk/lib/docket.hoon b/desk/lib/docket.hoon new file mode 100644 index 0000000..ef39b7f --- /dev/null +++ b/desk/lib/docket.hoon @@ -0,0 +1,223 @@ +/- *docket +|% +:: +++ mime + |% + +$ draft + $: title=(unit @t) + info=(unit @t) + color=(unit @ux) + glob-http=(unit [=url hash=@uvH]) + glob-ames=(unit [=ship hash=@uvH]) + base=(unit term) + site=(unit path) + image=(unit url) + version=(unit version) + website=(unit url) + license=(unit cord) + == + :: + ++ finalize + |= =draft + ^- (unit docket) + ?~ title.draft ~ + ?~ info.draft ~ + ?~ color.draft ~ + ?~ version.draft ~ + ?~ website.draft ~ + ?~ license.draft ~ + =/ href=(unit href) + ?^ site.draft `[%site u.site.draft] + ?~ base.draft ~ + ?^ glob-http.draft + `[%glob u.base hash.u.glob-http %http url.u.glob-http]:draft + ?~ glob-ames.draft + ~ + `[%glob u.base hash.u.glob-ames %ames ship.u.glob-ames]:draft + ?~ href ~ + =, draft + :- ~ + :* %1 + u.title + u.info + u.color + u.href + image + u.version + u.website + u.license + == + :: + ++ from-clauses + =| =draft + |= cls=(list clause) + ^- (unit docket) + =* loop $ + ?~ cls (finalize draft) + =* clause i.cls + =. draft + ?- -.clause + %title draft(title `title.clause) + %info draft(info `info.clause) + %color draft(color `color.clause) + %glob-http draft(glob-http `[url hash]:clause) + %glob-ames draft(glob-ames `[ship hash]:clause) + %base draft(base `base.clause) + %site draft(site `path.clause) + %image draft(image `url.clause) + %version draft(version `version.clause) + %website draft(website `website.clause) + %license draft(license `license.clause) + == + loop(cls t.cls) + :: + ++ to-clauses + |= d=docket + ^- (list clause) + %- zing + :~ :~ title+title.d + info+info.d + color+color.d + version+version.d + website+website.d + license+license.d + == + ?~ image.d ~ ~[image+u.image.d] + ?: ?=(%site -.href.d) ~[site+path.href.d] + =/ ref=glob-reference glob-reference.href.d + :~ base+base.href.d + ?- -.location.ref + %http [%glob-http url.location.ref hash.ref] + %ames [%glob-ames ship.location.ref hash.ref] + == == == + :: + ++ spit-clause + |= =clause + ^- tape + %+ weld " {(trip -.clause)}+" + ?+ -.clause "'{(trip +.clause)}'" + %color (scow %ux color.clause) + %site (spud path.clause) + :: + %glob-http + "['{(trip url.clause)}' {(scow %uv hash.clause)}]" + :: + %glob-ames + "[{(scow %p ship.clause)} {(scow %uv hash.clause)}]" + :: + %version + =, version.clause + "[{(scow %ud major)} {(scow %ud minor)} {(scow %ud patch)}]" + == + :: + ++ spit-docket + |= dock=docket + ^- tape + ;: welp + ":~\0a" + `tape`(zing (join "\0a" (turn (to-clauses dock) spit-clause))) + "\0a==" + == + -- +:: +++ enjs + =, enjs:format + |% + :: + ++ charge-update + |= u=^charge-update + ^- json + %+ frond -.u + ^- json + ?- -.u + %del-charge s+desk.u + :: + %initial + %- pairs + %+ turn ~(tap by initial.u) + |=([=desk c=^charge] [desk (charge c)]) + :: + %add-charge + %- pairs + :~ desk+s+desk.u + charge+(charge charge.u) + == + == + :: + ++ num + |= a=@u + ^- ^tape + =/ p=json (numb a) + ?> ?=(%n -.p) + (trip p.p) + :: + ++ version + |= v=^version + ^- json + :- %s + %- crip + "{(num major.v)}.{(num minor.v)}.{(num patch.v)}" + :: + ++ merge + |= [a=json b=json] + ^- json + ?> &(?=(%o -.a) ?=(%o -.b)) + [%o (~(uni by p.a) p.b)] + :: + ++ href + |= h=^href + %+ frond -.h + ?- -.h + %site s+(spat path.h) + %glob + %- pairs + :~ base+s+base.h + glob-reference+(glob-reference glob-reference.h) + == + == + :: + ++ glob-reference + |= ref=^glob-reference + %- pairs + :~ hash+s+(scot %uv hash.ref) + location+(glob-location location.ref) + == + :: + ++ glob-location + |= loc=^glob-location + ^- json + %+ frond -.loc + ?- -.loc + %http s+url.loc + %ames s+(scot %p ship.loc) + == + :: + ++ charge + |= c=^charge + %+ merge (docket docket.c) + %- pairs + :~ chad+(chad chad.c) + == + :: + ++ docket + |= d=^docket + ^- json + %- pairs + :~ title+s+title.d + info+s+info.d + color+s+(scot %ux color.d) + href+(href href.d) + image+?~(image.d ~ s+u.image.d) + version+(version version.d) + license+s+license.d + website+s+website.d + == + :: + ++ chad + |= c=^chad + %+ frond -.c + ?+ -.c ~ + %hung s+err.c + == + -- +-- diff --git a/desk/lib/markdown.hoon b/desk/lib/markdown.hoon new file mode 100644 index 0000000..67ee3ad --- /dev/null +++ b/desk/lib/markdown.hoon @@ -0,0 +1,1711 @@ +/- m=markdown +:: + +=> |% + :: Set label for collapsed / shortcut reference links + ++ backfill-ref-link + |= [a=link:inline:m] + ^- link:inline:m + =/ t target.a + ?+ -.t a :: only reference links + %ref + ?: =(%full type.t) a :: only collapsed / shortcut links + =/ node=element:inline.m (head contents.a) + ?+ -.node a :: ...and it's a %text node + %text + %_ a + target %_ t + label text.node + == + == + == + == + :: + ++ whitespace (mask " \09\0d\0a") :: whitespace: space, tab, or newline + :: + ++ all-link-ref-definitions :: Recursively get link ref definitions + =< process-nodes + |% + ++ process-nodes + |= [nodes=markdown:m] + ^- (map @t urlt:ln:m) + ?~ nodes ~ + %- %~(uni by (process-node (head nodes))) + $(nodes +.nodes) + :: + ++ process-nodeses + |= [nodeses=(list markdown:m)] + ^- (map @t urlt:ln:m) + ?~ nodeses ~ + %- %~(uni by (process-nodes (head nodeses))) + $(nodeses +.nodeses) + :: + ++ process-node + |= [node=node:markdown:m] + ^- (map @t urlt:ln:m) + =/ result *(map @t urlt:ln:m) + ?- -.node + %leaf :: Leaf node: check if it's a link ref def + =/ leaf=node:leaf:m +.node + ?+ -.leaf result + %link-ref-definition (~(put by result) label.leaf urlt.leaf) + == + :: + %container + =/ container=node:container:m +.node + ?- -.container + %block-quote (process-nodes markdown.container) + %ol (process-nodeses contents.container) + %ul (process-nodeses contents.container) + %tl (process-nodeses (turn contents.container |=([is-checked=? =markdown:m] markdown))) + == + == + -- + -- +|% + :: + :: Parse to and from Markdown text format + ++ md + |% + ++ de :: de:md Deserialize (parse) + |% + ++ escaped + |= [char=@t] + (cold char (jest (crip (snoc "\\" char)))) + :: + ++ newline + %+ cold '\0a' :: EOL, with or without carriage return '\0d' + ;~(pfix ;~(pose (just '\0d') (easy ~)) (just '\0a')) + ++ line-end :: Either EOL or EOF + %+ cold '\0a' + ;~(pose newline (full (easy ~))) + :: + ++ ln :: Links and urls + |% + ++ url + =< %+ cook |=(a=url:ln:m a) :: Cast + ;~(pose with-triangles without-triangles) + |% + ++ with-triangles + ;~ plug + %+ cook crip + %+ ifix [gal gar] + %- star + ;~ pose + (escaped '<') + (escaped '>') + ;~(less gal gar line-end prn) :: Anything except '<', '>' or newline + == + (easy %.y) :: "yes triangles" + == + ++ without-triangles + ;~ plug + %+ cook crip + ;~ less + gal :: Doesn't start with '<' + %- plus :: Non-empty + ;~ less + whitespace :: No whitespace allowed + ;~ pose + (escaped '(') + (escaped ')') + ;~(less pal par line-end prn) :: Anything except '(', ')' or newline + == + == + == + (easy %.n) :: "no triangles" + == + -- + :: + ++ urlt + %+ cook |=(a=urlt:ln:m a) :: Cast + ;~ plug + url + %- punt :: Optional title-text + ;~ pfix (plus whitespace) :: Separated by some whitespace + %+ cook crip ;~ pose :: Enclosed in single quote, double quote, or '(...)' + (ifix [soq soq] (star ;~(pose (escaped '\'') ;~(less soq prn)))) + (ifix [doq doq] (star ;~(pose (escaped '"') ;~(less doq prn)))) + (ifix [pal par] (star ;~(pose (escaped '(') (escaped ')') ;~(less pal par prn)))) + == + == + == + :: + :: Labels are used in inline link targets and in a block-level element (labeled link references) + ++ label + %+ cook crip + %+ ifix [sel ser] :: Enclosed in '[...]' + %+ ifix :- (star whitespace) :: Strip leading and trailing whitespapce + (star whitespace) + %- plus ;~ pose :: Non-empty + (escaped '[') + (escaped ']') + ;~(less sel ser prn) :: Anything except '[', ']' (must be escaped) + == + :: + ++ target :: Link target, either reference or direct + =< %+ cook |=(a=target:ln:m a) + ;~(pose target-direct target-ref) + |% + ++ target-direct + %+ cook |=(a=target:ln:m a) + %+ stag %direct + %+ ifix [pal par] :: Direct links are enclosed in '(...)' + %+ ifix :- (star whitespace) :: Strip leading and trailing whitespace + (star whitespace) + urlt :: Just the target + ++ target-ref + %+ cook |=(a=target:ln:m a) + %+ stag %ref + ;~ pose + %+ stag %full label + %+ stag %collapsed (cold '' (jest '[]')) + %+ stag %shortcut (easy '') + == + -- + -- + ++ inline :: Inline elements + |% + ++ contents (cook |=(a=contents:inline:m a) (star element)) :: Element sequence + ++ element :: Any element + %+ cook |=(a=element:inline:m a) + ;~ pose + escape + entity + strong + emphasis + code + link + image + autolink + text + softbrk + hardbrk + == + :: + ++ text + %+ knee *text:inline:m |. ~+ :: recurse + %+ cook |=(a=text:inline:m a) + %+ stag %text + %+ cook crip + %- plus :: At least one character + ;~ less :: ...which doesn't match any other inline rule + escape + entity + link + image + autolink + emphasis + strong + code + softbrk + hardbrk + :: ...etc + prn + == + :: + ++ escape + %+ cook |=(a=escape:inline:m a) + %+ stag %escape + ;~ pose + :: \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~ + (escaped '[') (escaped ']') (escaped '(') (escaped ')') + (escaped '!') (escaped '*') (escaped '*') (escaped '_') + (escaped '&') (escaped '\\') + :: etc + == + ++ entity + %+ cook |=(a=entity:inline:m a) + %+ stag %entity + %+ ifix [pam mic] + %+ cook crip + ;~ pose + ;~(plug hax (stun [1 7] nud)) :: '#' and one to seven digits + (plus alf) :: Named entity + == + :: + ++ softbrk :: Newline + %+ cook |=(a=softbrk:inline:m a) + %+ stag %soft-line-break + (cold ~ newline) + :: + ++ hardbrk + %+ cook |=(a=hardbrk:inline:m a) + %+ stag %line-break + %+ cold ~ + ;~ pose + ;~(plug (jest ' ') (star ace) newline) :: Two or more spaces before a newline + ;~(plug (just '\\') newline) :: An escaped newline + == + ++ link + %+ knee *link:inline:m |. ~+ :: recurse + %+ cook backfill-ref-link + %+ stag %link + ;~ plug + %+ ifix [sel ser] :: Display text is wrapped in '[...]' + %- star ;~ pose :: Display text can contain various contents + escape + entity + emphasis + strong + code + image + :: Text: => + %+ knee *text:inline:m |. ~+ :: recurse + %+ cook |=(a=text:inline:m a) + %+ stag %text + %+ cook crip + %- plus :: At least one character + ;~ less :: ...which doesn't match any other inline rule + escape + entity + emphasis + strong + code + ser :: No closing ']' + prn + == + == + target:ln + == + :: + ++ image + %+ cook |=(a=image:inline:m a) + %+ stag %image + ;~ plug + %+ ifix [(jest '![') (just ']')] :: alt-text is wrapped in '![...]' + %+ cook crip + %- star ;~ pose + (escaped ']') + ;~(less ser prn) + == + target:ln + == + :: + ++ autolink + %+ cook |=(a=autolink:inline:m a) + %+ stag %autolink + %+ ifix [gal gar] :: Enclosed in '<...>' + %+ cook crip + %- star ;~ pose + ;~(less ace gar prn) :: Spaces are not allowed; neither are backslash-escapes + == + :: + ++ emphasis + %+ knee *emphasis:inline:m |. ~+ :: recurse + %+ cook |=(a=emphasis:inline:m a) + %+ stag %emphasis + ;~ pose + %+ ifix [tar tar] + ;~ plug + (easy '*') + %- plus ;~ pose :: Display text can contain various contents + escape + entity + strong + link + autolink + code + image + link + softbrk + hardbrk + %+ knee *text:inline:m |. ~+ :: recurse + %+ cook |=(a=text:inline:m a) + %+ stag %text + %+ cook crip + %- plus :: At least one character + ;~ less :: ...which doesn't match any other inline rule + escape + entity + strong + link + autolink + code + image + link + softbrk + hardbrk + :: + tar :: If a '*', then it's the end of the `emphasis` + :: + prn + == + == + == + %+ ifix [cab cab] + ;~ plug + (easy '_') + %- plus ;~ pose :: Display text can contain various contents + escape + entity + strong + link + autolink + code + image + link + softbrk + hardbrk + %+ knee *text:inline:m |. ~+ :: recurse + %+ cook |=(a=text:inline:m a) + %+ stag %text + %+ cook crip + %- plus :: At least one character + ;~ less :: ...which doesn't match any other inline rule + escape + entity + strong + link + autolink + code + image + link + softbrk + hardbrk + :: + cab :: If a '*', then it's the end of the `emphasis` + :: + prn + == + == + == + == + :: + ++ strong + %+ knee *strong:inline:m |. ~+ :: recurse + %+ cook |=(a=strong:inline:m a) + %+ stag %strong + ;~ pose + %+ ifix [(jest '**') (jest '**')] + ;~ plug + (easy '*') + %- plus ;~ pose :: Display text can contain various contents + escape + emphasis + link + autolink + code + image + link + softbrk + hardbrk + %+ knee *text:inline:m |. ~+ :: recurse + %+ cook |=(a=text:inline:m a) + %+ stag %text + %+ cook crip + %- plus :: At least one character + ;~ less :: ...which doesn't match any other inline rule + escape + emphasis + link + autolink + code + image + link + softbrk + hardbrk + :: ...etc + (jest '**') :: If a '**', then it's the end of the `emphasis` + prn + == + == + == + %+ ifix [(jest '__') (jest '__')] + ;~ plug (easy '_') + %- plus ;~ pose :: Display text can contain various contents + escape + emphasis + link + autolink + code + image + link + softbrk + hardbrk + %+ knee *text:inline:m |. ~+ :: recurse + %+ cook |=(a=text:inline:m a) + %+ stag %text + %+ cook crip + %- plus :: At least one character + ;~ less :: ...which doesn't match any other inline rule + escape + emphasis + link + autolink + code + image + link + softbrk + hardbrk + :: + (jest '__') :: If a '**', then it's the end of the `emphasis` + prn + == + == + == + == + :: + ++ code + =< %+ cook |=(a=code:inline:m a) + %+ stag %code-span + inner-parser + |% + ++ inner-parser + |= =nail + =/ vex ((plus tic) nail) :: Read the first backtick string + ?~ q.vex vex :: If no vex is found, fail + =/ tic-sequence ^- tape p:(need q.vex) + %. + q:(need q.vex) + %+ cook |= [a=tape] :: Attach the backtick length to it + [(lent tic-sequence) (crip a)] + ;~ sfix + %+ cook + |= [a=(list tape)] + ^- tape + (zing a) + %- star ;~ pose + %+ cook trip ;~(less tic prn) :: Any character other than a backtick + %+ sear :: A backtick string that doesn't match the opener + |= [a=tape] + ^- (unit tape) + ?: =((lent a) (lent tic-sequence)) + ~ + `a + (plus tic) + == + (jest (crip tic-sequence)) :: Followed by a closing backtick string + == + -- + -- + :: + ++ leaf + |% + ++ node + %+ cook |=(a=node:leaf:m a) + ;~ pose + blank-line + heading + break + codeblk-indent + codeblk-fenced + link-ref-def + :: ...etc + table + paragraph + == + ++ blank-line + %+ cook |=(a=blank-line:leaf:m a) + %+ stag %blank-line + (cold ~ newline) + ++ heading + =< %+ cook |=(a=heading:leaf:m a) + %+ stag %heading + ;~(pose atx setext) + |% + ++ atx + =/ atx-eol ;~ plug + (star ace) + (star hax) + (star ace) + line-end + == + + %+ stag %atx + %+ cook :: Parse heading inline content + |= [level=@ text=tape] + [level (scan text contents:inline)] + ;~ pfix + (stun [0 3] ace) :: Ignore up to 3 leading spaces + ;~ plug + (cook |=(a=tape (lent a)) (stun [1 6] hax)) :: Heading level + %+ ifix [(plus ace) atx-eol] :: One leading space is required; rest is ignored + %- star + ;~(less atx-eol prn) :: Trailing haxes/spaces are ignored + == + == + ++ setext + %+ stag %setext + %+ cook + |= [text=tape level=@] + [level (scan text contents:inline)] + ;~ plug :: Wow this is a mess + %+ ifix [(stun [0 3] ace) (star ace)] :: Strip up to 3 spaces, and trailing space + (star ;~(less ;~(pfix (star ace) newline) prn)) :: Any text... + ;~ pfix + newline :: ...followed by newline... + (stun [0 3] ace) :: ...up to 3 spaces (stripped)... + ;~ sfix + ;~ pose :: ...and an underline + (cold 1 (plus (just '-'))) :: Underlined by '-' means heading lvl 1 + (cold 2 (plus (just '='))) :: Underlined by '=' means heading lvl 2 + == + (star ace) + == + == + == + -- + ++ break + %+ cook |=(a=break:leaf:m a) + %+ stag %break + %+ cook + |= [first-2=@t trailing=tape] + [(head trailing) (add 2 (lent trailing))] + %+ ifix :- (stun [0 3] ace) :: Strip indent and trailing space + ;~ plug + (star (mask " \09")) + newline :: No other chars allowed on the line + == + ;~ pose + ;~(plug (jest '**') (plus tar)) :: At least 3, but can be more + ;~(plug (jest '--') (plus hep)) + ;~(plug (jest '__') (plus cab)) + == + :: + ++ codeblk-indent + %+ cook |=(a=codeblk-indent:leaf:m a) + %+ stag %indent-codeblock + %+ cook |=(a=(list tape) (crip (zing a))) + %- plus :: 1 or more lines + ;~ pfix + (jest ' ') :: 4 leading spaces + %+ cook snoc ;~ plug + (star ;~(less line-end prn)) + line-end + == + == + :: + ++ codeblk-fenced + =+ |% + :: Returns a 3-tuple: + :: - indent size + :: - char type + :: - fence length + ++ code-fence + ;~ plug + %+ cook |=(a=tape (lent a)) (stun [0 3] ace) + %+ cook |=(a=tape [(head a) (lent a)]) :: Get code fence char and length + ;~ pose + (stun [3 999.999.999] sig) + (stun [3 999.999.999] tic) + == + == + :: + ++ info-string + %+ cook crip + %+ ifix [(star ace) line-end] :: Strip leading whitespace + (star ;~(less line-end tic prn)) :: No backticks in a code fence + -- + |* =nail + :: Get the marker and indent size + =/ vex (code-fence nail) + ?~ q.vex vex :: If no match found, fail + =/ [indent=@ char=@t len=@] p:(need q.vex) + =/ closing-fence + ;~ plug + (star ace) + (stun [len 999.999.999] (just char)) :: Closing fence must be at least as long as opener + (star ace) :: ...and cannot have any following text except space + line-end + == + :: Read the rest of the list item block + %. + q:(need q.vex) + %+ cook |=(a=codeblk-fenced:leaf:m a) + %+ stag %fenced-codeblock + ;~ plug + %+ cook |=(a=@t a) (easy char) + (easy len) + %+ cook |=(a=@t a) info-string + (easy indent) + %+ cook |=(a=(list tape) (crip (zing a))) + ;~ sfix + %- star :: Any amount of lines + ;~ less closing-fence :: ...until the closing code fence + ;~ pfix (stun [0 indent] ace) :: Strip indent up to that of the opening fence + %+ cook |=(a=tape a) + ;~ pose :: Avoid infinite loop at EOF + %+ cook trip newline :: A line is either a blank line... + %+ cook snoc + ;~ plug :: Or a non-blank line + (plus ;~(less line-end prn)) + line-end + == + == + == + == + ;~(pose closing-fence (full (easy ~))) + == + == + :: + ++ link-ref-def + %+ cook |=(a=link-ref-def:leaf:m a) + %+ stag %link-ref-definition + %+ ifix [(stun [0 3] ace) line-end] :: Strip leading space + ;~ plug + ;~(sfix label:ln col) :: Label (enclosed in "[...]"), followed by col ":" + ;~ pfix :: Optional whitespace, including up to 1 newline + (star ace) + (stun [0 1] newline) + (star ace) + urlt:ln + == + == + :: + ++ paragraph + %+ cook |=(a=paragraph:leaf:m a) + %+ stag %paragraph + %+ cook :: Reparse the paragraph text as elements + |= [a=(list tape)] + (scan (zing a) contents:inline) + %- plus :: Read lines until a non-paragraph object is found + ;~ less + heading + break + block-quote-line:container :: Block quotes can interrupt paragraphs + %+ cook snoc ;~ plug + %- plus ;~(less line-end prn) :: Lines must be non-empty + line-end + == + == + :: + ++ table + => |% + +$ cell-t [len=@ =contents:inline:m] + ++ row + ;~ pfix bar :: A bar in front... + %- star + %+ cook :: compute the length and parse inlines + |= [pfx=@ stuff=tape sfx=@] + [;:(add pfx (lent stuff) sfx) (scan stuff contents:inline)] :: inline elements... + ;~ plug + (cook lent (star ace)) + (star ;~(less newline ;~(plug (star ace) bar) prn)) + (cook lent ;~(sfix (star ace) bar)) + == + == + ++ delimiter-row + ;~ pfix bar :: A bar in front... + %- star + %+ cook + |= [pfx=@ lal=? heps=@ ral=? sfx=@] + :- ;:(add pfx ?:(ral 1 0) heps ?:(lal 1 0) sfx) + ?:(ral ?:(lal %c %r) ?:(lal %l %n)) + ;~ plug + (cook lent (star ace)) :: Delimiter: leading space... + (cook |=(a=tape .?(a)) (stun [0 1] col)) :: maybe a ':'... + (cook lent (plus hep)) :: a bunch of '-'... + (cook |=(a=tape .?(a)) (stun [0 1] col)) :: maybe another ':'... + (cook lent ;~(sfix (star ace) bar)) :: ..and a bar as a terminator + == + == + -- + |* =nail :: Make it a (redundant) gate so I can use `=>` to add a helper core + %. nail :: apply the following parser + %+ cook + |= [hdr=(list cell-t) del=(list [len=@ al=?(%c %r %l %n)]) bdy=(list (list cell-t))] + ^- table:leaf:m + =/ widths=(list @) (turn del |=([len=@ al=*] len)) + =/ rows=(list (list cell-t)) (snoc bdy hdr) :: since they're the same data type + =/ computed-widths + |- + ?~ rows widths + %= $ + rows (tail rows) + widths =/ row=(list cell-t) (head rows) + |- + ?~ row ~ + :- (max (head widths) len:(head row)) + %= $ + widths (tail widths) + row (tail row) + == + == + :* %table + computed-widths + (turn hdr |=(cell=cell-t contents.cell)) + (turn del |=([len=@ al=?(%c %r %l %n)] al)) + (turn bdy |=(row=(list cell-t) (turn row |=(cell=cell-t contents.cell)))) + == + ;~ plug + ;~(sfix row line-end) + ;~(sfix delimiter-row line-end) + (star ;~(sfix row line-end)) + == + -- + :: + ++ container + =+ |% + :: + ++ line :: Read a line of plain text + %+ cook |=([a=tape b=tape c=tape] ;:(weld a b c)) + ;~ plug + (star ;~(less line-end prn)) + (cook trip line-end) + (star newline) :: Can have blank lines in a list item + == + ++ block-quote-marker + ;~ plug :: Single char '>' + (stun [0 3] ace) :: Indented up to 3 spaces + gar + (stun [0 1] ace) :: Optionally followed by a space + == + ++ block-quote-line + %+ cook snoc + ;~ plug :: Single line... + ;~ pfix block-quote-marker :: ...starting with ">..." + (star ;~(less line-end prn)) :: can be empty + == + line-end + == + :: + +$ ul-marker-t [indent=@ char=@t len=@] + ++ ul-marker + %+ cook :: Compute the length of the whole thing + |= [prefix=tape bullet=@t suffix=tape] + ^- ul-marker-t + :* (lent prefix) + bullet + ;:(add 1 (lent prefix) (lent suffix)) + == + ;~ plug + (stun [0 3] ace) + ;~(pose hep lus tar) :: Bullet char + (stun [1 4] ace) + == + :: + :: Produces a 3-tuple: + :: - bullet char (*, +, or -) + :: - indent level (number of spaces before the bullet) + :: - item contents (markdown) + +$ ul-item-t [char=@t indent=@ =markdown:m] + ++ ul-item + |* =nail + :: Get the marker and indent size + =/ vex (ul-marker nail) + ?~ q.vex vex :: If no match found, fail + =/ mrkr=ul-marker-t p:(need q.vex) + :: Read the rest of the list item block + %. + q:(need q.vex) + %+ cook + |= [a=(list tape)] + ^- ul-item-t + :* char.mrkr + indent.mrkr + (scan (zing a) markdown) + == + ;~ plug + line :: First line + %- star ;~ pfix :: Subsequent lines must have the same indent + (stun [len.mrkr len.mrkr] ace) :: the indent + line :: the line + == + == + :: + +$ ol-marker-t [indent=@ char=@t number=@ len=@] + ++ ol-marker + %+ cook :: Compute the length of the whole thing + |= [prefix=tape number=@ char=@t suffix=tape] + ^- ol-marker-t + :* (lent prefix) + char + number + ;:(add 1 (lent (a-co:co number)) (lent prefix) (lent suffix)) + == + ;~ plug + (stun [0 3] ace) + dem + ;~(pose dot par) :: Bullet char + (stun [1 4] ace) + == + :: + :: Produces a 4-tuple: + :: - delimiter char: either dot '.' or par ')' + :: - list item number + :: - indent level (number of spaces before the number) + :: - item contents (markdown) + +$ ol-item-t [char=@t number=@ indent=@ =markdown:m] + ++ ol-item + |* =nail + ::^- edge + :: Get the marker and indent size + =/ vex (ol-marker nail) + ?~ q.vex vex :: If no match found, fail + =/ mrkr=ol-marker-t p:(need q.vex) + :: Read the rest of the list item block + %. + q:(need q.vex) + %+ cook + |= [a=(list tape)] + ^- ol-item-t + :* char.mrkr + number.mrkr + indent.mrkr + (scan (zing a) markdown) + == + ;~ plug + line :: First line + %- star ;~ pfix :: Subsequent lines must have the same indent + (stun [len.mrkr len.mrkr] ace) :: the indent + line :: the line + == + == + :: + ++ tl-checkbox + ;~ pose + %+ cold %.y (jest '[x]') + %+ cold %.n (jest '[ ]') + == + :: + :: Produces a 4-tuple: + :: - bullet char (*, +, or -) + :: - indent level (number of spaces before the bullet) + :: - is-checked + :: - item contents (markdown) + +$ tl-item-t [char=@t indent=@ is-checked=? =markdown:m] + ++ tl-item + |* =nail + :: Get the marker and indent size + =/ vex (;~(plug ul-marker ;~(sfix tl-checkbox ace)) nail) + ?~ q.vex vex :: If no match found, fail + =/ [mrkr=ul-marker-t is-checked=?] p:(need q.vex) + :: Read the rest of the list item block + %. + q:(need q.vex) + %+ cook + |= [a=(list tape)] + ^- tl-item-t + :* char.mrkr + indent.mrkr + is-checked + (scan (zing a) markdown) + == + ;~ plug + line :: First line + %- star ;~ pfix :: Subsequent lines must have the same indent + (stun [len.mrkr len.mrkr] ace) :: the indent + line :: the line + == + == + -- + |% + ++ node + %+ cook |=(a=node:container:m a) + ;~ pose + block-quote + tl + ul + ol + == + :: + ++ block-quote + %+ cook |=(a=block-quote:container:m a) + %+ stag %block-quote + %+ cook |= [a=(list tape)] + (scan (zing a) markdown) + ;~ plug + block-quote-line + %- star :: At least one line + ;~ pose + block-quote-line + %+ cook zing %- plus :: Paragraph continuation (copied from `paragraph` above) + ;~ less :: ...basically just text that doesn't matchZ anything else + heading:leaf + break:leaf + :: ol + :: ul + block-quote-marker :: Can't start with ">" + line-end :: Can't be blank + %+ cook snoc ;~ plug + %- star ;~(less line-end prn) + line-end + == + == + == + == + :: + ++ ul + |* =nail + :: Start by finding the type of the first bullet (indent level and bullet char) + =/ vex (ul-item nail) + ?~ q.vex vex :: Fail if it doesn't match a list item + =/ first-item=ul-item-t p:(need q.vex) + :: Check for more list items + %. + q:(need q.vex) + %+ cook |=(a=ul:container:m a) + %+ stag %ul + ;~ plug :: Give the first item, first + (easy indent.first-item) + (easy char.first-item) + (easy markdown.first-item) + %- star + %+ sear :: Reject items that don't have the same bullet char + |= [item=ul-item-t] + ^- (unit markdown:m) + ?. =(char.item char.first-item) + ~ + `markdown.item + ul-item + == + :: + ++ ol + |* =nail + :: Start by finding the first number, char, and indent level + =/ vex (ol-item nail) + ?~ q.vex vex :: Fail if it doesn't match a list item + =/ first-item=ol-item-t p:(need q.vex) + :: Check for more list items + %. + q:(need q.vex) + %+ cook |=(a=ol:container:m a) + %+ stag %ol + ;~ plug :: Give the first item, first + (easy indent.first-item) + (easy char.first-item) + (easy number.first-item) + (easy markdown.first-item) + %- star + %+ sear :: Reject items that don't have the same delimiter + |= [item=ol-item-t] + ^- (unit markdown:m) + ?. =(char.item char.first-item) + ~ + `markdown.item + ol-item + == + :: + ++ tl + |* =nail + :: Start by finding the type of the first bullet (indent level and bullet char) + =/ vex (tl-item nail) + ?~ q.vex vex :: Fail if it doesn't match a list item + =/ first-item=tl-item-t p:(need q.vex) + :: Check for more list items + %. + q:(need q.vex) + %+ cook |=(a=tl:container:m a) + %+ stag %tl + ;~ plug :: Give the first item, first + (easy indent.first-item) + (easy char.first-item) + (easy [is-checked.first-item markdown.first-item]) + %- star + %+ sear :: Reject items that don't have the same bullet char + |= [item=tl-item-t] + ^- (unit [is-checked=? markdown:m]) + ?. =(char.item char.first-item) + ~ + `[is-checked.item markdown.item] + tl-item + == + -- + :: + ++ markdown + %+ cook |=(a=markdown:m a) + %- star ;~ pose + (stag %container node:container) + (stag %leaf node:leaf) + == + -- + :: + :: Enserialize (write out as text) + ++ en + |% + ++ escape-chars + |= [text=@t chars=(list @t)] + ^- tape + %+ rash text + %+ cook + |=(a=(list tape) `tape`(zing a)) + %- star ;~ pose + (cook |=(a=@t `tape`~['\\' a]) (mask chars)) + (cook trip prn) + == + :: + ++ ln + |% + ++ url + =< |= [u=url:ln:m] + ^- tape + ?: has-triangle-brackets.u + (with-triangles text.u) + (without-triangles text.u) + |% + ++ with-triangles + |= [text=@t] + ;: weld + "<" :: Put it inside triangle brackets + (escape-chars text "<>") :: Escape triangle brackets in the text + ">" + == + ++ without-triangles + |= [text=@t] + (escape-chars text "()") :: Escape all parentheses '(' and ')' + -- + ++ urlt + |= [u=urlt:ln:m] + ^- tape + ?~ title-text.u :: If there's no title text, then it's just an url + (url url.u) + ;:(weld (url url.u) " \"" (escape-chars (need title-text.u) "\"") "\"") + ++ label + |= [text=@t] + ^- tape + ;:(weld "[" (escape-chars text "[]") "]") + ++ target + |= [t=target:ln:m] + ^- tape + ?- -.t + %direct ;:(weld "(" (urlt urlt.t) ")") :: Wrap in parentheses + :: + %ref ?- type.t + %full (label label.t) + %collapsed "[]" + %shortcut "" + == + == + -- + :: + ++ inline + |% + ++ contents + |= [=contents:inline:m] + ^- tape + %- zing %+ turn contents element + ++ element + |= [e=element:inline:m] + ?+ -.e !! + %text (text e) + %link (link e) + %escape (escape e) + %entity (entity e) + %code-span (code e) + %strong (strong e) + %emphasis (emphasis e) + %soft-line-break (softbrk e) + %line-break (hardbrk e) + %image (image e) + %autolink (autolink e) + :: ...etc + == + ++ text + |= [t=text:inline:m] + ^- tape + (trip text.t) :: So easy! + :: + ++ entity + |= [e=entity:inline:m] + ^- tape + ;:(weld "&" (trip code.e) ";") + :: + ++ link + |= [l=link:inline:m] + ^- tape + ;: weld + "[" + (contents contents.l) + "]" + (target:ln target.l) + == + :: + ++ image + |= [i=image:inline:m] + ^- tape + ;: weld + "![" + (escape-chars alt-text.i "]") + "]" + (target:ln target.i) + == + :: + ++ autolink + |= [a=autolink:inline:m] + ^- tape + ;: weld + "<" + (trip text.a) + ">" + == + :: + ++ escape + |= [e=escape:inline:m] + ^- tape + (snoc "\\" char.e) :: Could use `escape-chars` but why bother-- this is shorter + :: + ++ softbrk + |= [s=softbrk:inline:m] + ^- tape + "\0a" + ++ hardbrk + |= [h=hardbrk:inline:m] + ^- tape + "\\\0a" + ++ code + |= [c=code:inline:m] + ^- tape + ;:(weld (reap num-backticks.c '`') (trip text.c) (reap num-backticks.c '`')) + :: + ++ strong + |= [s=strong:inline:m] + ^- tape + ;: weld + (reap 2 emphasis-char.s) + (contents contents.s) + (reap 2 emphasis-char.s) + == + :: + ++ emphasis + |= [e=emphasis:inline:m] + ^- tape + ;: weld + (trip emphasis-char.e) + (contents contents.e) + (trip emphasis-char.e) + == + -- + :: + ++ leaf + |% + ++ node + |= [n=node:leaf:m] + ?+ -.n !! + %blank-line (blank-line n) + %break (break n) + %heading (heading n) + %indent-codeblock (codeblk-indent n) + %fenced-codeblock (codeblk-fenced n) + %link-ref-definition (link-ref-def n) + %paragraph (paragraph n) + %table (table n) + :: ...etc + == + + ++ blank-line + |= [b=blank-line:leaf:m] + ^- tape + "\0a" + :: + ++ break + |= [b=break:leaf:m] + ^- tape + (weld (reap char-count.b char.b) "\0a") + :: + ++ heading + |= [h=heading:leaf:m] + ^- tape + ?- style.h + %atx + ;:(weld (reap level.h '#') " " (contents:inline contents.h) "\0a") + %setext + =/ line (contents:inline contents.h) + ;:(weld line "\0a" (reap (lent line) ?:(=(level.h 1) '-' '=')) "\0a") + == + :: + ++ codeblk-indent + |= [c=codeblk-indent:leaf:m] + ^- tape + %+ rash text.c + %+ cook + |= [a=(list tape)] + ^- tape + %- zing %+ turn a |=(t=tape (weld " " t)) + %- plus %+ cook snoc ;~(plug (star ;~(less (just '\0a') prn)) (just '\0a')) + :: + ++ codeblk-fenced + |= [c=codeblk-fenced:leaf:m] + ^- tape + ;: weld + (reap indent-level.c ' ') + (reap char-count.c char.c) + (trip info-string.c) + "\0a" + ^- tape %+ rash text.c + %+ cook zing %- star :: Many lines + %+ cook |= [a=tape newline=@t] :: Prepend each line with "> " + ^- tape + ;: weld + ?~(a "" (reap indent-level.c ' ')) :: If the line is blank, no indent + a + "\0a" + == + ;~ plug :: Break into lines + (star ;~(less (just '\0a') prn)) + (just '\0a') + == + (reap indent-level.c ' ') + (reap char-count.c char.c) + "\0a" + == + :: + ++ link-ref-def + |= [l=link-ref-def:leaf:m] + ^- tape + ;: weld + "[" + (trip label.l) + "]: " + (urlt:ln urlt.l) + "\0a" + == + :: + ++ table + => |% + ++ cell + |= [width=@ c=contents:inline:m] + ^- tape + =/ contents-txt (contents:inline c) + ;: weld + " " + contents-txt + (reap (sub width (add 1 (lent contents-txt))) ' ') + "|" + == + ++ row + |= [widths=(list @) cells=(list contents:inline:m)] + ^- tape + ;: weld + "|" + |- + ^- tape + ?~ widths ~ + %+ weld + (cell (head widths) (head cells)) + $(widths (tail widths), cells (tail cells)) + "\0a" + == + ++ delimiter-row + |= [widths=(list @) align=(list ?(%l %c %r %n))] + ^- tape + ;: weld + "|" + |- + ^- tape + ?~ align ~ + ;: weld + " " + ?- (head align) + %l (weld ":" (reap ;:(sub (head widths) 3) '-')) + %r (weld (reap ;:(sub (head widths) 3) '-') ":") + %c ;:(weld ":" (reap ;:(sub (head widths) 4) '-') ":") + %n (reap ;:(sub (head widths) 2) '-') + == + " |" + $(align (tail align), widths (tail widths)) + == + "\0a" + == + -- + |= [t=table:leaf:m] + ^- tape + ;: weld + (row widths.t head.t) + (delimiter-row widths.t align.t) + =/ rows rows.t + |- + ^- tape + ?~ rows ~ + %+ weld (row widths.t (head rows)) $(rows (tail rows)) + == + :: + ++ paragraph + |= [p=paragraph:leaf:m] + ^- tape + (contents:inline contents.p) + -- + :: + ++ container + => |% + ++ line + %+ cook snoc + ;~ plug + (star ;~(less (just '\0a') prn)) + (just '\0a') + == + -- + |% + ++ node + |= [n=node:container:m] + ?- -.n + %block-quote (block-quote n) + %ul (ul n) + %ol (ol n) + %tl (tl n) + == + :: + ++ block-quote + |= [b=block-quote:container:m] + ^- tape + %+ scan (markdown markdown.b) :: First, render the contents + %+ cook zing %- plus :: Many lines + %+ cook |= [a=tape newline=@t] :: Prepend each line with "> " + ^- tape + ;: weld + ">" + ?~(a "" " ") :: If the line is blank, no trailing space + a + "\0a" + == + ;~ plug :: Break into lines + (star ;~(less (just '\0a') prn)) + (just '\0a') + == + :: + ++ ul + |= [u=ul:container:m] + ^- tape + %- zing %+ turn contents.u :: Each bullet point... + |= [item=markdown:m] + ^- tape + %+ scan (markdown item) :: First, render bullet point contents + %+ cook zing + ;~ plug + %+ cook |= [a=tape] :: Prepend 1st line with indent + bullet char + ;: weld + (reap indent-level.u ' ') + (trip marker-char.u) + " " + a + == + line :: first line + %- star + %+ cook |= [a=tape] :: Subsequent lines just get indent + ?: ?|(=("" a) =("\0a" a)) a + ;: weld + (reap indent-level.u ' ') + " " :: 2 spaces, to make it even with the 1st line + a + == + line :: second and thereafter lines + == + ++ tl + |= [t=tl:container:m] + ^- tape + %- zing %+ turn contents.t :: Each bullet point... + |= [is-checked=? item=markdown:m] + ^- tape + %+ scan (markdown item) :: First, render bullet point contents + %+ cook zing + ;~ plug + %+ cook |= [a=tape] :: Prepend 1st line with indent, bullet char, checkbox + ;: weld + (reap indent-level.t ' ') + (trip marker-char.t) + " [" + ?:(is-checked "x" " ") + "] " + a + == + line :: first line + %- star + %+ cook |= [a=tape] :: Subsequent lines just get indent + ?: ?|(=("" a) =("\0a" a)) a + ;: weld + (reap indent-level.t ' ') + " " :: 2 spaces, to make it even with the 1st line + a + == + line :: second and thereafter lines + == + :: + ++ ol + |= [o=ol:container:m] + ^- tape + %- zing %+ turn contents.o :: Each item... + |= [item=markdown:m] + ^- tape + %+ scan (markdown item) :: First, render item contents + %+ cook zing + ;~ plug + %+ cook |= [a=tape] :: Prepend 1st line with indent + item number + ;: weld + (reap indent-level.o ' ') + (a-co:co start-num.o) + (trip marker-char.o) + " " + a + == + line :: first line + %- star + %+ cook |= [a=tape] :: Subsequent lines just get indent + ?: ?|(=("" a) =("\0a" a)) a + ;: weld + (reap indent-level.o ' ') + (reap (lent (a-co:co start-num.o)) ' ') + " " :: 2 spaces, to make it even with the 1st line + a + == + line :: second and thereafter lines + == + -- + :: + ++ markdown + |= [a=markdown:m] + ^- tape + %- zing %+ turn a |= [item=node:markdown:m] + ?- -.item + %leaf (node:leaf +.item) + %container (node:container +.item) + == + -- + -- + :: + :: Enserialize as Sail (manx and marl) + ++ sail-en + =< + |= [document=markdown:m] + =/ link-ref-defs (all-link-ref-definitions document) + ^- manx + ;div + ;* (~(markdown sail-en link-ref-defs) document) + == + :: + |_ [reference-links=(map @t urlt:ln:m)] + ++ inline + |% + ++ contents + |= [=contents:inline:m] + ^- marl + %+ turn contents element + ++ element + |= [e=element:inline:m] + ^- manx + ?+ -.e !! + %text (text e) + %link (link e) + %code-span (code e) + %escape (escape e) + %entity (entity e) + %strong (strong e) + %emphasis (emphasis e) + %soft-line-break (softbrk e) + %line-break (hardbrk e) + %image (image e) + %autolink (autolink e) + :: ...etc + == + ++ text + |= [t=text:inline:m] + ^- manx + [[%$ [%$ (trip text.t)] ~] ~] :: Magic; look up the structure of a `manx` if you want + ++ escape + |= [e=escape:inline:m] + ^- manx + [[%$ [%$ (trip char.e)] ~] ~] :: Magic; look up the structure of a `manx` if you want + ++ entity + |= [e=entity:inline:m] + ^- manx + =/ fulltext (crip ;:(weld "&" (trip code.e) ";")) + [[%$ [%$ `tape`[fulltext ~]] ~] ~] :: We do a little sneaky + ++ softbrk + |= [s=softbrk:inline:m] + ^- manx + (text [%text ' ']) + ++ hardbrk + |= [h=hardbrk:inline:m] + ^- manx + ;br; + ++ code + |= [c=code:inline:m] + ^- manx + ;code: {(trip text.c)} + ++ link + |= [l=link:inline:m] + ^- manx + =/ target target.l + =/ urlt ?- -.target + %direct urlt.target :: Direct link; use it + %ref :: Ref link; look it up + ~| "reflink not found: {<label.target>}" + (~(got by reference-links) label.target) + == + ;a(href (trip text.url.urlt), title (trip (fall title-text.urlt ''))) + ;* (contents contents.l) + == + ++ image + |= [i=image:inline:m] + ^- manx + =/ target target.i + =/ urlt ?- -.target + %direct urlt.target :: Direct link; use it + %ref :: Ref link; look it up + ~| "reflink not found: {<label.target>}" + (~(got by reference-links) label.target) + == + ;img(src (trip text.url.urlt), alt (trip alt-text.i)); + ++ autolink + |= [a=autolink:inline:m] + ^- manx + ;a(href (trip text.a)): {(trip text.a)} + ++ emphasis + |= [e=emphasis:inline:m] + ^- manx + ;em + ;* (contents contents.e) + == + ++ strong + |= [s=strong:inline:m] + ^- manx + ;strong + ;* (contents contents.s) + == + -- + ++ leaf + |% + ++ node + |= [n=node:leaf:m] + ^- manx + ?+ -.n !! + %blank-line (blank-line n) + %break (break n) + %heading (heading n) + %indent-codeblock (codeblk-indent n) + %fenced-codeblock (codeblk-fenced n) + %table (table n) + %paragraph (paragraph n) + %link-ref-definition (text:inline [%text ' ']) :: Link ref definitions don't render as anything + :: ...etc + == + ++ heading + |= [h=heading:leaf:m] + ^- manx + :- + :_ ~ ?+ level.h !! :: Tag and attributes; attrs are empty (~) + %1 %h1 + %2 %h2 + %3 %h3 + %4 %h4 + %5 %h5 + %6 %h6 + == + (contents:inline contents.h) + ++ blank-line + |= [b=blank-line:leaf:m] + ^- manx + (text:inline [%text ' ']) + ++ break + |= [b=break:leaf:m] + ^- manx + ;hr; + ++ codeblk-indent + |= [c=codeblk-indent:leaf:m] + ^- manx + ;pre + ;code: {(trip text.c)} + == + ++ codeblk-fenced + |= [c=codeblk-fenced:leaf:m] + ^- manx + ;pre + ;+ ?: =(info-string.c '') + ;code: {(trip text.c)} + ;code(class (weld "language-" (trip info-string.c))): {(trip text.c)} + == + ++ table + |= [t=table:leaf:m] + ^- manx + ;table + ;thead + ;tr + ;* =/ hdr head.t + =/ align align.t + |- + ?~ hdr ~ + :- ;th(align ?-((head align) %c "center", %r "right", %l "left", %n "")) + ;* (contents:inline (head hdr)) + == + $(hdr (tail hdr), align (tail align)) + + == + == + ;tbody + ;* %+ turn rows.t + |= [r=(list contents:inline:m)] + ^- manx + ;tr + ;* =/ row r + =/ align align.t + |- + ?~ row ~ + :- ;td(align ?-((head align) %c "center", %r "right", %l "left", %n "")) + ;* (contents:inline (head row)) + == + $(row (tail row), align (tail align)) + == + == + == + ++ paragraph + |= [p=paragraph:leaf:m] + ^- manx + ;p + ;* (contents:inline contents.p) + == + -- + :: + ++ container + |% + ++ node + |= [n=node:container:m] + ^- manx + ?- -.n + %block-quote (block-quote n) + %ul (ul n) + %ol (ol n) + %tl (tl n) + == + :: + ++ block-quote + |= [b=block-quote:container:m] + ^- manx + ;blockquote + ;* (~(. markdown reference-links) markdown.b) + == + :: + ++ ul + |= [u=ul:container:m] + ^- manx + ;ul + ;* %+ turn contents.u |= [a=markdown:m] + ^- manx + ;li + ;* (~(. markdown reference-links) a) + == + == + :: + ++ ol + |= [o=ol:container:m] + ^- manx + ;ol(start (a-co:co start-num.o)) + ;* %+ turn contents.o |= [a=markdown:m] + ^- manx + ;li + ;* (~(. markdown reference-links) a) + == + == + ++ tl + |= [t=tl:container:m] + ^- manx + ;ul.task-list + ;* %+ turn contents.t |= [is-checked=? a=markdown:m] + ^- manx + ;li + ;+ ?: is-checked + ;input(type "checkbox", checked "true"); + ;input(type "checkbox"); + ;* (~(. markdown reference-links) a) + == + == + -- + :: + ++ markdown + |= [a=markdown:m] + ^- marl + %+ turn a |= [item=node:markdown:m] + ?- -.item + %leaf (node:leaf +.item) + %container (node:container +.item) + == + -- +-- diff --git a/desk/lib/nostril/mutations.hoon b/desk/lib/nostril/mutations.hoon new file mode 100644 index 0000000..68c82d3 --- /dev/null +++ b/desk/lib/nostril/mutations.hoon @@ -0,0 +1,83 @@ +/- sur=nostril, post +/+ shim, postlib=nostril-post, sr=sortug +|_ [=state:sur =bowl:gall] +++ process-events ^- (quip card _state) + =/ l events.state + =| cards=(list card:agent:gall) + |- ?~ l [cards state] + =/ n (event-parsing i.l) + $(cards -.n, state +.n, l t.l) + +++ event-parsing ^- (quip card _state) + |= =event:sur + |^ +:: https://nostrdata.github.io/kinds/ + ?: .=(kind.event 0) :: user metadata + parse-metadata + ?: .=(kind.event 1) :: apparently a poast, not sure which NIP + parse-poast + ?: .=(kind.event 2) :: recommended reay + parse-poast + ?: .=(kind.event 3) :: follow list + parse-follow + :: ?: .=(kind.event 5) :: delete + ?: .=(kind.event 6) :: RT + parse-follow + ?: .=(kind.event 7) :: Reaction + parse-follow + + `state + + ++ parse-metadata + =/ jstring content.event + =/ ujon=json (de:json:html jstring) + ?~ ujon !! + =/ =user-meta:sur (metadata:dejs:shim u.ujon) + =/ fid (~(get by following.state) pubkey.event) + ?~ fid `state + =/ nfid [user-meta feed.u.fid] + =. following.state (~(put by following.state) pubkey.event nfid) + `state + + + ++ parse-poast |= =event:sur + =/ fid (~(get by following.state) pubkey.event) + ?~ fid `state :: don't save post if we don't follow the fucker + + =/ cl (tokenize:postlib content.event) + + =/ ts (from-unix:jikan:sr created-at.event) + =/ cm (init-content-map:postlib cl ts) + + =/ =post:post :* + id=ts + author=pubkey.event + thread=~ + parent=~ + contents=cm + read=*lock:gate + write=*lock:gate + *engagement:post + 0 + *signature:post + tags=~ + == + =/ nfid (put:orm:post u.fid ts post) + =. following.state (~(put by following.state) pubkey.event nfid) + `state + + + ++ parse-follow + %+ turn tags.event |= t=tag:sur + ?> .=('p' key.t) + =/ pubkey value.t + =/ relay (snag 0 rest.t) + =/ rel ?: .=(relay '') ~ (some relay) + =/ nickname (snag 1 rest.t) + =/ following (~(get by follow-graph.state) pubkey.event) + =/ meta [pubkey nickname rel] + =/ nfollowing ?~ following (silt ~[meta]) (~(put in u.following) meta) + =. following-graph.state (~(put by follow-graph.state) pubkey.event nfollowing) + `state + -- +-- diff --git a/desk/lib/nostril/post.hoon b/desk/lib/nostril/post.hoon new file mode 100644 index 0000000..134ed27 --- /dev/null +++ b/desk/lib/nostril/post.hoon @@ -0,0 +1,330 @@ +/- tp=post, md=markdown +/+ sr=sortug, mdlib=markdown +|% +:: new! using wispem's lib +++ tokenise +|= t=@t ^- (each content-list:tp @t) + =/ parsed (rush t markdown:de:md:mdlib) + ?~ parsed [%| 'parsing error'] + :- %& + %+ turn u.parsed de-node +++ de-node |= =node:markdown:md ^- block:tp + ?~ node [%paragraph ~] + ?- -.node + %leaf (de-leaf +.node) + %container (de-cont +.node) + == + +++ de-leaf |= =node:leaf:markdown:md ^- block:tp + ?~ node [%paragraph ~] + ?- -.node + %heading (de-heading node) + %break [%paragraph :~([%break ~])] + %indent-codeblock [%codeblock text.node ''] + %fenced-codeblock [%codeblock text.node info-string.node] + %html [%codeblock text.node 'html'] + %link-ref-definition [%paragraph :~([%link '' label.node])] + %paragraph [%paragraph (de-inline contents.node)] + %blank-line [%paragraph :~([%break ~])] + %table [%paragraph :~([%break ~])] :: TODO + == +++ de-heading |= h=heading:leaf:markdown:md + :+ %heading (flatten-inline contents.h) + ?: .=(1 level.h) %h1 + ?: .=(2 level.h) %h2 + ?: .=(3 level.h) %h3 + ?: .=(4 level.h) %h4 + ?: .=(5 level.h) %h5 %h6 +++ de-inline |= inls=contents:inline:md + =| res=(list inline:tp) + |- ?~ inls (flop res) + =/ inl i.inls + =/ r=inline:tp ?- -.inl + %escape [%codespan char.inl] + %entity [%codespan code.inl] + %code-span [%codespan text.inl] + %line-break [%break ~] + %soft-line-break [%break ~] + %text [%text text.inl] + %emphasis (de-strong +.inl) + %strong (de-strong +.inl) + %link [%link (de-target target.inl) (flatten-inline contents.inl)] + %image [%img (de-target target.inl) alt-text.inl] + %autolink [%text ''] + %html [%codespan text.inl] + == + $(inls t.inls, res [r res]) +++ de-strong |= [char=@t inls=contents:inline:md] +?: .=('_' char) [%italic (flatten-inline inls)] + [%bold (flatten-inline inls)] +++ de-target |= tar=target:ln:md +:: TODO lotsa stuff here + ?- -.tar + %direct text.url.urlt.tar + %ref label.tar + == +++ flatten-inline |= inls=contents:inline:md ^- @t + =/ res "" + |- ?~ inls (crip res) + =/ inl i.inls + =/ r ?+ -.inl "" + %escape (trip char.inl) + %entity (trip code.inl) + %code-span (trip text.inl) + %text (trip text.inl) + %emphasis (trip (flatten-inline contents.inl)) + %strong (trip (flatten-inline contents.inl)) + %link (trip (flatten-inline contents.inl)) + %image (trip (de-target target.inl)) + %html (trip text.inl) + == + $(inls t.inls, res "{res} {r}") +++ de-cont |= =node:container:markdown:md ^- block:tp + ?~ node [%paragraph ~] + ?- -.node + %block-quote [%blockquote (denest +.node)] + %ol [%list (de-list contents.node) .y] + %ul [%list (de-list contents.node) .n] + %tl [%tasklist (turn contents.node de-task)] + == +++ de-task |= [checked=? mde=markdown:md] ^- task:tp + :_ checked (denest mde) +++ de-list |= lmd=(list markdown:md) ^- (list li:tp) + =| res=(list li:tp) + |- ?~ lmd (flop res) + =/ nodelist i.lmd + =/ blocks %+ turn nodelist de-node + $(lmd t.lmd, res [blocks res]) +++ denest |= mde=markdown:md ^- paragraph:tp + =| res=paragraph:tp + |- ?~ mde (flop res) + =/ block (de-node i.mde) + =/ r=paragraph:tp (break-block block) + =/ nr (weld res r) + $(mde t.mde, res nr) + +++ break-block |= =block:tp ^- paragraph:tp +?+ -.block ~ + %paragraph p.block + %blockquote p.block + %heading :~([%text p.block]) + %codeblock :~([%text code.block]) + %eval :~([%text hoon.block]) + %list (break-list p.block) +== +++ break-list |= lis=(list li:tp) ^- paragraph:tp + =| res=paragraph:tp + |- ?~ lis (flop res) + =/ par (ibreak-list i.lis) + =/ nr (weld res par) + $(lis t.lis, res nr) +++ ibreak-list |= blocks=(list block:tp) ^- paragraph:tp + =| res=paragraph:tp + |- ?~ blocks (flop res) + =/ par (break-block i.blocks) + =/ nr (weld res par) + $(blocks t.blocks, res nr) + +:: tape -> post:trill, parsing user input from Sail ++$ heading $?(%h1 %h2 %h3 %h4 %h5 %h6) + +++ parse :: Markdown parser. Actually udon parser but it'll do + |= s=tape ^- (unit marl:hoot) :: finally + :: Annoying it requires a line break but then parses it as a space wtf + =, vast + (rust s cram:(sail .y)) +++ tokenize +|= s=@t ^- content-list:tp + =/ t (weld (trip s) "\0a") + =/ parsed (parse t) + :: =/ parsed2 (parse:md t) + :: ~& > diary-parser=parsed2 + :: \0a can't be followed by a space. ever. those are the rules + ?~ parsed ~& error-parsing-markdown=t ~ + (marl-to-cl u.parsed) +++ marl-to-cl +|= =marl:hoot ^- content-list:tp + %- flop + %+ roll marl + |= [=tuna:hoot acc=content-list:tp] + :: man this is an annoying type if I ever saw one + ?@ -.tuna acc + =/ blk (manx-to-block tuna) + ?~ blk acc :_ acc u.blk +++ manx-to-block + |= =manx:hoot ^- (unit block:tp) + ?+ n.g.manx ~ + heading %- some [%heading (phead n.g.manx c.manx)] + %p %- some [%paragraph (inline-list c.manx)] + %blockquote %- some [%blockquote (inline-list c.manx)] + %pre %- some [%codeblock (pre c.manx)] + %hr %- some [%paragraph ~[[%break ~]]] + %ul %- some [%list (list-items c.manx) .n] + %ol %- some [%list (list-items c.manx) .y] + :: %table %- some (table-rows c.manx) + == +++ list-items +|= =marl:hoot ^- (list li:clist:tp) +%- flop + %+ roll marl |= [=tuna:hoot acc=(list li:clist:tp)] + ?@ -.tuna acc + ?. ?=(%li n.g.tuna) acc :_ acc (marl-to-cl c.tuna) + ++ phead + |= [h=heading c=marl:hoot] ^- [p=cord q=heading] + :- (get-tag-text c) h +++ inline-list + |= c=marl:hoot ^- (list inline:tp) + %- flop + %+ roll c + |= [=tuna:hoot acc=(list inline:tp)] + ?@ -.tuna acc :_ acc (inline tuna) + ++ inline + |= =manx:hoot ^- inline:tp + ?: ?=(%$ n.g.manx) [%text (get-attrs-text a.g.manx)] + =/ text=@t (get-tag-text c.manx) + ?+ n.g.manx [%text text] + %i [%italic text] + %b [%bold text] + %code [%codespan text] + %br [%break ~] + %a :+ %link (get-attrs-text a.g.manx) (get-tag-text c.manx) + %img :+ %link (get-attr-text a.g.manx %src) (get-attr-text a.g.manx %alt) + == +:: +++ reduce-block +|= c=marl:hoot ^- @t + %+ roll c + |= [=tuna:hoot acc=@t] + ?@ -.tuna acc + ?+ n.g.tuna acc + %p (get-tag-text c.tuna) + == +++ get-attr-text +|= [a=mart:hoot attr=@tas] ^- @t + %- crip %- flop + %+ roll a + |= [[n=mane v=(list beer:hoot)] acc=tape] + ?. .=(attr n) acc + %+ roll v + |= [b=beer:hoot acc=tape] + ?^ b acc [b acc] +++ get-attrs-text :: this assumes we don't care about which attr, which we usually don't +|= a=mart:hoot ^- @t + :: ?: (gte (lent a) 1) + %- crip %- flop + %+ roll a + |= [[n=mane v=(list beer:hoot)] acc=tape] + %+ roll v + |= [b=beer:hoot acc=tape] + ?^ b acc [b acc] +++ get-tag-text +|= c=marl:hoot ^- @t +:: there's only really one child in these things + %+ roll c + |= [=tuna:hoot acc=@t] + ?@ -.tuna acc + %- crip + %- flop + %+ roll a.g.tuna + |= [[n=mane v=(list beer:hoot)] acc=tape] + %+ roll v + |= [b=beer:hoot acc=tape] + ?^ b acc [b acc] + +++ pre + |= c=marl:hoot ^- [cord cord] + :_ '' :: lang not supported, duh + %+ roll c + |= [=tuna:hoot acc=@t] + ?@ -.tuna acc + (get-attrs-text a.g.tuna) + +++ parse-tags +|= t=@t ^- (unit (set @t)) + =/ lst (rush t (csplit:sr com)) + ?~ lst ~ (some (sy u.lst)) +:: post:trill -> (markdown) tape for display on sail +++ block-to-md +|= b=block:tp ^- tape + ?+ -.b "" +%paragraph + %^ foldi:sr p.b "" |= [in=@ud i=inline:tp acc=tape] + =/ il (inline-to-tape i) + ?: .=(+(in) (lent p.b)) + "{acc}{il}" + "{acc}{il} " +%blockquote + %+ weld "> " + %^ foldi:sr p.b "" |= [in=@ud i=inline:tp acc=tape] + =/ il (inline-to-tape i) + ?: .=(+(in) (lent p.b)) + "{acc}{il}" + "{acc}{il} " +%list + %^ foldi:sr p.b "" |= [in=@ud =li:tp acc=tape] + =/ li-tape (content-list-to-md li) + =/ line ?: ordered.b + "{<+(in)>}. {li-tape}" + "- {li-tape}" + ?: .=(+(in) (lent p.b)) + "{acc}{line}" + "{acc}{line}\0a" +%media + ?+ -.media.b "})" +%images %^ foldi:sr p.media.b "" |= [i=@ud [url=@t caption=@t] acc=tape] + =/ line "})" + ?: .=(+(i) (lent p.media.b)) + "{acc}{line}" + "{acc}{line}\0a" + == +%codeblock + """ + ``` + {(trip code.b)} + ``` + """ +%heading =/ dashes=tape ?- q.b + %h1 "# " + %h2 "## " + %h3 "### " + %h4 "#### " + %h5 "##### " + %h6 "###### " + == "{dashes}{(trip p.b)}" +%tasklist "" ::TODO + :: + :: %table acc + :: %eval acc + :: %ref acc + :: %json acc + == +++ content-list-to-md +|= =content-list:tp ^- tape + %^ foldi:sr content-list "" |= [i=@ud b=block:tp acc=tape] + =/ block-tape (block-to-md b) + ?: .=(+(i) (lent content-list)) + "{acc}{block-tape}" + "{acc}{block-tape}\0a\0a" +++ inline-to-tape +|= i=inline:tp ^- tape + ?- -.i + %text (trip p.i) + %italic "_{(trip p.i)}_" + %bold "*{(trip p.i)}*" + %strike "~~{(trip p.i)}~~" + %ship (scow %p p.i) + %codespan "`{(trip p.i)}`" + %link "[{(trip show.i)}]({(trip href.i)})" + %img "})" + %break "\0a" + == +++ tags-to-tape +|= t=(set @t) ^- tape + %^ foldi:sr ~(tap in t) "" |= [i=@ud c=@t acc=tape] + ?: .=(+(i) ~(wyt in t)) + "{acc}{(trip c)}" + "{acc}{(trip c)}," +++ init-content-map |= [cl=content-list:tp date=@da] ^- content-map:tp + (put:corm:tp *content-map:tp date cl) + +-- diff --git a/desk/lib/shim.hoon b/desk/lib/shim.hoon index ad07685..cc8e60e 100644 --- a/desk/lib/shim.hoon +++ b/desk/lib/shim.hoon @@ -46,7 +46,7 @@ [key value rest] - +++ metadata (om so) -- -- diff --git a/desk/mar/docket-0.hoon b/desk/mar/docket-0.hoon new file mode 100644 index 0000000..c3b253b --- /dev/null +++ b/desk/mar/docket-0.hoon @@ -0,0 +1,25 @@ +/+ dock=docket +|_ =docket:dock +++ grow + |% + ++ mime + ^- ^mime + [/text/x-docket (as-octt:mimes:html (spit-docket:mime:dock docket))] + ++ noun docket + ++ json (docket:enjs:dock docket) + -- +++ grab + |% + :: + ++ mime + |= [=mite len=@ud tex=@] + ^- docket:dock + %- need + %- from-clauses:mime:dock + !<((list clause:dock) (slap !>(~) (ream tex))) + + :: + ++ noun docket:dock + -- +++ grad %noun +-- diff --git a/desk/sur/docket.hoon b/desk/sur/docket.hoon new file mode 100644 index 0000000..091c8c9 --- /dev/null +++ b/desk/sur/docket.hoon @@ -0,0 +1,82 @@ +|% +:: ++$ version + [major=@ud minor=@ud patch=@ud] +:: ++$ glob (map path mime) +:: ++$ url cord +:: $glob-location: How to retrieve a glob +:: ++$ glob-reference + [hash=@uvH location=glob-location] +:: ++$ glob-location + $% [%http =url] + [%ames =ship] + == +:: $href: Where a tile links to +:: ++$ href + $% [%glob base=term =glob-reference] + [%site =path] + == +:: $chad: State of a docket +:: ++$ chad + $~ [%install ~] + $% :: Done + [%glob =glob] + [%site ~] + :: Waiting + [%install ~] + [%suspend glob=(unit glob)] + :: Error + [%hung err=cord] + == +:: +:: $charge: A realized $docket +:: ++$ charge + $: =docket + =chad + == +:: +:: $clause: A key and value, as part of a docket +:: +:: Only used to parse $docket +:: ++$ clause + $% [%title title=@t] + [%info info=@t] + [%color color=@ux] + [%glob-http url=cord hash=@uvH] + [%glob-ames =ship hash=@uvH] + [%image =url] + [%site =path] + [%base base=term] + [%version =version] + [%website website=url] + [%license license=cord] + == +:: +:: $docket: A description of JS bundles for a desk +:: ++$ docket + $: %1 + title=@t + info=@t + color=@ux + =href + image=(unit url) + =version + website=url + license=cord + == +:: ++$ charge-update + $% [%initial initial=(map desk charge)] + [%add-charge =desk =charge] + [%del-charge =desk] + == +-- diff --git a/desk/sur/feed.hoon b/desk/sur/feed.hoon new file mode 100644 index 0000000..459d965 --- /dev/null +++ b/desk/sur/feed.hoon @@ -0,0 +1,16 @@ +/- post +|% ++$ feeds (map ship feed) ++$ feed ((mop id:post post:post) gth) ++$ index (map @t (set pid:post)) +++ orm ((on id:post post:post) gth) ++$ full-graph ((mop id:post full-node:post) gth) +++ form ((on id:post full-node:post) gth) ++$ global ((mop pid:post post:post) ggth) +++ ggth |=([[ship a=time] [ship b=time]] (gth a b)) +++ gorm ((on pid:post post:post) ggth) + ++$ cursor (unit @da) ++$ fc [=feed:feed start=cursor end=cursor] ++$ gc [mix=global:feed start=cursor end=cursor] +-- diff --git a/desk/sur/markdown.hoon b/desk/sur/markdown.hoon new file mode 100644 index 0000000..baf266e --- /dev/null +++ b/desk/sur/markdown.hoon @@ -0,0 +1,157 @@ +=> |% + ++ ln + |% + :: + :: Url: optionally enclosed in triangle brackets + :: A link destination consists of either + :: - a sequence of zero or more characters between an opening < and a closing > that + :: contains no line breaks or unescaped < or > characters, or + :: - a nonempty sequence of characters that does not start with <, does not include + :: ASCII space or control characters, and includes parentheses only if (a) they are + :: backslash-escaped or (b) they are part of a balanced pair of unescaped parentheses. + :: (Implementations may impose limits on parentheses nesting to avoid performance + :: issues, but at least three levels of nesting should be supported.) + +$ url [text=@t has-triangle-brackets=?] + :: + :: Url with optional title-text + +$ urlt [=url title-text=(unit @t)] + :: + :: Link target: the part of a link after the display text. can be direct or reference + :: A reference link is in square brackets, and refers to a named link elsewhere. + :: - full => [Display][foo] + :: - collapsed => [Display][] + :: - shortcut => [Display] + :: Collapsed and shortcut links have a `label` equal to the display text. + +$ target $% [%direct =urlt] + [%ref type=?(%full %collapsed %shortcut) label=@t] + == + -- + -- +:: +|% + :: + :: Markdown document or fragment: a list of nodes + ++ markdown =< $+ markdown + (list node) + |% + +$ node $+ markdown-node + $@ ~ :: `$@ ~` is magic that makes recursive structures work + $% [%leaf node:leaf] + [%container node:container] + == + -- + :: + ++ inline + |% + :: A single inline element + ++ element $+ inline-element + $@ ~ + $%(escape entity code hardbrk softbrk text emphasis strong link image autolink html) + :: + :: Any amount of elements + ++ contents (list element) + :: + :: ----------------------- + :: List of inline elements + :: ----------------------- + :: + :: Backslash-escaped character + +$ escape [%escape char=@t] + :: + :: HTML-entity + +$ entity [%entity code=@t] + :: + :: Code span (inline code). Interpreted literally, cannot have nested elements. + :: Can be enclosed by any amount of backticks on each side, >= 1. Must be balanced. + +$ code [%code-span num-backticks=@ text=@t] + :: + :: Line break + +$ hardbrk [%line-break ~] + :: + :: Soft line break: a newline in the source code, will be rendered as a single space + +$ softbrk [%soft-line-break ~] + :: + :: Text: Just text + +$ text [%text text=@t] + :: + :: Emphasis and strong emphasis + :: Can use either tar "*" or cab "_" as the emphasis character. + :: Can have nested inline elements. + +$ emphasis [%emphasis emphasis-char=@t =contents] + +$ strong [%strong emphasis-char=@t =contents] + :: + :: Link + +$ link [%link =contents =target:ln] + :: + :: Images + +$ image [%image alt-text=@t =target:ln] + :: + :: Autolink: a link that's just itself, surrounded by "<...>" + +$ autolink [%autolink text=@t] + :: + :: HTML + +$ html [%html text=@t] + -- + :: + :: Leaf nodes: non-nested (i.e., terminal) nodes + ++ leaf + |% + ++ node $+ leaf-node + $@ ~ + $%(heading break codeblk-indent codeblk-fenced html link-ref-def table paragraph blank-line) + :: + :: Heading, either setext or ATX style + +$ heading [%heading style=?(%setext %atx) level=@ =contents:inline] + :: + :: Thematic break (horizontal line) + :: Consists of at least 3 repetitions of either hep '-', cab '_', or tar '*' + +$ break [%break char=@t char-count=@] + :: + :: Indentation-based code block: indented 4 spaces. Can include newlines and blank lines. + +$ codeblk-indent [%indent-codeblock text=@t] + :: + :: Fenced code block: begins and ends with 3+ repetitions of tic (`) or sig (~). + :: Can be indented up to 3 spaces. + +$ codeblk-fenced [%fenced-codeblock char=@t char-count=@ info-string=@t indent-level=@ text=@t] + :: + :: HTML + +$ html [%html text=@t] + :: + :: Link reference definition (defines a named link which can be referenced elsewhere) + +$ link-ref-def [%link-ref-definition label=@t =urlt:ln] + :: + :: Paragraph + +$ paragraph [%paragraph =contents:inline] + :: + :: Blank lines (not rendered, but lets user control aethetic layout of the source code) + +$ blank-line [%blank-line ~] + :: + :: Table (alignments: [l]eft, [r]ight, [c]enter, [n]one) + +$ table [%table widths=(list @) head=(list contents:inline) align=(list ?(%l %c %r %n)) rows=(list (list contents:inline))] + -- + :: + :: Container node: can contain other nodes (either container or leaf). + ++ container + |% + ++ node $+ container-node + $@ ~ + $%(block-quote ol ul tl) + :: + :: Block quote. Can be nested. + +$ block-quote [%block-quote =markdown] + :: + :: Ordered list: numbered based on first list item marker. + :: Marker char can be either dot '1. asdf' or par '1) asdf' + :: Can be indented up to 3 spaces + +$ ol [%ol indent-level=@ marker-char=@t start-num=@ contents=(list markdown)] :: is-tight=? + :: + :: Unordered list: bullet point list + :: Marker char can be either hep (-), lus (+) or tar (*) + :: Can be indented up to 3 spaces + +$ ul [%ul indent-level=@ marker-char=@t contents=(list markdown)] :: is-tight=? + :: + :: Task list: unordered list of tasks + :: Can be indented up to 3 spaces + +$ tl [%tl indent-level=@ marker-char=@t contents=(list [is-checked=? =markdown])] :: is-tight=? + -- +-- diff --git a/desk/sur/nostril.hoon b/desk/sur/nostril.hoon index 1e51695..87f6611 100644 --- a/desk/sur/nostril.hoon +++ b/desk/sur/nostril.hoon @@ -1,17 +1,25 @@ +/- feed |% ++ shim-url 'http://localhost:8888/shim' +$ state state-0 +$ state-0 $: %0 + :: nostr config relays=(set @t) events=(list event) :: let's limit it to 100 keys=(set keys) + :: feeds + own=feed:feed + following=(map @ux [profile=user-meta =feed:feed]) + follow-graph=(map @ux (set [pubkey=@ux name=@t relay=(unit @t)])) + :: TODO global feed somehow? + == +$ keys [pub=@ priv=@] ++ default ^- state-0 =/ s *state-0 s(relays (silt ~['wss://relay.damus.io' 'wss://nos.lol'])) :: NOSTR structs :: -++ event ++$ event $: id=@ux :: 32bytes pubkey=@ux :: 32bytes created-at=@ud :: seconds @@ -20,9 +28,15 @@ $: id=@ux :: 32bytes content=@t sig=@ux :: 64bytes == -++ tag ++$ tag $: key=@t value=@t rest=(list @t) == ++$ user-meta :: NIP-1 + (map @t @t) +:: $: name=@t +:: about=@t +:: pic=@t +:: == -- diff --git a/desk/sur/post.hoon b/desk/sur/post.hoon index f7bdde6..75f2aa3 100644 --- a/desk/sur/post.hoon +++ b/desk/sur/post.hoon @@ -73,6 +73,7 @@ +$ content-map ((mop time content-list) gth) ++ corm ((on time content-list) gth) +$ content-list contents:contents-1 ++$ li content-list ++ contents-1 |% +$ contents (list block) diff --git a/front b/front new file mode 160000 +Subproject b3c02f5400223ced29f6b1f81b4e618c42c2bb5 |