Compare commits

...

18 Commits

Author SHA1 Message Date
yung calibri
f9423d0a64 zuse 412 2023-09-29 00:59:30 -04:00
yung calibri
7337f74e5c add new-update indicator on frontend 2023-09-29 00:49:16 -04:00
yung calibri
fef352b073 add updates page 2023-09-28 23:09:28 -04:00
yung calibri
10615eeaf1 add version display in UI 2023-09-28 22:51:08 -04:00
yung calibri
b0f6563536 add divert from UI 2023-09-28 22:09:25 -04:00
yung calibri
625f2707de sort paths in UI 2023-09-28 21:39:15 -04:00
yung calibri
ba7ef21a5a add steps field to state 2023-09-28 17:34:17 -04:00
yung calibri
a976552b3a update references to en-json 2023-09-27 20:00:06 -04:00
yung calibri
6d174ef6c6 add divert doc to usage placard 2023-09-12 00:23:09 -04:00
yung calibri
c541bccb6f add %divert action 2023-09-12 00:17:19 -04:00
yung calibri
bec28eaf04 Merge branch 'master' into v0.7.0 2023-09-11 22:48:27 -04:00
yung calibri
c659e29402 Revert "reverse shortlinks so the most recently added appears at the top"
This reverts commit 2eeb641c92.
2023-09-11 22:48:11 -04:00
yung calibri
2eeb641c92 reverse shortlinks so the most recently added appears at the top 2023-09-09 22:39:23 -04:00
yung calibri
b7d177f13c move new shortlink form to top of page 2023-09-09 22:39:01 -04:00
yung calibri
e8965306e1 inline validation for unique shortlink paths 2023-09-09 22:36:35 -04:00
yung calibri
50db71b442 add validate/unique endpoint 2023-09-09 22:25:11 -04:00
yung calibri
574d0dfd64 move shortlinks view to main page 2023-09-09 21:09:24 -04:00
yung calibri
5bd3544579 update helper core invocation, use redirect-on-post pattern 2023-08-31 14:41:48 -04:00
10 changed files with 551 additions and 209 deletions

View File

@ -13,6 +13,12 @@ Use `direct` to create a redirect.
dojo> :prism|direct ~.dev 'https://developers.urbit.org/'
```
Use `divert` to create a redirect from a random path (drawn from the set
of stars, eg `/apps/prism/ronfeb`).
```
dojo> :prism|divert 'https://newgrounds.com/'
```
Use `defect` to disable an existing redirect.
```
dojo> :prism|defect ~.dev

View File

