mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 01:52:42 +03:00
1383 lines
38 KiB
Plaintext
1383 lines
38 KiB
Plaintext
:: chat-cli: cli chat client using chat-store and friends
|
|
::
|
|
:: pulls all known messages into a single stream.
|
|
:: type ;help for usage instructions.
|
|
::
|
|
:: note that while the chat-store only cares about paths,
|
|
:: we mostly deal with [ship path] (aka target) here.
|
|
:: when sending messages (through the chat hook),
|
|
:: we concat the ship onto the head of the path,
|
|
:: and trust it to take care of the rest.
|
|
::
|
|
/- *chat-store, *chat-view, *chat-hook,
|
|
*permission-store, *group-store, *invite-store,
|
|
sole-sur=sole
|
|
/+ sole-lib=sole, chat-eval, default-agent, verb, dbug,
|
|
auto=language-server-complete
|
|
::
|
|
|%
|
|
+$ card card:agent:gall
|
|
+$ state
|
|
$: grams=(list mail) :: all messages
|
|
known=(set [target serial]) :: known message lookup
|
|
count=@ud :: (lent grams)
|
|
bound=(map target glyph) :: bound circle glyphs
|
|
binds=(jug glyph target) :: circle glyph lookup
|
|
audience=(set target) :: active targets
|
|
settings=(set term) :: frontend flags
|
|
width=@ud :: display width
|
|
timez=(pair ? @ud) :: timezone adjustment
|
|
cli=state=sole-share:sole-sur :: console state
|
|
eny=@uvJ :: entropy
|
|
==
|
|
::
|
|
+$ mail [source=target envelope]
|
|
+$ target [=ship =path]
|
|
::
|
|
+$ glyph char
|
|
++ glyphs "!@#$%^&()-=_+[]\{};'\\:\",.<>?"
|
|
::
|
|
::NOTE only the "simple" modes from rw-security
|
|
+$ nu-security ?(%channel %village)
|
|
::
|
|
+$ command
|
|
$% [%target (set target)] :: set messaging target
|
|
[%say letter] :: send message
|
|
[%eval cord hoon] :: send #-message
|
|
::
|
|
::
|
|
:: create chat
|
|
::[%create nu-security path (unit glyph) (unit ?)]
|
|
[%delete path] :: delete chat
|
|
[%invite path (set ship)] :: allow
|
|
[%banish path (set ship)] :: disallow
|
|
::
|
|
[%join target (unit glyph) (unit ?)] :: join target
|
|
[%leave target] :: nuke target
|
|
::
|
|
[%bind glyph target] :: bind glyph
|
|
[%unbind glyph (unit target)] :: unbind glyph
|
|
[%what (unit $@(char target))] :: glyph lookup
|
|
::
|
|
[%settings ~] :: show active settings
|
|
[%set term] :: set settings flag
|
|
[%unset term] :: unset settings flag
|
|
[%width @ud] :: adjust display width
|
|
[%timezone ? @ud] :: adjust time printing
|
|
::
|
|
[%select $@(rel=@ud [zeros=@u abs=@ud])] :: rel/abs msg selection
|
|
[%chats ~] :: list available chats
|
|
[%help ~] :: print usage info
|
|
== ::
|
|
::
|
|
--
|
|
=| state
|
|
=* all-state -
|
|
::
|
|
%- agent:dbug
|
|
%+ verb |
|
|
^- agent:gall
|
|
=<
|
|
|_ =bowl:gall
|
|
+* this .
|
|
talk-core +>
|
|
tc ~(. talk-core(eny eny.bowl) bowl)
|
|
def ~(. (default-agent this %|) bowl)
|
|
::
|
|
++ on-init
|
|
^- (quip card _this)
|
|
:- [connect:tc]~
|
|
%_ this
|
|
audience [[our-self:tc /] ~ ~]
|
|
settings (sy %showtime %notify ~)
|
|
width 80
|
|
==
|
|
::
|
|
++ on-save !>(all-state)
|
|
::
|
|
++ on-load
|
|
|= old-state=vase
|
|
^- (quip card _this)
|
|
=/ old !<(state old-state)
|
|
=^ cards all-state (prep:tc `old)
|
|
[cards this]
|
|
::
|
|
++ on-poke
|
|
|= [=mark =vase]
|
|
^- (quip card _this)
|
|
=^ cards all-state
|
|
?+ mark (on-poke:def mark vase)
|
|
%noun (poke-noun:tc !<(* vase))
|
|
%sole-action (poke-sole-action:tc !<(sole-action:sole-sur vase))
|
|
==
|
|
[cards this]
|
|
::
|
|
++ on-watch
|
|
|= =path
|
|
^- (quip card _this)
|
|
=^ cards all-state (peer:tc path)
|
|
[cards this]
|
|
::
|
|
++ on-leave on-leave:def
|
|
++ on-peek on-peek:def
|
|
++ on-agent
|
|
|= [=wire =sign:agent:gall]
|
|
^- (quip card _this)
|
|
=^ cards all-state
|
|
?- -.sign
|
|
%poke-ack [- all-state]:(on-agent:def wire sign)
|
|
%watch-ack [- all-state]:(on-agent:def wire sign)
|
|
%kick [?:(?=([%chat-store ~] wire) ~[connect:tc] ~) all-state]
|
|
%fact
|
|
?+ p.cage.sign ~|([%chat-cli-bad-sub-mark wire p.cage.sign] !!)
|
|
%chat-update (diff-chat-update:tc wire !<(chat-update q.cage.sign))
|
|
==
|
|
==
|
|
[cards this]
|
|
::
|
|
++ on-arvo on-arvo:def
|
|
++ on-fail on-fail:def
|
|
--
|
|
::
|
|
|_ =bowl:gall
|
|
:: +prep: setup & state adapter
|
|
::
|
|
++ prep
|
|
|= old=(unit state)
|
|
^- (quip card state)
|
|
?^ old
|
|
:_ u.old
|
|
?: (~(has by wex.bowl) [/chat-store our-self %chat-store])
|
|
~
|
|
~[connect]
|
|
=^ cards all-state
|
|
%_ catch-up
|
|
audience [[our-self /] ~ ~]
|
|
settings (sy %showtime %notify ~)
|
|
width 80
|
|
==
|
|
[[connect cards] all-state]
|
|
:: +catch-up: process all chat-store state
|
|
::
|
|
++ catch-up
|
|
^- (quip card state)
|
|
=/ =inbox
|
|
.^ inbox
|
|
%gx
|
|
(scot %p our.bowl)
|
|
%chat-store
|
|
(scot %da now.bowl)
|
|
/all/noun
|
|
==
|
|
|- ^- (quip card state)
|
|
?~ inbox [~ all-state]
|
|
=* path p.n.inbox
|
|
=* mailbox q.n.inbox
|
|
=/ =target (path-to-target path)
|
|
=^ cards-n all-state (read-envelopes target envelopes.mailbox)
|
|
=^ cards-l all-state $(inbox l.inbox)
|
|
=^ cards-r all-state $(inbox r.inbox)
|
|
[:(weld cards-n cards-l cards-r) all-state]
|
|
:: +connect: connect to the chat-store
|
|
::
|
|
++ connect
|
|
^- card
|
|
[%pass /chat-store %agent [our-self %chat-store] %watch /updates]
|
|
::
|
|
++ our-self (name:title our.bowl)
|
|
:: +target-to-path: prepend ship to the path
|
|
::
|
|
++ target-to-path
|
|
|= target
|
|
[(scot %p ship) path]
|
|
:: +path-to-target: deduces a target from a mailbox path
|
|
::
|
|
++ path-to-target
|
|
|= =path
|
|
^- target
|
|
?. ?=([@ @ *] path)
|
|
::TODO can we safely assert the above?
|
|
~& [%path-without-host path]
|
|
[our-self path]
|
|
=+ who=(slaw %p i.path)
|
|
?~ who [our-self path]
|
|
[u.who t.path]
|
|
:: +poke-noun: debug helpers
|
|
::
|
|
++ poke-noun
|
|
|= a=*
|
|
^- (quip card state)
|
|
?: ?=(%connect a)
|
|
[[connect ~] all-state]
|
|
?: ?=(%catch-up a)
|
|
catch-up
|
|
[~ all-state]
|
|
:: +poke-sole-action: handle cli input
|
|
::
|
|
++ poke-sole-action
|
|
::TODO use id.act to support multiple separate sessions
|
|
|= [act=sole-action:sole-sur]
|
|
^- (quip card state)
|
|
(sole:sh-in act)
|
|
:: +peer: accept only cli subscriptions from ourselves
|
|
::
|
|
++ peer
|
|
|= =path
|
|
^- (quip card state)
|
|
?. (team:title our-self src.bowl)
|
|
~| [%peer-talk-stranger src.bowl]
|
|
!!
|
|
?. ?=([%sole *] path)
|
|
~| [%peer-talk-strange path]
|
|
!!
|
|
:: display a fresh prompt
|
|
:- [prompt:sh-out ~]
|
|
:: start with fresh sole state
|
|
all-state(state.cli *sole-share:sole-sur)
|
|
:: +diff-chat-update: get new mailboxes & messages
|
|
::
|
|
++ diff-chat-update
|
|
|= [=wire upd=chat-update]
|
|
^- (quip card state)
|
|
?+ -.upd [~ all-state]
|
|
%create (notice-create (path-to-target path.upd))
|
|
%delete [[(show-delete:sh-out (path-to-target path.upd)) ~] all-state]
|
|
%message (read-envelope (path-to-target path.upd) envelope.upd)
|
|
%messages (read-envelopes (path-to-target path.upd) envelopes.upd)
|
|
==
|
|
::
|
|
++ read-envelopes
|
|
|= [=target envs=(list envelope)]
|
|
^- (quip card state)
|
|
?~ envs [~ all-state]
|
|
=^ cards-i all-state (read-envelope target i.envs)
|
|
=^ cards-t all-state $(envs t.envs)
|
|
[(weld cards-i cards-t) all-state]
|
|
::
|
|
++ notice-create
|
|
|= =target
|
|
^- (quip card state)
|
|
=^ cards all-state
|
|
?: (~(has by bound) target)
|
|
[~ all-state]
|
|
(bind-default-glyph target)
|
|
[[(show-create:sh-out target) cards] all-state]
|
|
:: +bind-default-glyph: bind to default, or random available
|
|
::
|
|
++ bind-default-glyph
|
|
|= =target
|
|
^- (quip card state)
|
|
=; =glyph (bind-glyph glyph target)
|
|
|^ =/ g=glyph (choose glyphs)
|
|
?. (~(has by binds) g) g
|
|
=/ available=(list glyph)
|
|
%~ tap in
|
|
(~(dif in `(set glyph)`(sy glyphs)) ~(key by binds))
|
|
?~ available g
|
|
(choose available)
|
|
++ choose
|
|
|= =(list glyph)
|
|
=; i=@ud (snag i list)
|
|
(mod (mug target) (lent list))
|
|
--
|
|
:: +bind-glyph: add binding for glyph
|
|
::
|
|
++ bind-glyph
|
|
|= [=glyph =target]
|
|
^- (quip card state)
|
|
::TODO should send these to settings store eventually
|
|
:: if the target was already bound to another glyph, un-bind that
|
|
::
|
|
=? binds (~(has by bound) target)
|
|
(~(del ju binds) (~(got by bound) target) target)
|
|
=. bound (~(put by bound) target glyph)
|
|
=. binds (~(put ju binds) glyph target)
|
|
[(show-glyph:sh-out glyph `target) all-state]
|
|
:: +unbind-glyph: remove all binding for glyph
|
|
::
|
|
++ unbind-glyph
|
|
|= [=glyph targ=(unit target)]
|
|
^- (quip card state)
|
|
?^ targ
|
|
=. binds (~(del ju binds) glyph u.targ)
|
|
=. bound (~(del by bound) u.targ)
|
|
[(show-glyph:sh-out glyph ~) all-state]
|
|
=/ ole=(set target)
|
|
(~(get ju binds) glyph)
|
|
=. binds (~(del by binds) glyph)
|
|
=. bound
|
|
|-
|
|
?~ ole bound
|
|
=. bound $(ole l.ole)
|
|
=. bound $(ole r.ole)
|
|
(~(del by bound) n.ole)
|
|
[(show-glyph:sh-out glyph ~) all-state]
|
|
:: +decode-glyph: find the target that matches a glyph, if any
|
|
::
|
|
++ decode-glyph
|
|
|= =glyph
|
|
^- (unit target)
|
|
=+ lax=(~(get ju binds) glyph)
|
|
:: no circle
|
|
?: =(~ lax) ~
|
|
%- some
|
|
:: single circle
|
|
?: ?=([* ~ ~] lax) n.lax
|
|
:: in case of multiple audiences, pick the most recently active one
|
|
|- ^- target
|
|
?~ grams -:~(tap in lax)
|
|
=* source source.i.grams
|
|
?: (~(has in lax) source)
|
|
source
|
|
$(grams t.grams)
|
|
:: +read-envelope: add envelope to state and show it to user
|
|
::
|
|
++ read-envelope
|
|
|= [=target =envelope]
|
|
^- (quip card state)
|
|
?: (~(has in known) [target uid.envelope])
|
|
::NOTE we no-op only because edits aren't possible
|
|
[~ all-state]
|
|
:- (show-envelope:sh-out target envelope)
|
|
%_ all-state
|
|
known (~(put in known) [target uid.envelope])
|
|
grams [[target envelope] grams]
|
|
count +(count)
|
|
==
|
|
::
|
|
:: +sh-in: handle user input
|
|
::
|
|
++ sh-in
|
|
::NOTE interestingly, adding =, sh-out breaks compliation
|
|
|%
|
|
:: +sole: apply sole action
|
|
::
|
|
++ sole
|
|
|= act=sole-action:sole-sur
|
|
^- (quip card state)
|
|
?- -.dat.act
|
|
%det (edit +.dat.act)
|
|
%clr [~ all-state]
|
|
%ret obey
|
|
%tab (tab +.dat.act)
|
|
==
|
|
:: +tab-list: static list of autocomplete entries
|
|
++ tab-list
|
|
^- (list (option:auto tank))
|
|
:~
|
|
[%join leaf+";join ~ship/chat-name (glyph)"]
|
|
[%leave leaf+";leave ~ship/chat-name"]
|
|
::
|
|
::[%create leaf+";create [type] /chat-name (glyph)"]
|
|
[%delete leaf+";delete /chat-name"]
|
|
[%invite leaf+";invite /chat-name ~ships"]
|
|
[%banish leaf+";banish /chat-name ~ships"]
|
|
::
|
|
[%bind leaf+";bind [glyph] ~ship/chat-name"]
|
|
[%unbind leaf+";unbind [glyph]"]
|
|
[%what leaf+";what (~ship/chat-name) (glyph)"]
|
|
::
|
|
[%settings leaf+";settings"]
|
|
[%set leaf+";set key (value)"]
|
|
[%unset leaf+";unset key"]
|
|
::
|
|
[%chats leaf+";chats"]
|
|
[%help leaf+";help"]
|
|
==
|
|
++ tab
|
|
|= pos=@ud
|
|
^- (quip card state)
|
|
?: ?| =(~ buf.state.cli)
|
|
!=(';' -.buf.state.cli)
|
|
==
|
|
:_ all-state
|
|
[(effect:sh-out [%bel ~]) ~]
|
|
::
|
|
=+ (get-id:auto pos (tufa buf.state.cli))
|
|
=/ needle=term
|
|
(fall id '')
|
|
?: &(!=(pos 1) =(0 (met 3 needle)))
|
|
[~ all-state] :: autocomplete empty command iff user at start of command
|
|
=/ options=(list (option:auto tank))
|
|
(search-prefix:auto needle tab-list)
|
|
=/ advance=term
|
|
(longest-match:auto options)
|
|
=/ to-send=tape
|
|
(trip (rsh 3 (met 3 needle) advance))
|
|
=/ send-pos
|
|
(add pos (met 3 (fall forward '')))
|
|
=| moves=(list card)
|
|
=? moves ?=(^ options)
|
|
[(tab:sh-out options) moves]
|
|
=| fxs=(list sole-effect:sole-sur)
|
|
|- ^- (quip card state)
|
|
?~ to-send
|
|
[(flop moves) all-state]
|
|
=^ char state.cli
|
|
(~(transmit sole-lib state.cli) [%ins send-pos `@c`i.to-send])
|
|
%_ $
|
|
moves [(effect:sh-out %det char) moves]
|
|
send-pos +(send-pos)
|
|
to-send t.to-send
|
|
==
|
|
:: +edit: apply sole edit
|
|
::
|
|
:: called when typing into the cli prompt.
|
|
:: applies the change and does sanitizing.
|
|
::
|
|
++ edit
|
|
|= cal=sole-change:sole-sur
|
|
^- (quip card state)
|
|
=^ inv state.cli (~(transceive sole-lib state.cli) cal)
|
|
=+ fix=(sanity inv buf.state.cli)
|
|
?~ lit.fix
|
|
[~ all-state]
|
|
:: just capital correction
|
|
?~ err.fix
|
|
(slug fix)
|
|
:: allow interior edits and deletes
|
|
?. &(?=($del -.inv) =(+(p.inv) (lent buf.state.cli)))
|
|
[~ all-state]
|
|
(slug fix)
|
|
:: +sanity: check input sanity
|
|
::
|
|
:: parses cli prompt using +read.
|
|
:: if invalid, produces error correction description, for use with +slug.
|
|
::
|
|
++ sanity
|
|
|= [inv=sole-edit:sole-sur buf=(list @c)]
|
|
^- [lit=(list sole-edit:sole-sur) err=(unit @u)]
|
|
=+ res=(rose (tufa buf) read)
|
|
?: ?=(%& -.res) [~ ~]
|
|
[[inv]~ `p.res]
|
|
:: +slug: apply error correction to prompt input
|
|
::
|
|
++ slug
|
|
|= [lit=(list sole-edit:sole-sur) err=(unit @u)]
|
|
^- (quip card state)
|
|
?~ lit [~ all-state]
|
|
=^ lic state.cli
|
|
%- ~(transmit sole-lib state.cli)
|
|
^- sole-edit:sole-sur
|
|
?~(t.lit i.lit [%mor lit])
|
|
:_ all-state
|
|
:_ ~
|
|
%+ effect:sh-out %mor
|
|
:- [%det lic]
|
|
?~(err ~ [%err u.err]~)
|
|
:: +read: command parser
|
|
::
|
|
:: parses the command line buffer.
|
|
:: produces commands which can be executed by +work.
|
|
::
|
|
++ read
|
|
|^
|
|
%+ knee *command |. ~+
|
|
=- ;~(pose ;~(pfix mic -) message)
|
|
;~ pose
|
|
(stag %target tars)
|
|
::
|
|
:: ;~ (glue ace)
|
|
:: (tag %create)
|
|
:: security
|
|
:: ;~ plug
|
|
:: path
|
|
:: (punt ;~(pfix ace glyph))
|
|
:: (punt ;~(pfix ace (fuss 'y' 'n')))
|
|
:: ==
|
|
:: ==
|
|
:: ;~((glue ace) (tag %delete) path)
|
|
:: ;~((glue ace) (tag %invite) path ships)
|
|
:: ;~((glue ace) (tag %banish) path ships)
|
|
::
|
|
;~ (glue ace)
|
|
(tag %join)
|
|
;~ plug
|
|
targ
|
|
(punt ;~(pfix ace glyph))
|
|
(punt ;~(pfix ace (fuss 'y' 'n')))
|
|
==
|
|
==
|
|
;~((glue ace) (tag %leave) targ)
|
|
::
|
|
;~((glue ace) (tag %bind) glyph targ)
|
|
;~((glue ace) (tag %unbind) ;~(plug glyph (punt ;~(pfix ace targ))))
|
|
;~(plug (perk %what ~) (punt ;~(pfix ace ;~(pose glyph targ))))
|
|
::
|
|
;~(plug (tag %settings) (easy ~))
|
|
;~((glue ace) (tag %set) flag)
|
|
;~((glue ace) (tag %unset) flag)
|
|
;~(plug (cold %width (jest 'set width ')) dem:ag)
|
|
;~ plug
|
|
(cold %timezone (jest 'set timezone '))
|
|
;~ pose
|
|
(cold %| (just '-'))
|
|
(cold %& (just '+'))
|
|
==
|
|
%+ sear
|
|
|= a=@ud
|
|
^- (unit @ud)
|
|
?:(&((gte a 0) (lte a 14)) `a ~)
|
|
dem:ag
|
|
==
|
|
::
|
|
;~(plug (tag %chats) (easy ~))
|
|
;~(plug (tag %help) (easy ~))
|
|
::
|
|
(stag %select nump)
|
|
==
|
|
::
|
|
::TODO
|
|
:: ++ cmd
|
|
:: |* [cmd=term req=(list rule) opt=(list rule)]
|
|
:: |^ ;~ plug
|
|
:: (tag cmd)
|
|
:: ::
|
|
:: ::TODO this feels slightly too dumb
|
|
:: ?~ req
|
|
:: ?~ opt (easy ~)
|
|
:: (opt-rules opt)
|
|
:: ?~ opt (req-rules req)
|
|
:: ;~(plug (req-rules req) (opt-rules opt)) ::TODO rest-loop
|
|
:: ==
|
|
:: ++ req-rules
|
|
:: |* req=(lest rule)
|
|
:: =- ;~(pfix ace -)
|
|
:: ?~ t.req i.req
|
|
:: ;~(plug i.req $(req t.req))
|
|
:: ++ opt-rules
|
|
:: |* opt=(lest rule)
|
|
:: =- (punt ;~(pfix ace -))
|
|
:: ?~ t.opt ;~(pfix ace i.opt)
|
|
:: ;~(pfix ace ;~(plug i.opt $(opt t.opt)))
|
|
:: --
|
|
::
|
|
++ tag |*(a=@tas (cold a (jest a))) ::TODO into stdlib
|
|
++ ship ;~(pfix sig fed:ag)
|
|
++ path ;~(pfix net (most net urs:ab))
|
|
:: +tarl: local target, as /path
|
|
::
|
|
++ tarl (stag our-self path)
|
|
:: +tarp: sponsor target, as ^/path
|
|
::
|
|
++ tarp
|
|
=- ;~(pfix ket (stag - path))
|
|
(sein:title our.bowl now.bowl our-self)
|
|
:: +targ: any target, as tarl, tarp, ~ship/path or glyph
|
|
::
|
|
++ targ
|
|
;~ pose
|
|
tarl
|
|
tarp
|
|
;~(plug ship path)
|
|
(sear decode-glyph glyph)
|
|
==
|
|
:: +tars: set of comma-separated targs
|
|
::
|
|
++ tars
|
|
%+ cook ~(gas in *(set target))
|
|
(most ;~(plug com (star ace)) targ)
|
|
:: +ships: set of comma-separated ships
|
|
::
|
|
++ ships
|
|
%+ cook ~(gas in *(set ^ship))
|
|
(most ;~(plug com (star ace)) ship)
|
|
::
|
|
:: +security: security mode
|
|
::
|
|
++ security
|
|
(perk %channel %village ~)
|
|
::
|
|
:: +glyph: shorthand character
|
|
::
|
|
++ glyph (mask glyphs)
|
|
:: +flag: valid flag
|
|
::
|
|
++ flag
|
|
%- perk :~
|
|
%notify
|
|
%showtime
|
|
==
|
|
:: +nump: message number reference
|
|
::
|
|
++ nump
|
|
;~ pose
|
|
;~(pfix hep dem:ag)
|
|
;~ plug
|
|
(cook lent (plus (just '0')))
|
|
;~(pose dem:ag (easy 0))
|
|
==
|
|
(stag 0 dem:ag)
|
|
(cook lent (star mic))
|
|
==
|
|
:: +message: all messages
|
|
::
|
|
++ message
|
|
;~ pose
|
|
;~(plug (cold %eval hax) expr)
|
|
(stag %say letter)
|
|
==
|
|
:: +letter: simple messages
|
|
::
|
|
++ letter
|
|
;~ pose
|
|
(stag %url turl)
|
|
(stag %me ;~(pfix vat text))
|
|
(stag %text ;~(less mic hax text))
|
|
==
|
|
:: +turl: url parser
|
|
::
|
|
++ turl
|
|
=- (sear - text)
|
|
|= t=cord
|
|
^- (unit cord)
|
|
?~((rush t aurf:de-purl:html) ~ `t)
|
|
:: +text: text message body
|
|
::
|
|
++ text
|
|
%+ cook crip
|
|
(plus ;~(less (jest '•') next))
|
|
:: +expr: parse expression into [cord hoon]
|
|
::
|
|
++ expr
|
|
|= tub=nail
|
|
%. tub
|
|
%+ stag (crip q.tub)
|
|
wide:(vang & [&1:% &2:% (scot %da now.bowl) |3:%])
|
|
--
|
|
:: +obey: apply result
|
|
::
|
|
:: called upon hitting return in the prompt.
|
|
:: if input is invalid, +slug is called.
|
|
:: otherwise, the appropriate work is done and
|
|
:: the command (if any) gets echoed to the user.
|
|
::
|
|
++ obey
|
|
^- (quip card state)
|
|
=+ buf=buf.state.cli
|
|
=+ fix=(sanity [%nop ~] buf)
|
|
?^ lit.fix
|
|
(slug fix)
|
|
=+ jub=(rust (tufa buf) read)
|
|
?~ jub [[(effect:sh-out %bel ~) ~] all-state]
|
|
=^ cal state.cli (~(transmit sole-lib state.cli) [%set ~])
|
|
=^ cards all-state (work u.jub)
|
|
:_ all-state
|
|
%+ weld
|
|
^- (list card)
|
|
:: echo commands into scrollback
|
|
?. =(`0 (find ";" buf)) ~
|
|
[(note:sh-out (tufa `(list @)`buf)) ~]
|
|
:_ cards
|
|
%+ effect:sh-out %mor
|
|
:~ [%nex ~]
|
|
[%det cal]
|
|
==
|
|
:: +work: run user command
|
|
::
|
|
++ work
|
|
|= job=command
|
|
^- (quip card state)
|
|
|^ ?- -.job
|
|
%target (set-target +.job)
|
|
%say (say +.job)
|
|
%eval (eval +.job)
|
|
::
|
|
:: %create (create +.job)
|
|
%delete (delete +.job)
|
|
%invite (change-permission & +.job)
|
|
%banish (change-permission | +.job)
|
|
::
|
|
%join (join +.job)
|
|
%leave (leave +.job)
|
|
::
|
|
%bind (bind-glyph +.job)
|
|
%unbind (unbind-glyph +.job)
|
|
%what (lookup-glyph +.job)
|
|
::
|
|
%settings show-settings
|
|
%set (set-setting +.job)
|
|
%unset (unset-setting +.job)
|
|
%width (set-width +.job)
|
|
%timezone (set-timezone +.job)
|
|
::
|
|
%select (select +.job)
|
|
%chats chats
|
|
%help help
|
|
==
|
|
:: +act: build action card
|
|
::
|
|
++ act
|
|
|= [what=term app=term =cage]
|
|
^- card
|
|
:* %pass
|
|
/cli-command/[what]
|
|
%agent
|
|
[our-self app]
|
|
%poke
|
|
cage
|
|
==
|
|
:: +invite-card: build invite card
|
|
::
|
|
++ invite-card
|
|
|= [where=path who=ship]
|
|
^- card
|
|
:* %pass
|
|
/cli-command/invite
|
|
%agent
|
|
[who %invite-hook] ::NOTE only place chat-cli pokes others
|
|
%poke
|
|
%invite-action
|
|
::
|
|
!>
|
|
^- invite-action
|
|
:^ %invite /chat
|
|
(shax (jam [our-self where] who))
|
|
^- invite
|
|
=; desc=cord
|
|
[our-self %chat-hook where who desc]
|
|
%- crip
|
|
%+ weld
|
|
"You have been invited to chat at "
|
|
~(full tr [our-self where])
|
|
==
|
|
:: +set-target: set audience, update prompt
|
|
::
|
|
++ set-target
|
|
|= tars=(set target)
|
|
^- (quip card state)
|
|
=. audience tars
|
|
[[prompt:sh-out ~] all-state]
|
|
:: +create: new local mailbox
|
|
::
|
|
::++ create
|
|
:: |= [security=nu-security =path gyf=(unit char) allow-history=(unit ?)]
|
|
:: ^- (quip card state)
|
|
:: ::TODO check if already exists
|
|
:: =/ =target [our-self path]
|
|
:: =. audience [target ~ ~]
|
|
:: =^ moz all-state
|
|
:: ?. ?=(^ gyf) [~ all-state]
|
|
:: (bind-glyph u.gyf target)
|
|
:: =- [[- moz] all-state]
|
|
:: %^ act %do-create %chat-view
|
|
:: :- %chat-view-action
|
|
:: !> ^- chat-view-action
|
|
:: :* %create
|
|
:: path
|
|
:: security
|
|
:: :: ensure we can read from/write to our own chats
|
|
:: ::
|
|
:: ?- security
|
|
:: %channel ~
|
|
:: %village [our-self ~ ~]
|
|
:: ==
|
|
:: (fall allow-history %.y)
|
|
:: ==
|
|
:: +delete: delete local chats
|
|
::
|
|
++ delete
|
|
|= =path
|
|
^- (quip card state)
|
|
=- [[- ~] all-state]
|
|
%^ act %do-delete %chat-view
|
|
:- %chat-view-action
|
|
!> ^- chat-view-action
|
|
[%delete (target-to-path our-self path)]
|
|
:: +change-permission: modify permissions on a local chat
|
|
::
|
|
++ change-permission
|
|
|= [allow=? =path ships=(set ship)]
|
|
^- (quip card state)
|
|
:_ all-state
|
|
=; card=(unit card)
|
|
%+ weld (drop card)
|
|
?. allow ~
|
|
%+ turn ~(tap in ships)
|
|
(cury invite-card path)
|
|
=. path
|
|
[%chat (target-to-path our-self path)]
|
|
:: whitelist: empty if no matching permission, else true if whitelist
|
|
::
|
|
=/ whitelist=(unit ?)
|
|
=; perm=(unit permission)
|
|
?~(perm ~ `?=(%white kind.u.perm))
|
|
::TODO +permission-of-target?
|
|
.^ (unit permission)
|
|
%gx
|
|
(scot %p our-self)
|
|
%permission-store
|
|
(scot %da now.bowl)
|
|
%permission
|
|
(snoc path %noun)
|
|
==
|
|
?~ whitelist
|
|
~& [%weird-no-permission path]
|
|
~
|
|
%- some
|
|
%^ act %do-permission %group-store
|
|
:- %group-action
|
|
!> ^- group-action
|
|
?: =(u.whitelist allow)
|
|
[%add ships path]
|
|
[%remove ships path]
|
|
:: +join: sync with remote mailbox
|
|
::
|
|
++ join
|
|
|= [=target gyf=(unit char) ask-history=(unit ?)]
|
|
^- (quip card state)
|
|
=^ moz all-state
|
|
?. ?=(^ gyf) [~ all-state]
|
|
(bind-glyph u.gyf target)
|
|
=. audience [target ~ ~]
|
|
=; =card
|
|
[[card prompt:sh-out moz] all-state]
|
|
::TODO ideally we'd check permission first. attempting this and failing
|
|
:: gives ugly %chat-hook-reap
|
|
%^ act %do-join %chat-view
|
|
:- %chat-view-action
|
|
!> ^- chat-view-action
|
|
[%join ship.target path.target (fall ask-history %.y)]
|
|
:: +leave: unsync & destroy mailbox
|
|
::
|
|
::TODO allow us to "mute" local chats using this
|
|
++ leave
|
|
|= =target
|
|
=- [[- ~] all-state]
|
|
?: =(our-self ship.target)
|
|
%- print:sh-out
|
|
"can't ;leave local chats, maybe use ;delete instead"
|
|
%^ act %do-leave %chat-hook
|
|
:- %chat-hook-action
|
|
!> ^- chat-hook-action
|
|
[%remove (target-to-path target)]
|
|
:: +say: send messages
|
|
::
|
|
++ say
|
|
|= =letter
|
|
^- (quip card state)
|
|
~! bowl
|
|
=/ =serial (shaf %msg-uid eny.bowl)
|
|
:_ all-state(eny (shax eny.bowl))
|
|
^- (list card)
|
|
%+ turn ~(tap in audience)
|
|
|= =target
|
|
%^ act %out-message %chat-hook
|
|
:- %chat-action
|
|
!> ^- chat-action
|
|
:+ %message (target-to-path target)
|
|
[serial *@ our-self now.bowl letter]
|
|
:: +eval: run hoon, send code and result as message
|
|
::
|
|
:: this double-virtualizes and clams to disable .^ for security reasons
|
|
::
|
|
++ eval
|
|
|= [txt=cord exe=hoon]
|
|
(say %code txt (eval:chat-eval bowl exe))
|
|
:: +lookup-glyph: print glyph info for all, glyph or target
|
|
::
|
|
++ lookup-glyph
|
|
|= qur=(unit $@(glyph target))
|
|
^- (quip card state)
|
|
=- [[- ~] all-state]
|
|
?^ qur
|
|
?^ u.qur
|
|
=+ gyf=(~(get by bound) u.qur)
|
|
(print:sh-out ?~(gyf "none" [u.gyf]~))
|
|
=+ pan=~(tap in (~(get ju binds) `@t`u.qur))
|
|
?: =(~ pan) (print:sh-out "~")
|
|
=< (effect:sh-out %mor (turn pan .))
|
|
|=(t=target [%txt ~(phat tr t)])
|
|
%- print-more:sh-out
|
|
%- ~(rep by binds)
|
|
|= $: [=glyph tars=(set target)]
|
|
lis=(list tape)
|
|
==
|
|
%+ weld lis
|
|
^- (list tape)
|
|
%- ~(rep in tars)
|
|
|= [t=target l=(list tape)]
|
|
%+ weld l
|
|
^- (list tape)
|
|
[glyph ' ' ~(phat tr t)]~
|
|
:: +show-settings: print enabled flags, timezone and width settings
|
|
::
|
|
++ show-settings
|
|
^- (quip card state)
|
|
:_ all-state
|
|
:~ %- print:sh-out
|
|
%- zing
|
|
^- (list tape)
|
|
:- "flags: "
|
|
%+ ^join ", "
|
|
(turn `(list @t)`~(tap in settings) trip)
|
|
::
|
|
%- print:sh-out
|
|
%+ weld "timezone: "
|
|
^- tape
|
|
:- ?:(p.timez '+' '-')
|
|
(scow %ud q.timez)
|
|
::
|
|
(print:sh-out "width: {(scow %ud width)}")
|
|
==
|
|
:: +set-setting: enable settings flag
|
|
::
|
|
++ set-setting
|
|
|= =term
|
|
^- (quip card state)
|
|
[~ all-state(settings (~(put in settings) term))]
|
|
:: +unset-setting: disable settings flag
|
|
::
|
|
++ unset-setting
|
|
|= =term
|
|
^- (quip card state)
|
|
[~ all-state(settings (~(del in settings) term))]
|
|
:: +set-width: configure cli printing width
|
|
::
|
|
++ set-width
|
|
|= w=@ud
|
|
[~ all-state(width w)]
|
|
:: +set-timezone: configure timestamp printing adjustment
|
|
::
|
|
++ set-timezone
|
|
|= tz=[? @ud]
|
|
[~ all-state(timez tz)]
|
|
:: +select: expand message from number reference
|
|
::
|
|
++ select
|
|
::NOTE rel is the nth most recent message,
|
|
:: abs is the last message whose numbers ends in n
|
|
:: (with leading zeros used for precision)
|
|
::
|
|
|= num=$@(rel=@ud [zeros=@u abs=@ud])
|
|
^- (quip card state)
|
|
|^ ?@ num
|
|
=+ tum=(scow %s (new:si | +(num)))
|
|
?: (gte rel.num count)
|
|
%- just-print
|
|
"{tum}: no such telegram"
|
|
(activate tum rel.num)
|
|
?. (gte abs.num count)
|
|
?: =(count 0)
|
|
(just-print "0: no messages")
|
|
=+ msg=(index (dec count) num)
|
|
(activate (scow %ud msg) (sub count +(msg)))
|
|
%- just-print
|
|
"…{(reap zeros.num '0')}{(scow %ud abs.num)}: no such telegram"
|
|
:: +just-print: full [cards state] output with a single print card
|
|
::
|
|
++ just-print
|
|
|= txt=tape
|
|
[[(print:sh-out txt) ~] all-state]
|
|
:: +index: get message index from absolute reference
|
|
::
|
|
++ index
|
|
|= [max=@ud nul=@u fin=@ud]
|
|
^- @ud
|
|
=+ dog=|-(?:(=(0 fin) 1 (mul 10 $(fin (div fin 10)))))
|
|
=. dog (mul dog (pow 10 nul))
|
|
=- ?:((lte - max) - (sub - dog))
|
|
(add fin (sub max (mod max dog)))
|
|
:: +activate: echo message selector and print details
|
|
::
|
|
++ activate
|
|
|= [number=tape index=@ud]
|
|
^- (quip card state)
|
|
=+ gam=(snag index grams)
|
|
=. audience [source.gam ~ ~]
|
|
:_ all-state
|
|
^- (list card)
|
|
:~ (print:sh-out ['?' ' ' number])
|
|
(effect:sh-out ~(render-activate mr gam))
|
|
prompt:sh-out
|
|
==
|
|
--
|
|
:: +chats: display list of local mailboxes
|
|
::
|
|
++ chats
|
|
^- (quip card state)
|
|
:_ all-state
|
|
:_ ~
|
|
%- print-more:sh-out
|
|
=/ all
|
|
::TODO refactor
|
|
::TODO remote scries fail... but moon support?
|
|
.^ (set path)
|
|
%gx
|
|
/(scot %p our-self)/chat-store/(scot %da now.bowl)/keys/noun
|
|
==
|
|
%+ turn ~(tap in all)
|
|
%+ cork path-to-target
|
|
|= target
|
|
(weld (scow %p ship) (spud path))
|
|
:: +help: print (link to) usage instructions
|
|
::
|
|
++ help
|
|
^- (quip card state)
|
|
=- [[- ~] all-state]
|
|
(print:sh-out "see https://urbit.org/using/operations/using-your-ship/#messaging")
|
|
--
|
|
--
|
|
::
|
|
:: +sh-out: output to the cli
|
|
::
|
|
++ sh-out
|
|
|%
|
|
:: +effect: console effect card
|
|
::
|
|
++ effect
|
|
|= fec=sole-effect:sole-sur
|
|
^- card
|
|
::TODO don't hard-code session id 'drum' here
|
|
[%give %fact ~[/sole/drum] %sole-effect !>(fec)]
|
|
:: +tab: print tab-complete list
|
|
::
|
|
++ tab
|
|
|= options=(list [cord tank])
|
|
^- card
|
|
(effect %tab options)
|
|
:: +print: puts some text into the cli as-is
|
|
::
|
|
++ print
|
|
|= txt=tape
|
|
^- card
|
|
(effect %txt txt)
|
|
:: +print-more: puts lines of text into the cli
|
|
::
|
|
++ print-more
|
|
|= txs=(list tape)
|
|
^- card
|
|
%+ effect %mor
|
|
(turn txs |=(t=tape [%txt t]))
|
|
:: +note: prints left-padded ---| txt
|
|
::
|
|
++ note
|
|
|= txt=tape
|
|
^- card
|
|
=+ lis=(simple-wrap txt (sub width 16))
|
|
%- print-more
|
|
=+ ?:((gth (lent lis) 0) (snag 0 lis) "")
|
|
:- (runt [14 '-'] '|' ' ' -)
|
|
%+ turn (slag 1 lis)
|
|
|=(a=tape (runt [14 ' '] '|' ' ' a))
|
|
:: +prompt: update prompt to display current audience
|
|
::
|
|
++ prompt
|
|
^- card
|
|
%+ effect %pro
|
|
:+ & %talk-line
|
|
^- tape
|
|
=- ?: =(1 (lent -)) "{-} "
|
|
"[{-}] "
|
|
=/ all
|
|
%+ sort ~(tap in audience)
|
|
|= [a=target b=target]
|
|
(~(beat tr a) b)
|
|
=+ fir=&
|
|
|- ^- tape
|
|
?~ all ~
|
|
;: welp
|
|
?:(fir "" " ")
|
|
~(show tr i.all)
|
|
$(all t.all, fir |)
|
|
==
|
|
:: +show-envelope: print incoming message
|
|
::
|
|
:: every five messages, prints the message number also.
|
|
:: if the message mentions the user's (shortened) ship name,
|
|
:: and the %notify flag is set, emit a bell.
|
|
::
|
|
++ show-envelope
|
|
|= [=target =envelope]
|
|
^- (list card)
|
|
%+ weld
|
|
^- (list card)
|
|
?. =(0 (mod count 5)) ~
|
|
:_ ~
|
|
=+ num=(scow %ud count)
|
|
%- print
|
|
(runt [(sub 13 (lent num)) '-'] "[{num}]")
|
|
=+ lis=~(render-inline mr target envelope)
|
|
?~ lis ~
|
|
:_ ~
|
|
%+ effect %mor
|
|
%+ turn `(list tape)`lis
|
|
=+ nom=(scag 7 (cite:title our-self))
|
|
|= t=tape
|
|
?. ?& (~(has in settings) %notify)
|
|
?=(^ (find nom (slag 15 t)))
|
|
==
|
|
[%txt t]
|
|
[%mor [%txt t] [%bel ~] ~]
|
|
:: +show-create: print mailbox creation notification
|
|
::
|
|
++ show-create
|
|
|= =target
|
|
^- card
|
|
(note "new: {~(phat tr target)}")
|
|
:: +show-delete: print mailbox deletion notification
|
|
::
|
|
++ show-delete
|
|
|= =target
|
|
^- card
|
|
(note "del: {~(phat tr target)}")
|
|
:: +show-glyph: print glyph un/bind notification
|
|
::
|
|
++ show-glyph
|
|
|= [=glyph target=(unit target)]
|
|
^- (list card)
|
|
:_ [prompt ~]
|
|
%- note
|
|
%+ weld "set: {[glyph ~]} "
|
|
?~ target "unbound"
|
|
~(phat tr u.target)
|
|
--
|
|
::
|
|
:: +tr: render targets
|
|
::
|
|
++ tr
|
|
|_ :: one: the target.
|
|
::
|
|
one=target
|
|
:: +beat: true if one is more "relevant" than two
|
|
::
|
|
++ beat
|
|
|= two=target
|
|
^- ?
|
|
:: the target that's ours is better.
|
|
?: =(our-self ship.one)
|
|
?. =(our-self ship.two) &
|
|
?< =(path.one path.two)
|
|
:: if both targets are ours, the main story is better.
|
|
?: =(%inbox path.one) &
|
|
?: =(%inbox path.two) |
|
|
:: if neither are, pick the "larger" one.
|
|
(lth (lent path.one) (lent path.two))
|
|
:: if one isn't ours but two is, two is better.
|
|
?: =(our-self ship.two) |
|
|
?: =(ship.one ship.two)
|
|
:: if they're from the same ship, pick the "larger" one.
|
|
(lth (lent path.one) (lent path.two))
|
|
:: if they're from different ships, neither ours, pick hierarchically.
|
|
(lth (xeb ship.one) (xeb ship.two))
|
|
:: +full: render target fully, always
|
|
::
|
|
++ full
|
|
^- tape
|
|
(weld (scow %p ship.one) (spud path.one))
|
|
:: +phat: render target with local shorthand
|
|
::
|
|
:: renders as ~ship/path.
|
|
:: for local mailboxes, renders just /path.
|
|
:: for sponsor's mailboxes, renders ^/path.
|
|
::
|
|
::NOTE but, given current implementation, all will be local
|
|
::
|
|
++ phat
|
|
^- tape
|
|
%+ weld
|
|
?: =(our-self ship.one) ~
|
|
?: =((sein:title our.bowl now.bowl our-self) ship.one) "^"
|
|
(scow %p ship.one)
|
|
(spud path.one)
|
|
:: +show: render as tape, as glyph if we can
|
|
::
|
|
++ show
|
|
^- tape
|
|
=+ cha=(~(get by bound) one)
|
|
?~(cha phat "{u.cha ~}")
|
|
:: +glyph: tape for glyph of target, defaulting to *
|
|
::
|
|
++ glyph
|
|
^- tape
|
|
[(~(gut by bound) one '*') ~]
|
|
--
|
|
::
|
|
:: +mr: render messages
|
|
::
|
|
++ mr
|
|
|_ $: source=target
|
|
envelope
|
|
==
|
|
:: +activate: produce sole-effect for printing message details
|
|
::
|
|
++ render-activate
|
|
^- sole-effect:sole-sur
|
|
~[%mor [%tan meta] body]
|
|
:: +meta: render message metadata (serial, timestamp, author, target)
|
|
::
|
|
++ meta
|
|
^- tang
|
|
=. when (sub when (mod when (div when ~s0..0001))) :: round
|
|
=+ hed=leaf+"{(scow %uv uid)} at {(scow %da when)}"
|
|
=/ src=tape ~(phat tr source)
|
|
[%rose [" " ~ ~] [hed >author< [%rose [", " "to " ~] [leaf+src]~] ~]]~
|
|
:: +body: long-form render of message contents
|
|
::
|
|
++ body
|
|
|- ^- sole-effect:sole-sur
|
|
?- -.letter
|
|
?(%text %me)
|
|
=/ pre=tape ?:(?=(%me -.letter) "@ " "")
|
|
tan+~[leaf+"{pre}{(trip +.letter)}"]
|
|
::
|
|
%url
|
|
url+url.letter
|
|
::
|
|
%code
|
|
=/ texp=tape ['>' ' ' (trip expression.letter)]
|
|
:- %mor
|
|
|- ^- (list sole-effect:sole-sur)
|
|
?: =("" texp) [tan+output.letter ~]
|
|
=/ newl (find "\0a" texp)
|
|
?~ newl [txt+texp $(texp "")]
|
|
=+ (trim u.newl texp)
|
|
:- txt+(scag u.newl texp)
|
|
$(texp [' ' ' ' (slag +(u.newl) texp)])
|
|
==
|
|
:: +render-inline: produces lines to display message body in scrollback
|
|
::
|
|
++ render-inline
|
|
^- (list tape)
|
|
=/ wyd
|
|
:: termwidth,
|
|
%+ sub width
|
|
:: minus autor,
|
|
%+ add 14
|
|
:: minus timestamp.
|
|
?:((~(has in settings) %showtime) 10 0)
|
|
=+ txs=(line wyd)
|
|
?~ txs ~
|
|
:: nom: rendered author
|
|
:: den: regular indent
|
|
:: tam: timestamp, if desired
|
|
::
|
|
=/ nom=tape (nome author)
|
|
=/ den=tape (reap (lent nom) ' ')
|
|
=/ tam=tape
|
|
?. (~(has in settings) %showtime) ""
|
|
=. when
|
|
%. [when (mul q.timez ~h1)]
|
|
?:(p.timez add sub)
|
|
=+ dat=(yore when)
|
|
=/ t
|
|
|= a/@
|
|
%+ weld
|
|
?:((lth a 10) "0" ~)
|
|
(scow %ud a)
|
|
=/ time
|
|
;: weld
|
|
"~" (t h.t.dat)
|
|
"." (t m.t.dat)
|
|
"." (t s.t.dat)
|
|
==
|
|
%+ weld
|
|
(reap (sub +(wyd) (min wyd (lent (tuba i.txs)))) ' ')
|
|
time
|
|
%- flop
|
|
%+ roll `(list tape)`txs
|
|
|= [t=tape l=(list tape)]
|
|
?~ l [:(weld nom t tam) ~]
|
|
[(weld den t) l]
|
|
:: +nome: prints a ship name in 14 characters, left-padding with spaces
|
|
::
|
|
++ nome
|
|
|= =ship
|
|
^- tape
|
|
=+ raw=(cite:title ship)
|
|
(runt [(sub 14 (lent raw)) ' '] raw)
|
|
:: +line: renders most important contents, tries to fit one line
|
|
::
|
|
::TODO this should probably be rewritten someday
|
|
++ line
|
|
:: pre: replace/append line prefix
|
|
::
|
|
=| pre=(unit (pair ? tape))
|
|
|= wyd=@ud
|
|
^- (list tape)
|
|
?- -.letter
|
|
%code
|
|
=+ texp=(trip expression.letter)
|
|
=+ newline=(find "\0a" texp)
|
|
=? texp ?=(^ newline)
|
|
(weld (scag u.newline texp) " ...")
|
|
:- (truncate wyd '#' ' ' texp)
|
|
?~ output.letter ~
|
|
=- [' ' (truncate (dec wyd) ' ' -)]~
|
|
~(ram re (snag 0 `(list tank)`output.letter))
|
|
::
|
|
%url
|
|
:_ ~
|
|
=+ ful=(trip url.letter)
|
|
=+ pef=q:(fall pre [p=| q=""])
|
|
:: clean up prefix if needed.
|
|
=? pef =((scag 1 (flop pef)) " ")
|
|
(scag (dec (lent pef)) pef)
|
|
=. pef (weld "/" pef)
|
|
=. wyd (sub wyd +((lent pef))) :: account for prefix.
|
|
:: if the full url fits, just render it.
|
|
?: (gte wyd (lent ful)) :(weld pef " " ful)
|
|
:: if it doesn't, prefix with _ and render just (the tail of) the domain.
|
|
%+ weld (weld pef "_")
|
|
=+ prl=(rust ful aurf:de-purl:html)
|
|
?~ prl (weld (scag (dec wyd) ful) "…")
|
|
=+ hok=r.p.p.u.prl
|
|
=- (swag [a=(sub (max wyd (lent -)) wyd) b=wyd] -)
|
|
^- tape
|
|
=< ?: ?=(%& -.hok)
|
|
(reel p.hok .)
|
|
+:(scow %if p.hok)
|
|
|= [a=knot b=tape]
|
|
?~ b (trip a)
|
|
(welp b '.' (trip a))
|
|
::
|
|
?(%text %me)
|
|
:: glyph prefix
|
|
=/ pef=tape
|
|
?: &(?=(^ pre) p.u.pre) q.u.pre
|
|
?: ?=(%me -.letter) " "
|
|
=- (weld - q:(fall pre [p=| q=" "]))
|
|
~(glyph tr source)
|
|
=/ lis=(list tape)
|
|
%+ simple-wrap
|
|
~| [%weird-text `@`+.letter]
|
|
`tape``(list @)`(tuba (trip +.letter))
|
|
(sub wyd (min (div wyd 2) (lent pef)))
|
|
=+ lef=(lent pef)
|
|
=+ ?:((gth (lent lis) 0) (snag 0 lis) "")
|
|
:- (weld pef -)
|
|
%+ turn (slag 1 lis)
|
|
|=(a=tape (runt [lef ' '] a))
|
|
==
|
|
:: +truncate: truncate txt to fit len, indicating truncation with _ or …
|
|
::
|
|
++ truncate
|
|
|= [len=@u txt=tape]
|
|
^- tape
|
|
?: (gth len (lent txt)) txt
|
|
=. txt (scag len txt)
|
|
|-
|
|
?~ txt txt
|
|
?: =(' ' i.txt)
|
|
|-
|
|
:- '_'
|
|
?. ?=([%' ' *] t.txt)
|
|
t.txt
|
|
$(txt t.txt)
|
|
?~ t.txt "…"
|
|
[i.txt $(txt t.txt)]
|
|
--
|
|
::
|
|
++ simple-wrap
|
|
|= [txt=tape wid=@ud]
|
|
^- (list tape)
|
|
?~ txt ~
|
|
=/ [end=@ud nex=?]
|
|
?: (lte (lent txt) wid) [(lent txt) &]
|
|
=+ ace=(find " " (flop (scag +(wid) `tape`txt)))
|
|
?~ ace [wid |]
|
|
[(sub wid u.ace) &]
|
|
:- (tufa (scag end `(list @)`txt))
|
|
$(txt (slag ?:(nex +(end) end) `tape`txt))
|
|
--
|