@ -4,6 +4,14 @@
+$ card card:agent:gall
+$ versioned-state
$% state-0
state-1
==
+$ state-1
$: %1
=paths
=steps
=brats
=snoop
==
+$ state-0
$: %0
@ -13,7 +21,7 @@
:: slate=@ta :: next randomly generated URL fragment
==
--
=| state-0
=| state-1
=* state -
%- agent:dbug
%+ verb |
@ -21,6 +29,7 @@
=<
|_ =bowl:gall
+* this .
hc ~(. ^hc bowl)
def ~(. (default-agent this %|) bowl)
++ on-init
^- (quip card _this)
@ -37,8 +46,29 @@
|= old-state=vase
^- (quip card _this)
=/ old !<(versioned-state old-state)
?- -.old
%0 [~ this(state old)]
?- -.old
%1
[~ this(state old)]
::
%0
:: we don't have order info for the old paths, so we have to start by
:: just assigning an order arbitrarily. the UI must allow the
:: user to reorder.
=/ spout=(pair (list [wright @ud]) @ud)
%^ spin ~(tap in ~(key by paths.old))
1
|=([=wright ord=@ud] [[wright ord] (add 2 ord)])
:: spin produces a pair of a list and a noun, and we only want
:: the former.
=/ order=^steps (malt p.spout)
=/ new=state-1
:* %1
paths.old
order
brats.old
snoop.old
==
[~ this(state new)]
==
::
++ on-poke
@ -49,11 +79,10 @@
%prism-action
?> =(src.bowl our.bowl)
=^ cards state
(handle-action !<(prism-action vase))
(handle-action:hc !<(prism-action vase))
[cards this]
::
%handle-http-request
?> =(src.bowl our.bowl)
=/ req !<([eyre-id=@ta =inbound-request:eyre] vase)
:: ehh. is it worth feeding this into handle-http?
:: or is that pointless?
@ -63,16 +92,16 @@
=^ cards state
^- (quip card _state)
:: unauthenticated requests are all handled by the pub arm.
?. authenticated.inbound-request.req
~(pub handle-http req)
?. =(src.bowl our.bowl)
~(pub handle-http:hc req)
:: authenticated requests are handled by the arm matching
:: the request method.
?+ method.request.inbound-request.req dump
%'GET'
~(get handle-http req)
~(get handle-http:hc req)
::
%'POST'
~(pot handle-http req)
~(pot handle-http:hc req)
==
[cards this]
==
@ -94,156 +123,258 @@
--
|%
::
++ handle-http
|_ [eyre-id=@ta =inbound-request:eyre]
+* req (parse-request-line:server url.request.inbound-request)
body body.request.inbound-request
send (cury response:schooner eyre-id)
beth (cury update-breath inbound-request)
dump [(send [404 ~ [%none ~]]) state]
derp [(send [500 ~ [%stock ~]]) state]
:: all public requests
++ pub
^- (quip card _state)
=/ site site.req
?+ site dump
::
[%apps %prism @t ~] :: /apps/prism/{path in paths.state}
:: it has to be a valid @ta or it won't go in paths.state
?. ((sane %ta) i.t.t.site) dump
:: "M-path", maybe path
=/ empath `@ta`i.t.t.site
:: dump if it doesn't exist
?. (~(has by paths.state) empath) dump
:: dump if it's disabled
?: (~(has in brats.state) empath) dump
:: get the breath for this wright, pull relevant
:: parameters, update snoop.state with new breath, and
:: redirect the request.
=/ new-snoop
(~(jab by snoop.state) empath beth)
:_ state(snoop new-snoop)
(send [302 ~ [%redirect (~(got by paths.state) empath)]])
==
:: authenticated GET
++ get
^- (quip card _state)
=/ site site.req
?+ site pub
::
[%apps %prism ~]
:_ state
(send [200 ~ [%manx ~(home view state)]])
::
[%apps %prism %shortlinks ~]
:_ state
(send [200 ~ [%manx ~(shortlinks view state)]])
==
:: authenticated POST
++ pot
^- (quip card _state)
=/ site site.req
?+ site dump
++ hc
|_ =bowl:gall
::
++ handle-http
|_ [eyre-id=@ta =inbound-request:eyre]
+* req (parse-request-line:server url.request.inbound-request)
body body.request.inbound-request
send (cury response:schooner eyre-id)
beth (cury update-breath inbound-request)
dump [(send [404 ~ [%none ~]]) state]
derp [(send [500 ~ [%stock ~]]) state]
:: all public requests
++ pub
^- (quip card _state)
=/ site site.req
?+ site dump
::
[%apps %prism %direct ~]
?~ body.request.inbound-request derp
=/ jon=(unit json)
(de:json:html q.u.body.request.inbound-request)
?~ jon derp
=/ act=prism-action (dejs-direct +.jon)
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action act)
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [200 ~ [%manx ~(shortlinks view +.u.scat)]])
[%apps %prism @t ~] :: /apps/prism/{path in paths.state}
:: it has to be a valid @ta or it won't go in paths.state
?. ((sane %ta) i.t.t.site) dump
:: "M-path", maybe path
=/ empath `@ta`i.t.t.site
:: dump if it doesn't exist
?. (~(has by paths.state) empath) dump
:: dump if it's disabled
?: (~(has in brats.state) empath) dump
:: get the breath for this wright, pull relevant
:: parameters, update snoop.state with new breath, and
:: redirect the request.
=/ new-snoop
(~(jab by snoop.state) empath beth)
:_ state(snoop new-snoop)
(send [302 ~ [%redirect (~(got by paths.state) empath)]])
==
:: authenticated GET
++ get
^- (quip card _state)
=/ site site.req
?+ site pub
::
[%apps %prism @t %defect ~]
:: attempt to handle the %defect action
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action `prism-action`[%defect i.t.t.site])
:: if applying the defect action failed, send 500
?~ scat derp
:: otherwise add our response card to the list of effects.
:_ +.u.scat
%+ weld
-.u.scat
(send [200 ~ [%manx ~(shortlinks view +.u.scat)]])
[%apps %prism ~]
:_ state
(send [200 ~ [%manx ~(shortlinks view state)]])
::
[%apps %prism %shortlinks ~]
:_ state
(send [303 ~ [%redirect '/apps/prism']])
::
[%apps %prism %direct ~]
:_ state
(send [200 ~ [%manx ~(direct view state)]])
::
[%apps %prism %divert ~]
:_ state
(send [200 ~ [%manx ~(divert view state)]])
::
[%apps %prism %about ~]
:_ state
(send [200 ~ [%manx ~(about view state)]])
::
[%apps %prism %updates ~]
:_ state
(send [200 ~ [%manx ~(updates view state)]])
==
:: authenticated POST
++ pot
^- (quip card _state)
=/ site site.req
?+ site dump
::
[%apps %prism %direct ~]
?~ body.request.inbound-request derp
=/ jon=(unit json)
(de:json:html q.u.body.request.inbound-request)
?~ jon derp
=/ act=prism-action (dejs-direct +.jon)
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action act)
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [303 ~ [%redirect '/apps/prism']])
::
[%apps %prism %validate %unique ~]
:: check whether the shortlink is unique
?~ body.request.inbound-request derp
=/ jon=(unit json)
(de:json:html q.u.body.request.inbound-request)
?~ jon derp
:: same inputs as the direct action, just use dejs-direct
=/ act=prism-action (dejs-direct +.jon)
:: satisfy the type checker by insisting that this is a %direct
?. ?=([%direct *] act) derp
:_ state
?. (~(has by paths) wright.act)
(send [200 ~ [%none ~]])
(send [200 ~ [%plain "{(trip wright.act)} is already bound!"]])
::
[%apps %prism %divert ~]
?~ body.request.inbound-request derp
=/ jon=(unit json)
(de:json:html q.u.body.request.inbound-request)
?~ jon derp
=/ act=prism-action (dejs-divert +.jon)
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action act)
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [303 ~ [%redirect '/apps/prism']])
::
[%apps %prism @t %defect ~]
:: attempt to handle the %defect action
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action `prism-action`[%defect i.t.t.site])
:: if applying the defect action failed, send 500
?~ scat derp
:: otherwise add our response card to the list of effects.
:_ +.u.scat
%+ weld
-.u.scat
(send [303 ~ [%redirect '/apps/prism']])
::
[%apps %prism @t %renege ~]
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action `prism-action`[%renege i.t.t.site])
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [303 ~ [%redirect '/apps/prism']])
::
[%apps %prism @t %delete ~]
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action `prism-action`[%delete i.t.t.site])
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [303 ~ [%redirect '/apps/prism']])
==
--
::
++ handle-action
|= act=prism-action
^- (quip card _state)
:: the last number we added to $steps is the highest number. we
:: don't always minimize the keys in steps (when deleting, for
:: instance), so it makes sense to derive this on demand rather
:: than keeping it in state.
=/ max-step=@ud
(~(rep by steps) |=([[* ord=@ud] acc=@ud] (max ord acc)))
:: the next step is the next odd integer.
=/ next-step=@ud (add 2 max-step)
?- -.act
%direct
:: ensure we got a valid @ta in the action (this will
:: be a url segment, so we have to be strict)
?. ((sane %ta) wright.act)
~|("Invalid path segment {<wright.act>}" !!)
:: ensure we don't already have a redirect at this path
?: (~(has by paths) wright.act)
~|("Path /apps/prism/{<wright.act>} already assigned" !!)
:: ensure toward is a properly formed URL
?~ (de-purl:html toward.act)
~|('URL is invalid' !!)
:- ~
:: add the path, initialize its entry in our snoop.state
%= state
paths (~(put by paths) wright.act toward.act)
steps (~(put by steps) wright.act next-step)
snoop (~(put by snoop) wright.act *breath)
==
::
[%apps %prism @t %renege ~]
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action `prism-action`[%renege i.t.t.site])
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [200 ~ [%manx ~(shortlinks view +.u.scat)]])
%divert
:: generate a random path segment for this redirect
?~ (de-purl:html toward.act)
~|('URL is invalid' !!)
:: random number generation starts from 0, so we need to
:: find the largest two-segment @p, i.e. `@p`(pow 2 16),
:: and then subtract the smallest two-segment patp,
:: which is (pow 2 8, then add one less again.
:: this gives us a random four-segment patp.
=/ scope=@ud (sub (pow 2 16) (pow 2 8))
=/ shift=@ud (sub (pow 2 8) 1)
:: generate a segment we don't already have
=/ segment=@ta
:: start with some random patp
=/ pat=@p `@p`(add (~(rad og eny.bowl) scope) shift)
|-
:: strip the sig from the front of our random patp
=/ strip=@ta
^- @ta
%- crip
:: interpolate pat into a string, and take its tail,
:: which doesn't include the sig
=< +.. "{<pat>}"
:: if we already have a path like this, add 1 until we find a
:: unique path. there are 64.000 stars, so we will never
:: do this, but still, better safe than sorry!
?: (~(has by paths) strip)
%= $
pat `@p`(add pat 1)
==
strip
::
~& "/apps/prism/{(trip `@t`segment)} -> {(trip toward.act)}"
:- ~
%= state
paths (~(put by paths) segment toward.act)
steps (~(put by steps) segment next-step)
snoop (~(put by snoop) segment *breath)
==
::
[%apps %prism @t %delete ~]
=/ scat=(unit (quip card _state))
%- mole
|. (handle-action `prism-action`[%delete i.t.t.site])
?~ scat derp
:_ +.u.scat
%+ weld
-.u.scat
(send [200 ~ [%manx ~(shortlinks view +.u.scat)]])
%defect
?. (~(has by paths) wright.act)
~|("There is no forward from /apps/prism/{<wright.act>}" !!)
?: (~(has in brats) wright.act)
~|("Path /apps/prism/{<wright.act>} is already disabled" !!)
[~ state(brats (~(put in brats) wright.act))]
::
%renege
?. (~(has by paths) wright.act)
~|("There is no forward from /apps/prism/{<wright.act>}" !!)
?. (~(has in brats) wright.act)
~|("Path /apps/prism/{<wright.act>} is already enabled" !!)
[~ state(brats (~(del in brats) wright.act))]
::
%delete
?. (~(has by paths) wright.act)
~|("There is no forward from /apps/prism/{<wright.act>}" !!)
?> (~(has by snoop) wright.act)
:: remove the deleted entry from brats, if it was there.
=/ clean-brats
?: (~(has in brats) wright.act)
(~(del in brats) wright.act)
brats
:- ~
%= state
paths (~(del by paths) wright.act)
steps (~(del by steps) wright.act)
snoop (~(del by snoop) wright.act)
brats clean-brats
==
==
--
::
++ handle-action
|= act=prism-action
^- (quip card _state)
?- -.act
%direct
:: ensure we got a valid @ta in the action (this will
:: be a url segment, so we have to be strict)
?. ((sane %ta) wright.act)
~|("Invalid path segment {<wright.act>}" !!)
:: ensure we don't already have a redirect at this path
?: (~(has by paths) wright.act)
~|("Path /apps/prism/{<wright.act>} already assigned" !!)
:: ensure toward is a properly formed URL
?~ (de-purl:html toward.act)
~|('URL is invalid' !!)
:- ~
:: add the path, initialize its entry in our snoop.state
%= state
paths (~(put by paths) wright.act toward.act)
snoop (~(put by snoop) wright.act *breath)
==
::
%defect
?. (~(has by paths) wright.act)
~|("There is no forward from /apps/prism/{<wright.act>}" !!)
?: (~(has in brats) wright.act)
~|("Path /apps/prism/{<wright.act>} is already disabled" !!)
[~ state(brats (~(put in brats) wright.act))]
::
%renege
?. (~(has by paths) wright.act)
~|("There is no forward from /apps/prism/{<wright.act>}" !!)
?. (~(has in brats) wright.act)
~|("Path /apps/prism/{<wright.act>} is already enabled" !!)
[~ state(brats (~(del in brats) wright.act))]
::
%delete
?. (~(has by paths) wright.act)
~|("There is no forward from /apps/prism/{<wright.act>}" !!)
?> (~(has by snoop) wright.act)
=/ clean-brats
?: (~(has in brats) wright.act)
(~(del in brats) wright.act)
brats
:- ~
%= state
paths (~(del by paths) wright.act)
snoop (~(del by snoop) wright.act)
brats clean-brats
==
==
--

View File

@ -2,7 +2,7 @@
[%info 'A URL shortener for your urbit']
[%color 0xff.ffff]
[%image 'https://f004.backblazeb2.com/file/demiurge/normul-postem/counter.gif']
[%version [0 6 2]]
[%version [0 7 0]]
[%website 'https://github.com/yungcalibri/prism']
[%license 'MIT']
[%site /apps/prism]

View File

@ -0,0 +1,8 @@
:: to add a new redirect with a random path
:: :prism|divert 'https://vienna.earth/'
:- %say
|= $: ^
[toward=@t ~]
~
==
[%prism-action [%divert toward]]

View File

@ -1,5 +1,16 @@
/- *prism
|_ [%0 =paths =brats =snoop]
/* docket %docket-0 /desk/docket-0
|_ [%1 =paths =steps =brats =snoop]
::
++ version
^- tape
=/ vex version.docket
"v{<major.vex>}.{<minor.vex>}.{<patch.vex>}"
::
++ version-json
^- tape
=/ vex version.docket
"{<major.vex>}.{<minor.vex>}.{<patch.vex>}"
::
++ page
|= kid=marl
@ -46,30 +57,31 @@
=integrity "sha384-39Mph3QgxUJ4Ou1dsJkb8LY0baiOtTwuW7LYX/pqchlr1glQOp1X8LL1LAkTlv5N"
=src "https://unpkg.com/@yungcalibri/layout@0.1.5/dist/bundle.js";
;script
=async ""
=crossorigin "anonymous"
=integrity "sha384-aOxz9UdWG0yBiyrTwPeMibmaoq07/d3a96GCbb9x60f3mOt5zwkjdbcHFnKH8qls"
=src "https://unpkg.com/htmx.org@1.9.0";
;script
=async ""
=crossorigin "anonymous"
=integrity "sha384-nRnAvEUI7N/XvvowiMiq7oEI04gOXMCqD3Bidvedw+YNbj7zTQACPlRI3Jt3vYM4"
=src "https://unpkg.com/htmx.org@1.9.0/dist/ext/json-enc.js";
;script
=async ""
=crossorigin "anonymous"
=integrity "sha384-8IQLVSa8SPeOEPFM9W1QHw0NcfoMataSHwhy8Nn9YBopVPLyDPnmR3+LnmZe0c+Q"
=src "https://unpkg.com/htmx.org@1.9.0/dist/ext/include-vals.js";
;script
=crossorigin "anonymous"
=integrity "sha384-rxmVjgE5bq1dIwkIxiYTYvnbO8P5U3eqfC/7oVtfj6sKu8M0P6Tozd2uZcmGNgNj"
=src "https://unpkg.com/compare-versions@6.1.0/lib/umd/index.js";
;script
=async ""
=crossorigin "anonymous"
=integrity "sha384-SWTvl6gg9wW7CzNqGD9/s3vxwaaKN2g8/eYyu0yT+rkQ/Rb/6NmjnbTi9lYNrpZ1"
=src "https://unpkg.com/hyperscript.org@0.9.11";
;script:"htmx.logAll();"
;script(type "text/hyperscript"):"on load set localStorage['prism-version'] to '[0,6,2]'"
;script: {page-script}
;style: {style}
==
;body(hx-ext "json-enc,include-vals")
;body(hx-boost "true", hx-ext "json-enc,include-vals")
::
;* kid
::
@ -94,15 +106,25 @@
==
==
;hr;
;nav(class "justify-content:end")
;nav
=class "justify-content:end"
=data-script "on load call updateNotification() then if localStorage['prism-updated'] then add .glow to #updates"
;stack-l(space "var(--s0)", style "align-items: stretch;")
;a/"/apps/prism": About Prism
;a/"/apps/prism/shortlinks": Shortlinks
;a/"/apps/prism": Shortlinks
;a/"/apps/prism/about": About Prism
;a
=id "updates"
=href "/apps/prism/updates"
=data-script "on click remove .glow from me then call localStorage.removeItem('prism-updated')"
; Latest Updates
==
==
==
;hr;
;footer(class "position:sticky bottom:0")
;cluster-l(class "justify-content:end")
;footer(class "position:sticky bottom:0", style "padding-bottom: 1rem;")
;cluster-l(class "justify-content:end", space "var(--s-1)")
;small
;+ ;/ version:.
==
;a/"https://github.com/yungcalibri/prism": Prism on Github
==
==
@ -129,8 +151,8 @@
==
:: end content
==
:: +home: home page (for the beta, anyway) with details about Prism
++ home
:: +about: details about Prism
++ about
^- manx
%- page
;* ;=
@ -201,6 +223,39 @@
:: end content
==
::
:: +updates: latest updates
++ updates
^- manx
%- page
;* ;=
:: begin content
;h2: v0.7.0
;p
; Quality-of-life improvements!
;ul
;li: The shortlinks page is now the main page for the app.
;li
; By default, the web UI will now randomly generate
; a shortlink URL drawn from the star namespace.
; This behavior is available in the terminal as well;
; see
;code:":prism|divert"
; under
;a/"#usage": Usage.
==
;li
; New links will now be shown in the order in which
; they were added. (Since there was no order
; information before, links added before v0.7.0 will
; appear in the same order they used to.
; Reordering from the UI will be possible in a future
; version.)
==
==
==
:: end content
==
::
++ usage
^- manx
;aside#usage
@ -224,6 +279,20 @@
==
;div:"to create a redirect from /apps/prism/fragment to https://urbit.org."
==
;div
;pre
;code
;span:":prism|"
;em:"divert"
;span:" 'https://newgrounds.com'"
==
==
;div
; to create a redirect from a random path to https://newgrounds.com.
; The random path will be drawn from the set of stars, e.g.:
; /apps/prism/ronfeb
==
==
;div
;pre
;code
@ -260,6 +329,11 @@
:: (eventually) provides controls to manage them.
++ shortlinks
^- manx
=/ sorted-paths=(list (pair wright @t))
%+ sort
~(tap by paths)
|= [[a=wright *] [b=wright *]]
(gth (~(got by steps) a) (~(got by steps) b))
%- page
;* ;=
:: begin content
@ -269,8 +343,10 @@
;/ "No shortlinks created yet"
==
;stack-l(space "var(--s0)")
;+ divert:.
;hr;
;* %+ turn
~(tap by paths)
sorted-paths
|= [wright=@ta toward=@t]
=/ beth=breath (~(got by snoop) wright)
=/ hits (~(gut bi beth) '' '' 0)
@ -292,7 +368,10 @@
==
;div
; To:
;a/"{(trip toward)}"
;a
=target "_blank"
=rel "noopener noreferer"
=href "{(trip toward)}"
;code: {(trip toward)}
==
==
@ -320,55 +399,132 @@
==
==
==
;hr;
;section(class "shortlink new")
;stack-l
;h3: New shortlink
;form#new-shortlink
=hx-post "/apps/prism/direct"
=hx-target "#content"
=hx-select "#content"
;sidebar-l(side "right", sideWidth "5em")
;stack-l(space "var(--s-2)")
;label
; From:
;br;
;code: /apps/prism/
;input
=name "wright"
=type "text"
=pattern "[a-zA-Z-._~]+"
=required ""
=placeholder "vienna"
=style "color: #f95";
==
;label
; To:
;br;
;input
=name "toward"
=type "url"
=required ""
=placeholder "https://vienna.earth/";
==
==
:: end content
==
::
++ direct
^- manx
;section(class "shortlink new", data-prism-action "direct")
;stack-l
;h3: New shortlink
;form#new-shortlink
=hx-post "/apps/prism/direct"
=hx-target "#content"
=hx-select "#content"
;sidebar-l(side "right", sideWidth "5em")
;div
;label(for "direct-toward")
; To:
==
;stack-l(class "justify-content:end")
;button: Create
;br;
;input#direct-toward
=name "toward"
=type "url"
=required ""
=placeholder "https://vienna.earth/";
;br;
;br;
;label(for "direct-wright")
; From:
==
;div(style "display: block-inline")
;code(style "display: inline"): /apps/prism/
;input#direct-wright
=hx-post "/apps/prism/validate/unique"
=hx-trigger "keyup changed delay:300ms"
=hx-target "next .error"
=hx-select "unset"
=name "wright"
=type "text"
=required ""
=placeholder "vienna"
=style "color: #f95";
==
;div(style "display: flex;")
;div(style "font-family: 'Cousine'; width: 13ch;");
;div.error;
==
==
;stack-l(class "justify-content:end")
;button: Create
==
==
==
==
==
:: end content
::
++ divert
^- manx
;section(class "shortlink new", data-prism-action "divert")
;stack-l
;h3: New shortlink
;form#new-shortlink
=hx-post "/apps/prism/divert"
=hx-target "#content"
=hx-select "#content"
;sidebar-l(side "right", sideWidth "5em")
;div
;label(for "divert-toward")
; To:
==
;br;
;input#divert-toward
=name "toward"
=type "url"
=required ""
=placeholder "https://vienna.earth/";
;br;
;p
; A URL segment will be generated automatically.
; You can also
;a
=href ""
=hx-get "/apps/prism/direct"
=hx-target "closest .shortlink.new"
=hx-swap "outerHTML"
=hx-select "unset"
; configure a custom path for this redirect.
==
==
==
;stack-l(class "justify-content:end")
;button: Create
==
==
==
==
==
::
++ page-script
"""
function updateNotification() \{
if (localStorage['prism-updated']) \{
return true;
}
const latest = {<version-json>};
const previous = localStorage.getItem('prism-version');
if (
!previous ||
previous === "[0,6,2]" ||
window.compareVersions.compare(latest, previous, '>')
) \{
localStorage.setItem('prism-version', latest);
localStorage.setItem('prism-updated', true);
console.log("Looks like there's a new version in town.");
} else \{
console.log("No new updates. Yippie-ki-yay.");
}
}
"""
::
++ style
^~
%- trip
'''
:root {
--measure: 80ch;
--glow-color: dodgerblue;
}
body {
font-family: 'Castoro', serif;
@ -499,5 +655,32 @@
margin-top: 0;
margin-bottom: 0;
}
.error {
color: #a32;
font-size: 80%;
min-height: 1.5lh;
padding-block: 0.25lh;
}
.glow {
position: relative;
}
.glow::before {
content: "◈";
position: absolute;
bottom: 0;
left: 0;
color: var(--glow-color);
pointer-events: none;
opacity: 0;
animation: pulse 3s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 0;
}
50% {
opacity: 1;
}
}
'''
--

View File

@ -10,6 +10,14 @@
%. jon
(ot ~[wright+so toward+so])
::
++ dejs-divert
|= jon=json
=, dejs:format
^- prism-action
:- %divert
%. jon
(ot ~[toward+so])
::
++ utm-parameters
^~
^- (set @t)
@ -114,5 +122,5 @@
%+ gasp-referer
req
spyr
:: ++ dejs-action
::
--

View File

@ -39,7 +39,7 @@
:- :- http-status
%+ weld headers
['content-type'^'application/json']~
`(as-octt:mimes:html (en-json:html j.resource))
`(as-octs:mimes:html (en:json:html j.resource))
::
%html
:- :- http-status

View File

@ -19,7 +19,7 @@
++ json-to-octs
|= jon=json
^- octs
(as-octt:mimes:html (en-json:html jon))
(as-octs:mimes:html (en:json:html jon))
::
++ app
|%

View File

@ -16,6 +16,10 @@
:: redirect url.
+$ paths (map wright @t)
::
:: $steps: maps path segments to the order in which they
:: should be rendered in the UI. odd integers.
+$ steps (map wright @ud)
::
:: $brats: set of all disabled wrights.
+$ brats (set wright)
::
@ -27,10 +31,12 @@
::
+$ prism-action
:: %direct: add a new wright
:: %divert: add a new redirect, generating a random path for the link
:: %defect: disable an existing wright
:: %renege: re-enable a wright
:: %delete: delete a wright
$% [%direct =wright toward=@t]
[%divert toward=@t]
[%defect =wright]
[%renege =wright]
[%delete =wright]

View File

@ -1 +1 @@
[%zuse 413]
[%zuse 412]