From e69afcff1c1f42436998ca0b070162d556dd577c Mon Sep 17 00:00:00 2001 From: polwex Date: Thu, 18 Sep 2025 10:04:43 +0700 Subject: more --- NOTES.md | 5 + desk/app/nostrill.hoon | 101 +++++---------- desk/lib/mutations/nostr.hoon | 243 +++++++++++++++++++++++++++++++++++ desk/lib/mutations/trill.hoon | 78 ++++++++++++ desk/lib/nostr.hoon | 51 -------- desk/lib/nostr/events.hoon | 49 ++++++++ desk/lib/nostr/keys.hoon | 51 ++++++++ desk/lib/nostrill.hoon | 54 +------- desk/lib/nostrill/comms.hoon | 4 +- desk/lib/nostrill/follows.hoon | 7 +- desk/lib/nostrill/mutations.hoon | 250 ------------------------------------- desk/lib/scri.hoon | 9 ++ desk/lib/shim.hoon | 6 +- desk/sur/nostrill/comms.hoon | 21 ++++ front/src/styles/ThemeProvider.tsx | 144 +++++++++++++++++++++ front/src/styles/styles.css | 15 +++ 16 files changed, 663 insertions(+), 425 deletions(-) create mode 100644 desk/lib/mutations/nostr.hoon create mode 100644 desk/lib/mutations/trill.hoon delete mode 100644 desk/lib/nostr.hoon create mode 100644 desk/lib/nostr/events.hoon create mode 100644 desk/lib/nostr/keys.hoon delete mode 100644 desk/lib/nostrill/mutations.hoon diff --git a/NOTES.md b/NOTES.md index 79406c2..cfca357 100644 --- a/NOTES.md +++ b/NOTES.md @@ -45,3 +45,8 @@ lol forget the frontend make it work for Primal - ~It appears relays ignore you if you pass more than 11 authors on a filter~ - Turns out the pubkeys were wrong because we were not padding the hex strings to 64 chars. fixed now - Relay send a notice if too many concurrent REQs + +# Hard stuff + +It's hard to sync with the Nostr relays. We can fetch a bunch of posts and that's fine but then we want to fetch other data related to those posts, say the authors metadata or reactions or whatever. So we do it in bulk, but when? Nostr data keeps trickling in if we're subscribed through the shim. + diff --git a/desk/app/nostrill.hoon b/desk/app/nostrill.hoon index 721a590..ddbcd61 100644 --- a/desk/app/nostrill.hoon +++ b/desk/app/nostrill.hoon @@ -1,7 +1,11 @@ -/- sur=nostrill, nsur=nostr, tf=trill-feed -/+ lib=nostrill, nlib=nostr, sr=sortug, scri, - shim, dbug, muta=nostrill-mutations, jsonlib=json-nostrill, - trill=trill-post, comms=nostrill-comms, followlib=nostrill-follows +/- sur=nostrill, nsur=nostr, tf=trill-feed, comms=nostrill-comms +/+ lib=nostrill, nostr-keys, sr=sortug, scri, + shim, dbug, + evlib=nostr-events, + mutations-nostr, + mutations-trill, + jsonlib=json-nostrill, + trill=trill-post, commlib=nostrill-comms, followlib=nostrill-follows /= web /web/router |% +$ versioned-state $%(state-0:sur) @@ -14,10 +18,11 @@ +* this . rout ~(. router:web [state bowl]) cards ~(. cards:lib bowl) - mutat ~(. muta [state bowl]) + mutan ~(. mutations-nostr [state bowl]) + mutat ~(. mutations-trill [state bowl]) shimm ~(. shim [state bowl]) scry ~(. scri [state bowl]) - coms ~(. comms [state bowl]) + coms ~(. commlib [state bowl]) fols ~(. followlib [state bowl]) ++ on-init ^- (quip card:agent:gall agent:gall) @@ -65,7 +70,7 @@ ?~ msg `this ?> ?=(%ws -.u.msg) :: =^ cards state (handle-shim-msg:mutat u.msg) - =^ cards state (handle-ws:mutat +.u.msg) + =^ cards state (handle-ws:mutan +.u.msg) [cards this] :: @@ -82,63 +87,19 @@ %fols (handle-fols +.u.upoke) %begs (handle-begs +.u.upoke) %prof (handle-prof +.u.upoke) - %post (handle-post +.u.upoke) %rela (handle-rela +.u.upoke) + %post =^ cs state + (handle-post:mutat +.u.upoke) + [cs this] == ++ handle-cycle-keys - =/ ks (gen-keys:nlib eny.bowl) + =/ ks (gen-keys:nostr-keys eny.bowl) =. keys [ks keys] :: =/ nkeys keys(i ks, t `(list keys:nsur)`keys) :: :: =. keys nkeys ~& new-keys=keys `this - ++ handle-post |= poke=post-poke:ui:sur - ?- -.poke - %add - =/ sp (build-sp:trill our.bowl our.bowl content.poke ~ ~) - =/ p (build-post:trill now.bowl pub.i.keys sp) - =. state (add-to-feed:mutat p) - =/ profile (~(get by profiles) [%urbit our.bowl]) - =/ pw [p (some pub.i.keys) ~ ~ profile] - =/ =fact:ui:sur [%post %add pw] - =/ card (update-ui:cards fact) - :_ this :~(card) - %quote - =/ sp (build-sp:trill our.bowl our.bowl content.poke ~ ~) - =/ quote [%ref %trill host.poke /(crip (scow:sr %ud id.poke))] - =. contents.sp (snoc contents.sp quote) - =/ p (build-post:trill now.bowl pub.i.keys sp) - =. state (add-to-feed:mutat p) - =/ profile (~(get by profiles) [%urbit our.bowl]) - =/ pw [p (some pub.i.keys) ~ ~ profile] - =/ =fact:ui:sur [%post %add pw] - =/ card (update-ui:cards fact) - :_ this :~(card) - %reply - =/ sp (build-sp:trill host.poke our.bowl content.poke `id.poke `thread.poke) - =/ p (build-post:trill now.bowl pub.i.keys sp) - =. state (add-to-feed:mutat p) - =/ profile (~(get by profiles) [%urbit our.bowl]) - =/ pw [p (some pub.i.keys) ~ ~ profile] - =/ =fact:ui:sur [%post %add pw] - =/ card (update-ui:cards fact) - :_ this :~(card) - %rp - =/ quote [%ref %trill host.poke /(crip (scow:sr %ud id.poke))] - =/ sp (build-sp:trill host.poke our.bowl '' ~ ~) - =. contents.sp ~[quote] - =/ p (build-post:trill now.bowl pub.i.keys sp) - =. state (add-to-feed:mutat p) - =/ profile (~(get by profiles) [%urbit our.bowl]) - =/ pw [p (some pub.i.keys) ~ ~ profile] - =/ =fact:ui:sur [%post %add pw] - =/ card (update-ui:cards fact) - :_ this :~(card) - - :: %rt `this - :: %del `this - == ++ handle-begs |= poke=begs-poke:ui:sur ?- -.poke %feed @@ -177,9 +138,9 @@ [cs this] :: %send - =/ upoast (get-poast:mutat host.poke id.poke) + =/ upoast (get-poast:scry host.poke id.poke) ?~ upoast `this - =/ event (post-to-event:lib i.keys eny.bowl u.upoast) + =/ event (post-to-event:evlib i.keys eny.bowl u.upoast) =/ req=bulk-req:shim:nsur [relays.poke %event event] =/ cards :~((send:shimm req)) [cards this] @@ -194,7 +155,7 @@ =/ l ~| "wtf" (need lol) `this %genkey - =/ keys (gen-keys:nlib eny.bowl) + =/ keys (gen-keys:nostr-keys eny.bowl) ~& pub=(scow:sr %ux -.keys) ~& priv=(scow:sr %ux +.keys) `this @@ -229,7 +190,7 @@ - (~(put in -.acc) id.ev) + (~(put in +.acc) pubkey.ev) == - =^ cards state (populate-profiles:mutat -.ids) + =^ cards state (populate-profiles:mutan -.ids) :: (get-profiles:shimm +.ids) :: (get-engagement:shimm -.ids) [cards this] @@ -254,7 +215,7 @@ ~& >>> invalid=invalid `this =/ p=event:nsur +.i.poasts - =/ valid (validate-pubkey:nlib pubkey.p) + =/ valid (validate-pubkey:nostr-keys pubkey.p) ?. valid =/ ids (crip (scow:sr %ux id.p)) ~& ids @@ -270,7 +231,7 @@ =/ npks (~(put in pubkeys) pubkey.p) $(poasts t.poasts, pubkeys npks) :: - =^ cards state (populate-profiles:mutat pks) + =^ cards state (populate-profiles:mutan pks) [cards this] %ui =/ =fact:ui:sur [%post %add *post-wrapper:sur] @@ -326,16 +287,22 @@ ~& on-agent=[wire -.sign] :: if p.sign is not ~ here that means it's intentional ?+ wire `this - [%refollow ~] - ?. ?=(%watch-ack -.sign) `this - ?~ p.sign `this - =^ cs state (handle-kick-nack:fols src.bowl) [cs this] [%follow ~] + ?: ?=(%watch-ack -.sign) + ?~ p.sign `this + =^ cs state (handle-kick-nack:fols src.bowl) [cs this] ?: ?=(%kick -.sign) =^ cs state (handle-refollow:fols src.bowl) [cs this] ?. ?=(%fact -.sign) `this - =^ cs state (handle-agent-res:fols q.q.cage.sign) + + =/ =fact:comms ;; fact:comms q.q.cage.sign + =^ cs state + ?- -.fact + %init (handle-follow-res:fols +.fact) + %post (handle-post-fact:mutat +.fact) + %prof (handle-prof-fact:mutan +.fact) + == [cs this] == @@ -351,7 +318,7 @@ =/ msg (parse-body:shimm jstring) ?~ msg ~& `@t`jstring `this ?> ?=(%http -.u.msg) - =^ cards state (handle-http:mutat sub-id.wire +.u.msg) + =^ cards state (handle-http:mutan sub-id.wire +.u.msg) `this == :: diff --git a/desk/lib/mutations/nostr.hoon b/desk/lib/mutations/nostr.hoon new file mode 100644 index 0000000..2c6acd5 --- /dev/null +++ b/desk/lib/mutations/nostr.hoon @@ -0,0 +1,243 @@ +/- sur=nostrill, nsur=nostr, comms=nostrill-comms, + post=trill-post, gate=trill-gate, feed=trill-feed + +/+ appjs=json-nostrill, + lib=nostrill, + njs=json-nostr, + postlib=trill-post, + shim, + sr=sortug + +|_ [=state:sur =bowl:gall] ++$ card card:agent:gall +:: events +++ 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) + :: TODO + `state + +:: ++ parse-events +:: |= evs=(list event:nsur) +:: ^- (quip card _state) +:: =| cards=(list card) +:: =^ cards state +:: |- ?~ evs [cards state] +:: =^ cards state (handle-event i.evs) +:: $(evs t.evs) +:: [cards state] + +++ populate-profiles + |= pubkeys=(set @ux) + ^- (quip card _state) + =/ shimm ~(. shim [state bowl]) + =^ cards state (get-profiles-http:shimm pubkeys) + [cards state] + + + + + +++ handle-http + |= [sub-id=@t msgs=(list relay-msg:nsur)] + ~& handling-http=[sub-id (lent msgs)] + =| cards=(list card) + |- ?~ msgs [cards state] + =^ cards state (handle-msg i.msgs) + $(msgs t.msgs) + + +++ handle-msg |= msg=relay-msg:nsur + ^- (quip card _state) + ?+ -.msg `state + %event + (handle-event '' sub-id.msg event.msg) + %eose + `state + %closed + `state + %auth ~& >> auth=+.msg :: TODO handle auth challenges? + `state + %notice ~& >> notice=+.msg :: TODO pass to UI? + `state + %error ~& >>> relay-error=+.msg + `state + == + +++ handle-ws |= [relay=@t msg=relay-msg:nsur] + =/ rs (~(get by relays.state) relay) + ?~ rs :: TODO do we really + `state + =^ cards state + ~& handle-ws=-.msg + ?- -.msg + %ok (handle-ok relay +.msg) + %event + =. relays.state (update-relay-stats relay sub-id.msg) + (handle-event relay sub-id.msg event.msg) + + %eose + :: TODO do unsub for replaceable/addressable events + =/ creq (~(get by reqs.u.rs) +.msg) + ?~ creq `state + :: =. reqs.u.rs (~(del by reqs.u.rs) +.msg) + :: =. relays.state (~(put by relays.state) relay u.rs) + =/ cardslib ~(. cards:lib bowl) + =/ c (update-ui:cardslib [%nostr nostr-feed.state]) + :_ state :~(c) + %closed =. reqs.u.rs (~(del by reqs.u.rs) sub-id.msg) + =. relays.state (~(put by relays.state) relay u.rs) + `state + %auth ~& >> auth=+.msg :: TODO handle auth challenges? + `state + %notice ~& >> notice=+.msg :: TODO pass to UI? + `state + %error ~& >>> relay-error=+.msg + =. relays.state (~(del by relays.state) relay) + `state + == + [cards state] + + + :: =^ cards state (handle-event:mutat url.u.msg sub-id.u.msg event.u.msg) + :: :: TODO not just stash events + :: =/ relay (~(get by relays) url.u.msg) + :: =/ nevents=(list event:nsur) ?~ relay [event.u.msg ~] [event.u.msg u.relay] + :: =/ nevents2 (scag 100 nevents) + + :: =. relays (~(put by relays) url.u.msg nevents2) + :: :: TODO respond better + :: =/ response (ebail:rout id.order) + :: =/ ncards (weld cards response) + + :: [ncards this] + :: `state + +++ update-relay-stats + |= [relay=@t sub-id=@t] ^+ relays.state + + =/ cur (~(get by relays.state) relay) + =/ curr ?~ cur *relay-stats:nsur u.cur + =? connected.curr ?=(%~ connected.curr) (some now.bowl) + =/ creq (~(get by reqs.curr) sub-id) + ?~ creq relays.state :: bail + =/ nreq u.creq(received +(received.u.creq)) + =. reqs.curr (~(put by reqs.curr) sub-id nreq) + (~(put by relays.state) relay curr) + +++ handle-ok |= [relay=@t event-id=@ux accepted=? msg=@t] + ^- (quip card _state) + :: TODO pass to UI + `state + + +++ handle-event + |= [relay=@t sub-id=@t =event:nsur] + ^- (quip card _state) + |^ + ~& parsing-nostr-event=kind.event +:: https://nostrdata.github.io/kinds/ + ?: .=(kind.event 666) :: one_off subs eose cf. 999 + parse-shim-oneose + ?: .=(kind.event 0) :: user metadata + parse-metadata + ?: .=(kind.event 1) :: apparently a poast + 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 + ^- (quip card _state) + =/ jstring content.event + =/ ujon (de:json:html jstring) + ?~ ujon ~& failed-parse-metadata=ujon `state + =/ umeta (user-meta:de:njs u.ujon) + ?~ umeta ~& >> failed-dejs-metadata=ujon `state + =. profiles.state (~(put by profiles.state) [%nostr pubkey.event] u.umeta) + `state + + + ++ parse-poast + ^- (quip card _state) + =. nostr-feed.state (put:norm:sur nostr-feed.state created-at.event event) + :: =/ uprof (~(get by profiles.state) pubkey.event) + :: ?~ uprof + :: =/ shimm ~(. shim [state bowl]) + :: =^ cards state (get-profiles:shimm (silt ~[pubkey.event])) + :: [cards state] + + + :: =/ 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) + :: :: TODO wtf + :: =/ cm=content-map:post (init-content-map:postlib cl ts) + + :: =/ p=post:post :* + :: id=ts + :: host=`@p`pubkey.event + :: author=`@p`pubkey.event + :: thread=ts + :: parent=~ + :: children=~ + :: contents=cm + :: read=*lock:gate + :: write=*lock:gate + :: *engagement:post + :: 0v0 + :: *signature:post + :: tags=~ + :: == + :: =/ nfid (put:orm:feed u.fid ts p) + :: =. following.state (~(put by following.state) pubkey.event nfid) + `state + ++ parse-follow + ^- (quip card _state) + =/ following (~(get by follow-graph.state) [%nostr pubkey.event]) + =/ follow-set ?~ following *(set follow:sur) u.following + |- ?~ tags.event `state + =/ t=tag:nsur i.tags.event + :: ?. .=('p' key.t) $(tags.event t.tags.event) + :: =/ pubkeys value.t + :: =/ pubkey (slaw:sr %ux pubkeys) + :: ?~ pubkey ~& "parsing hex error" $(tags.event t.tags.event) + :: =/ relay (snag 0 rest.t) + :: =/ rel ?: .=(relay '') ~ (some relay) + :: =/ nickname (snag 1 rest.t) + :: =/ meta=follow:sur [u.pubkey nickname rel] + :: =. follow-set (~(put in follow-set) meta) + :: =. follow-graph.state (~(put by follow-graph.state) pubkey.event follow-set) + $(tags.event t.tags.event) + ++ parse-shim-oneose + ^- (quip card _state) + =/ rs (~(get by relays.state) relay) + ?~ rs `state + =. reqs.u.rs (~(del by reqs.u.rs) sub-id) + =. relays.state (~(put by relays.state) relay u.rs) + `state + -- + +++ handle-prof-fact |= pf=prof-fact:comms + ^- (quip card _state) + =/ =user:sur [%urbit src.bowl] + ?- -.pf + %prof =. profiles.state (~(put by profiles.state) user +.pf) + :: TODO kinda wanna send it to the UI + `state + %keys `state + :: TODO really need a way to keep track of everyone's pubkeys + == +-- diff --git a/desk/lib/mutations/trill.hoon b/desk/lib/mutations/trill.hoon new file mode 100644 index 0000000..ea6dadf --- /dev/null +++ b/desk/lib/mutations/trill.hoon @@ -0,0 +1,78 @@ +/- sur=nostrill, nsur=nostr, comms=nostrill-comms, + post=trill-post, gate=trill-gate, feed=trill-feed + +/+ appjs=json-nostrill, + lib=nostrill, + trill=trill-post, + njs=json-nostr, + postlib=trill-post, + shim, + sr=sortug + +|_ [=state:sur =bowl:gall] ++$ card card:agent:gall +++ debug-own-feed + =/ postlist (tap:orm:feed feed.state) + =/ lol + |- ?~ postlist ~ + ~& >> poast=+.i.postlist + $(postlist t.postlist) + ~ + +:: state +++ add-to-feed |= p=post:post + =. feed.state (put:orm:feed feed.state id.p p) + state + +++ handle-post |= poke=post-poke:ui:sur + ^- (quip card _state) + =/ profile (~(get by profiles.state) [%urbit our.bowl]) + =/ pubkey pub.i.keys.state + =/ p=post:post + ?- -.poke + %add + =/ sp (build-sp:trill our.bowl our.bowl content.poke ~ ~) + (build-post:trill now.bowl pubkey sp) + %quote + =/ sp (build-sp:trill our.bowl our.bowl content.poke ~ ~) + =/ quote [%ref %trill host.poke /(crip (scow:sr %ud id.poke))] + =. contents.sp (snoc contents.sp quote) + (build-post:trill now.bowl pubkey sp) + %reply + =/ sp (build-sp:trill host.poke our.bowl content.poke `id.poke `thread.poke) + (build-post:trill now.bowl pubkey sp) + %rp + =/ quote [%ref %trill host.poke /(crip (scow:sr %ud id.poke))] + =/ sp (build-sp:trill host.poke our.bowl '' ~ ~) + =. contents.sp ~[quote] + (build-post:trill now.bowl pubkey sp) + == + =/ pw [p (some pubkey) ~ ~ profile] + =/ jfact=fact:ui:sur [%post %add pw] + =/ ui-card (update-ui:cards:lib jfact) + :: only update followers when we are updating our own feed + ?. .=(our.bowl host.p) [~[ui-card] state] + =. state (add-to-feed p) + =/ =fact:comms [%post %add p] + =/ fact-card (update-followers:cards:lib fact) + :_ state + :~ ui-card + fact-card + == + + +++ handle-post-fact |= pf=post-fact:comms + ^- (quip card _state) + =/ =user:sur [%urbit src.bowl] + =/ fed (~(get by following.state) user) + ?~ fed ~& "emmm not following ya" `state + =/ nf=feed:feed + ?: ?=(%del -.pf) + =< + (del:orm:feed u.fed id.pf) + ::mmm people aren't supposed to update if its not their own feeds + :: =/ =user:nsur [%urbit host.p.pdf] + (put:orm:feed u.fed id.p.pf p.pf) + =. following.state (~(put by following.state) user nf) + :: TODO update the ui with the changes + :_ state ~ +-- diff --git a/desk/lib/nostr.hoon b/desk/lib/nostr.hoon deleted file mode 100644 index 90eb563..0000000 --- a/desk/lib/nostr.hoon +++ /dev/null @@ -1,51 +0,0 @@ -/- sur=nostr -/+ js=json-nostr, sr=sortug -|% -++ gen-sub-id |= eny=@ ^- @t - %- crip (scag 60 (scow:sr %uw eny)) -++ gen-keys |= eny=@ ^- keys:sur - =, secp256k1:secp:crypto - =/ privkey - |- - =/ k (~(rad og eny) (bex 256)) - ?. (lth k n.t) $ k - - =/ pubkey (priv-to-pub privkey) - =/ pub (compress-point pubkey) - :: =/ pub (serialize-point pubkey) - [pub=pub priv=privkey] -:: -++ hash-event |= raw=raw-event:sur ^- @ux - =/ jon (raw-event:en:js raw) - =/ jstring (en:json:html jon) - (swp 3 (shax jstring)) - -++ raws - |= [eny=@ bits=@] - ^- [@ @] - [- +>-]:(~(raws og eny) bits) - -++ sign-event |= [priv=@ux hash=@ux eny=@] - =^ sed eny (raws eny 256) - (sign:schnorr:secp256k1:secp:crypto priv hash sed) - - -:: -++ validate-pubkey |= pubkey=@ux ^- ? - =/ tap (scow:sr %ux pubkey) - .= (lent tap) 64 -:: -++ diff-filters -|= [a=filter:sur b=filter:sur] ^- filter:sur - =/ ids (unit-set-dif ids.a ids.b) - =/ authors (unit-set-dif authors.a authors.b) - =/ kinds (unit-set-dif kinds.a kinds.b) - =/ tags ~ - =/ since ~ - =/ until ~ - =/ limit ~ :: TODO - [ids authors kinds tags since until limit] -++ unit-set-dif - |* [a=(unit) b=(unit)] - %^ clap a b |* [x=(set) y=(set)] (~(dif in x) y) --- diff --git a/desk/lib/nostr/events.hoon b/desk/lib/nostr/events.hoon new file mode 100644 index 0000000..2a3e818 --- /dev/null +++ b/desk/lib/nostr/events.hoon @@ -0,0 +1,49 @@ +/- sur=nostrill, nsur=nostr, post=trill-post, gate=trill-gate +/+ js=json-nostr, sr=sortug, trill=trill-post, nostr-keys +|% +++ post-to-event |= [=keys:nsur eny=@ p=post:post] ^- event:nsur + =/ cl (latest-post-content:trill contents.p) + =/ string (crip (content-list-to-md:trill cl)) + =/ ts (to-unix-secs:jikan:sr id.p) + =/ raw=raw-event:nsur [pub.keys ts 1 ~ string] + =/ event-id (hash-event:nostr-keys raw) + =/ signature (sign-event:nostr-keys priv.keys event-id eny) + ~& hash-and-signed=[event-id signature] + =/ =event:nsur :* + event-id + pub.keys + created-at.raw + kind.raw + tags.raw + content.raw + signature + == + event + +++ event-to-post + |= [=event:nsur profile=(unit user-meta:nsur) relay=(unit @t)] + ^- post-wrapper:sur + + =/ cl (tokenize:trill content.event) + =/ ts (from-unix:jikan:sr created-at.event) + =/ cm=content-map:post (init-content-map:trill cl ts) + + :: TODO more about @ps and stuff + =/ p=post:post :* + id=ts + host=`@p`pubkey.event + author=`@p`pubkey.event + thread=ts + parent=~ + children=~ + contents=cm + read=*lock:gate + write=*lock:gate + *engagement:post + 0v0 + *signature:post + tags=~ + == + =/ meta [(some pubkey.event) (some id.event) relay profile] + [p meta] +-- diff --git a/desk/lib/nostr/keys.hoon b/desk/lib/nostr/keys.hoon new file mode 100644 index 0000000..90eb563 --- /dev/null +++ b/desk/lib/nostr/keys.hoon @@ -0,0 +1,51 @@ +/- sur=nostr +/+ js=json-nostr, sr=sortug +|% +++ gen-sub-id |= eny=@ ^- @t + %- crip (scag 60 (scow:sr %uw eny)) +++ gen-keys |= eny=@ ^- keys:sur + =, secp256k1:secp:crypto + =/ privkey + |- + =/ k (~(rad og eny) (bex 256)) + ?. (lth k n.t) $ k + + =/ pubkey (priv-to-pub privkey) + =/ pub (compress-point pubkey) + :: =/ pub (serialize-point pubkey) + [pub=pub priv=privkey] +:: +++ hash-event |= raw=raw-event:sur ^- @ux + =/ jon (raw-event:en:js raw) + =/ jstring (en:json:html jon) + (swp 3 (shax jstring)) + +++ raws + |= [eny=@ bits=@] + ^- [@ @] + [- +>-]:(~(raws og eny) bits) + +++ sign-event |= [priv=@ux hash=@ux eny=@] + =^ sed eny (raws eny 256) + (sign:schnorr:secp256k1:secp:crypto priv hash sed) + + +:: +++ validate-pubkey |= pubkey=@ux ^- ? + =/ tap (scow:sr %ux pubkey) + .= (lent tap) 64 +:: +++ diff-filters +|= [a=filter:sur b=filter:sur] ^- filter:sur + =/ ids (unit-set-dif ids.a ids.b) + =/ authors (unit-set-dif authors.a authors.b) + =/ kinds (unit-set-dif kinds.a kinds.b) + =/ tags ~ + =/ since ~ + =/ until ~ + =/ limit ~ :: TODO + [ids authors kinds tags since until limit] +++ unit-set-dif + |* [a=(unit) b=(unit)] + %^ clap a b |* [x=(set) y=(set)] (~(dif in x) y) +-- diff --git a/desk/lib/nostrill.hoon b/desk/lib/nostrill.hoon index 6d22adc..41caff2 100644 --- a/desk/lib/nostrill.hoon +++ b/desk/lib/nostrill.hoon @@ -1,5 +1,5 @@ -/- post=trill-post, nsur=nostr, sur=nostrill, gate=trill-gate -/+ trill=trill-post, nostr, sr=sortug, jsonlib=json-nostrill +/- post=trill-post, nsur=nostr, sur=nostrill, gate=trill-gate, comms=nostrill-comms +/+ trill=trill-post, nostr-keys, sr=sortug, jsonlib=json-nostrill |% :: ++ default-state |= =bowl:gall ^- state:sur @@ -9,7 +9,7 @@ :: =/ l ~['wss://relay.damus.io' 'wss://nos.lol'] =/ rl %+ turn l |= t=@t [t *relay-stats:nsur] :: =/ l ~[['wss://relay.damus.io' ~]] - =/ key (gen-keys:nostr eny.bowl) + =/ key (gen-keys:nostr-keys eny.bowl) =/ keyl [key ~] s(relays (malt rl), keys keyl) @@ -29,51 +29,6 @@ $(l t.l) :: -++ post-to-event |= [=keys:nsur eny=@ p=post:post] ^- event:nsur - =/ cl (latest-post-content:trill contents.p) - =/ string (crip (content-list-to-md:trill cl)) - =/ ts (to-unix-secs:jikan:sr id.p) - =/ raw=raw-event:nsur [pub.keys ts 1 ~ string] - =/ event-id (hash-event:nostr raw) - =/ signature (sign-event:nostr priv.keys event-id eny) - ~& hash-and-signed=[event-id signature] - =/ =event:nsur :* - event-id - pub.keys - created-at.raw - kind.raw - tags.raw - content.raw - signature - == - event - -++ event-to-post - |= [=event:nsur profile=(unit user-meta:nsur) relay=(unit @t)] - ^- post-wrapper:sur - - =/ cl (tokenize:trill content.event) - =/ ts (from-unix:jikan:sr created-at.event) - =/ cm=content-map:post (init-content-map:trill cl ts) - - :: TODO more about @ps and stuff - =/ p=post:post :* - id=ts - host=`@p`pubkey.event - author=`@p`pubkey.event - thread=ts - parent=~ - children=~ - contents=cm - read=*lock:gate - write=*lock:gate - *engagement:post - 0v0 - *signature:post - tags=~ - == - =/ meta [(some pubkey.event) (some id.event) relay profile] - [p meta] ++ cards |_ =bowl:gall @@ -82,5 +37,8 @@ ++ update-ui |= =fact:ui:sur ^- card:agent:gall =/ jon (fact:en:jsonlib fact) [%give %fact ~[/ui] %json !>(jon)] + :: ++ update-followers |= =fact:comms ^- card:agent:gall + ++ update-followers |= =fact:comms ^- card:agent:gall + [%give %fact ~[/follow] %noun !>(fact)] -- -- diff --git a/desk/lib/nostrill/comms.hoon b/desk/lib/nostrill/comms.hoon index 23e442a..5ae07a0 100644 --- a/desk/lib/nostrill/comms.hoon +++ b/desk/lib/nostrill/comms.hoon @@ -83,8 +83,10 @@ =/ c2 [%give %kick paths ~] :~(c1 c2) :: for the follow flow - =/ cage [%noun !>(res)] + =/ cage [%noun !>([%init res])] =/ c1 [%give %fact paths cage] :~(c1) + + -- diff --git a/desk/lib/nostrill/follows.hoon b/desk/lib/nostrill/follows.hoon index 1cf8a66..9cda041 100644 --- a/desk/lib/nostrill/follows.hoon +++ b/desk/lib/nostrill/follows.hoon @@ -31,10 +31,7 @@ =/ c2 (urbit-leave +.user) :~(c1 c2) -++ handle-agent-res |= raw=* - ~& "handling-agent-res" - =/ =res:comms ;; res:comms raw - ~& res=-.res +++ handle-follow-res |= =res:comms ?- -.res %ng :: bruh `state @@ -47,7 +44,7 @@ ++ handle-refollow |= sip=@p :_ state :_ ~ :: (urbit-watch sip) - [%pass /refollow %agent [sip dap.bowl] %watch /followre] + [%pass /follow %agent [sip dap.bowl] %watch /follow] ++ handle-follow-ok |= [=user:sur =fc:feed profile=(unit user-meta:nsur)] ^- (quip card:agent:gall _state) diff --git a/desk/lib/nostrill/mutations.hoon b/desk/lib/nostrill/mutations.hoon deleted file mode 100644 index 8fca2b2..0000000 --- a/desk/lib/nostrill/mutations.hoon +++ /dev/null @@ -1,250 +0,0 @@ -/- sur=nostrill, nsur=nostr, - post=trill-post, gate=trill-gate, feed=trill-feed - -/+ appjs=json-nostrill, - lib=nostrill, - njs=json-nostr, - postlib=trill-post, - shim, - sr=sortug - -|_ [=state:sur =bowl:gall] -+$ card card:agent:gall -++ debug-own-feed - =/ postlist (tap:orm:feed feed.state) - =/ lol - |- ?~ postlist ~ - ~& >> poast=+.i.postlist - $(postlist t.postlist) - ~ -:: TODO not a mutation but fuck it -++ get-poast |= [host=@p id=@] ^- (unit post:post) - =/ poast ?: .=(host our.bowl) - (get:orm:feed feed.state id) - ~ - poast - -:: state -++ add-to-feed |= p=post:post - =. feed.state (put:orm:feed feed.state id.p p) - state -:: events -++ 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) - :: TODO - `state - -:: ++ parse-events -:: |= evs=(list event:nsur) -:: ^- (quip card _state) -:: =| cards=(list card) -:: =^ cards state -:: |- ?~ evs [cards state] -:: =^ cards state (handle-event i.evs) -:: $(evs t.evs) -:: [cards state] - -++ populate-profiles - |= pubkeys=(set @ux) - ^- (quip card _state) - =/ shimm ~(. shim [state bowl]) - =^ cards state (get-profiles-http:shimm pubkeys) - [cards state] - - - - - -++ handle-http - |= [sub-id=@t msgs=(list relay-msg:nsur)] - ~& handling-http=[sub-id (lent msgs)] - =| cards=(list card) - |- ?~ msgs [cards state] - =^ cards state (handle-msg i.msgs) - $(msgs t.msgs) - - -++ handle-msg |= msg=relay-msg:nsur - ^- (quip card _state) - ?+ -.msg `state - %event - (handle-event '' sub-id.msg event.msg) - %eose - `state - %closed - `state - %auth ~& >> auth=+.msg :: TODO handle auth challenges? - `state - %notice ~& >> notice=+.msg :: TODO pass to UI? - `state - %error ~& >>> relay-error=+.msg - `state - == - -++ handle-ws |= [relay=@t msg=relay-msg:nsur] - =/ rs (~(get by relays.state) relay) - ?~ rs :: TODO do we really - `state - =^ cards state - ~& handle-ws=-.msg - ?- -.msg - %ok (handle-ok relay +.msg) - %event - =. relays.state (update-relay-stats relay sub-id.msg) - (handle-event relay sub-id.msg event.msg) - - %eose - :: TODO do unsub for replaceable/addressable events - =/ creq (~(get by reqs.u.rs) +.msg) - ?~ creq `state - :: =. reqs.u.rs (~(del by reqs.u.rs) +.msg) - :: =. relays.state (~(put by relays.state) relay u.rs) - =/ cardslib ~(. cards:lib bowl) - =/ c (update-ui:cardslib [%nostr nostr-feed.state]) - :_ state :~(c) - %closed =. reqs.u.rs (~(del by reqs.u.rs) sub-id.msg) - =. relays.state (~(put by relays.state) relay u.rs) - `state - %auth ~& >> auth=+.msg :: TODO handle auth challenges? - `state - %notice ~& >> notice=+.msg :: TODO pass to UI? - `state - %error ~& >>> relay-error=+.msg - =. relays.state (~(del by relays.state) relay) - `state - == - [cards state] - - - :: =^ cards state (handle-event:mutat url.u.msg sub-id.u.msg event.u.msg) - :: :: TODO not just stash events - :: =/ relay (~(get by relays) url.u.msg) - :: =/ nevents=(list event:nsur) ?~ relay [event.u.msg ~] [event.u.msg u.relay] - :: =/ nevents2 (scag 100 nevents) - - :: =. relays (~(put by relays) url.u.msg nevents2) - :: :: TODO respond better - :: =/ response (ebail:rout id.order) - :: =/ ncards (weld cards response) - - :: [ncards this] - :: `state - -++ update-relay-stats - |= [relay=@t sub-id=@t] ^+ relays.state - - =/ cur (~(get by relays.state) relay) - =/ curr ?~ cur *relay-stats:nsur u.cur - =? connected.curr ?=(%~ connected.curr) (some now.bowl) - =/ creq (~(get by reqs.curr) sub-id) - ?~ creq relays.state :: bail - =/ nreq u.creq(received +(received.u.creq)) - =. reqs.curr (~(put by reqs.curr) sub-id nreq) - (~(put by relays.state) relay curr) - -++ handle-ok |= [relay=@t event-id=@ux accepted=? msg=@t] - ^- (quip card _state) - :: TODO pass to UI - `state - - -++ handle-event - |= [relay=@t sub-id=@t =event:nsur] - ^- (quip card _state) - |^ - ~& parsing-nostr-event=kind.event -:: https://nostrdata.github.io/kinds/ - ?: .=(kind.event 666) :: one_off subs eose cf. 999 - parse-shim-oneose - ?: .=(kind.event 0) :: user metadata - parse-metadata - ?: .=(kind.event 1) :: apparently a poast - 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 - ^- (quip card _state) - =/ jstring content.event - =/ ujon (de:json:html jstring) - ?~ ujon ~& failed-parse-metadata=ujon `state - =/ umeta (user-meta:de:njs u.ujon) - ?~ umeta ~& >> failed-dejs-metadata=ujon `state - =. profiles.state (~(put by profiles.state) [%nostr pubkey.event] u.umeta) - `state - - - ++ parse-poast - ^- (quip card _state) - =. nostr-feed.state (put:norm:sur nostr-feed.state created-at.event event) - :: =/ uprof (~(get by profiles.state) pubkey.event) - :: ?~ uprof - :: =/ shimm ~(. shim [state bowl]) - :: =^ cards state (get-profiles:shimm (silt ~[pubkey.event])) - :: [cards state] - - - :: =/ 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) - :: :: TODO wtf - :: =/ cm=content-map:post (init-content-map:postlib cl ts) - - :: =/ p=post:post :* - :: id=ts - :: host=`@p`pubkey.event - :: author=`@p`pubkey.event - :: thread=ts - :: parent=~ - :: children=~ - :: contents=cm - :: read=*lock:gate - :: write=*lock:gate - :: *engagement:post - :: 0v0 - :: *signature:post - :: tags=~ - :: == - :: =/ nfid (put:orm:feed u.fid ts p) - :: =. following.state (~(put by following.state) pubkey.event nfid) - `state - ++ parse-follow - ^- (quip card _state) - =/ following (~(get by follow-graph.state) [%nostr pubkey.event]) - =/ follow-set ?~ following *(set follow:sur) u.following - |- ?~ tags.event `state - =/ t=tag:nsur i.tags.event - :: ?. .=('p' key.t) $(tags.event t.tags.event) - :: =/ pubkeys value.t - :: =/ pubkey (slaw:sr %ux pubkeys) - :: ?~ pubkey ~& "parsing hex error" $(tags.event t.tags.event) - :: =/ relay (snag 0 rest.t) - :: =/ rel ?: .=(relay '') ~ (some relay) - :: =/ nickname (snag 1 rest.t) - :: =/ meta=follow:sur [u.pubkey nickname rel] - :: =. follow-set (~(put in follow-set) meta) - :: =. follow-graph.state (~(put by follow-graph.state) pubkey.event follow-set) - $(tags.event t.tags.event) - ++ parse-shim-oneose - ^- (quip card _state) - =/ rs (~(get by relays.state) relay) - ?~ rs `state - =. reqs.u.rs (~(del by reqs.u.rs) sub-id) - =. relays.state (~(put by relays.state) relay u.rs) - `state - -- --- diff --git a/desk/lib/scri.hoon b/desk/lib/scri.hoon index b590624..e76d79a 100644 --- a/desk/lib/scri.hoon +++ b/desk/lib/scri.hoon @@ -12,6 +12,15 @@ |_ [=state:sur =bowl:gall] +$ card card:agent:gall + +++ get-poast |= [host=@p id=@] ^- (unit post:post) + =/ poast ?: .=(host our.bowl) + (get:orm:feed feed.state id) + ~ + poast + + + ++ thread |= [hs=@t ids=@t] ^- (unit (unit cage)) :- ~ :- ~ :- %json !> %- beg-res:en:appjs diff --git a/desk/lib/shim.hoon b/desk/lib/shim.hoon index d9a5e6e..af2d142 100644 --- a/desk/lib/shim.hoon +++ b/desk/lib/shim.hoon @@ -1,5 +1,5 @@ /- sur=nostrill, nsur=nostr -/+ js=json-nostr, sr=sortug, nlib=nostr, constants +/+ js=json-nostr, sr=sortug, nostr-keys, constants /= web /web/router |_ [=state:sur =bowl:gall] @@ -21,7 +21,7 @@ ^- [bulk-req:shim:nsur _state] =/ rls ~(tap by relays.state) =| urls=(list @t) - =/ sub-id (gen-sub-id:nlib eny.bowl) + =/ sub-id (gen-sub-id:nostr-keys eny.bowl) =/ =req:shim:nsur [%req sub-id fs] |- ?~ rls [[urls req] state] :: build http card @@ -120,7 +120,7 @@ :: TODO make a function to use most reliable =/ relay (head ~(tap in relays)) ~& http=relay - =/ sub-id (gen-sub-id:nlib eny.bowl) + =/ sub-id (gen-sub-id:nostr-keys eny.bowl) =/ kinds (silt ~[0]) =/ total=filter:nsur [~ `pubkeys `kinds ~ ~ ~ ~] =/ req=http-req:shim:nsur [relay http-delay:constants sub-id ~[total]] diff --git a/desk/sur/nostrill/comms.hoon b/desk/sur/nostrill/comms.hoon index 4930235..42ea1ba 100644 --- a/desk/sur/nostrill/comms.hoon +++ b/desk/sur/nostrill/comms.hoon @@ -5,6 +5,11 @@ [%res res] [%dbug *] == ++$ emgagement + $% [%reply host=@p id=@da] + [%del-reply host=@p id=@da] + [%reaction host=@p id=@da reaction=@t] + == +$ req $% [%feed ~] [%thread id=@da] @@ -17,4 +22,20 @@ $% [%feed =fc:feed profile=(unit user-meta:nsur)] [%thread p=full-node:post] == +:: TODO there's some overlap between what we send to the UI and we send to our followers +:: but it's not exactly the same ++$ fact + $% [%post post-fact] + [%prof prof-fact] + [%init res] + == ++$ post-fact + $% [%add p=post:post] + [%del id=@da] + [%changes p=post:post] + == ++$ prof-fact + $% [%prof =user-meta:nsur] + [%keys pub=@ux] + == -- diff --git a/front/src/styles/ThemeProvider.tsx b/front/src/styles/ThemeProvider.tsx index 2cc0ca6..08d2e64 100644 --- a/front/src/styles/ThemeProvider.tsx +++ b/front/src/styles/ThemeProvider.tsx @@ -19,6 +19,8 @@ export interface ThemeColors { primary: string; primaryHover: string; secondary: string; + accent: string; + accentHover: string; background: string; surface: string; surfaceHover: string; @@ -37,11 +39,82 @@ export interface ThemeColors { overlay: string; } +export interface ThemeTypography { + fontSizeXs: string; + fontSizeSm: string; + fontSizeMd: string; + fontSizeLg: string; + fontSizeXl: string; + fontWeightNormal: string; + fontWeightMedium: string; + fontWeightSemibold: string; + fontWeightBold: string; +} + +export interface ThemeSpacing { + spacingXs: string; + spacingSm: string; + spacingMd: string; + spacingLg: string; + spacingXl: string; +} + +export interface ThemeRadius { + radiusSm: string; + radiusMd: string; + radiusLg: string; + radiusFull: string; +} + +export interface ThemeTransitions { + transitionFast: string; + transitionNormal: string; + transitionSlow: string; +} + export interface Theme { name: ThemeName; colors: ThemeColors; + typography: ThemeTypography; + spacing: ThemeSpacing; + radius: ThemeRadius; + transitions: ThemeTransitions; } +// Common theme properties +const commonTypography: ThemeTypography = { + fontSizeXs: "0.75rem", + fontSizeSm: "0.875rem", + fontSizeMd: "1rem", + fontSizeLg: "1.125rem", + fontSizeXl: "1.25rem", + fontWeightNormal: "400", + fontWeightMedium: "500", + fontWeightSemibold: "600", + fontWeightBold: "700", +}; + +const commonSpacing: ThemeSpacing = { + spacingXs: "0.25rem", + spacingSm: "0.5rem", + spacingMd: "1rem", + spacingLg: "1.5rem", + spacingXl: "2rem", +}; + +const commonRadius: ThemeRadius = { + radiusSm: "0.25rem", + radiusMd: "0.5rem", + radiusLg: "0.75rem", + radiusFull: "9999px", +}; + +const commonTransitions: ThemeTransitions = { + transitionFast: "150ms ease", + transitionNormal: "250ms ease", + transitionSlow: "350ms ease", +}; + const themes: Record = { light: { name: "light", @@ -49,6 +122,8 @@ const themes: Record = { primary: "#543fd7", primaryHover: "#4532b8", secondary: "#f39c12", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#ffffff", surface: "#f8f9fa", surfaceHover: "#e9ecef", @@ -66,6 +141,10 @@ const themes: Record = { shadow: "rgba(0, 0, 0, 0.1)", overlay: "rgba(0, 0, 0, 0.5)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, dark: { name: "dark", @@ -73,6 +152,8 @@ const themes: Record = { primary: "#7c6ef7", primaryHover: "#9085f9", secondary: "#f39c12", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#0d1117", surface: "#161b22", surfaceHover: "#21262d", @@ -90,6 +171,10 @@ const themes: Record = { shadow: "rgba(0, 0, 0, 0.3)", overlay: "rgba(0, 0, 0, 0.7)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, sepia: { name: "sepia", @@ -97,6 +182,8 @@ const themes: Record = { primary: "#8b4513", primaryHover: "#6b3410", secondary: "#d2691e", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#f4e8d0", surface: "#ede0c8", surfaceHover: "#e6d9c0", @@ -114,6 +201,10 @@ const themes: Record = { shadow: "rgba(62, 39, 35, 0.1)", overlay: "rgba(62, 39, 35, 0.5)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, noir: { name: "noir", @@ -121,6 +212,8 @@ const themes: Record = { primary: "#ffffff", primaryHover: "#e0e0e0", secondary: "#808080", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#000000", surface: "#0a0a0a", surfaceHover: "#1a1a1a", @@ -138,6 +231,10 @@ const themes: Record = { shadow: "rgba(255, 255, 255, 0.1)", overlay: "rgba(0, 0, 0, 0.9)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, ocean: { name: "ocean", @@ -145,6 +242,8 @@ const themes: Record = { primary: "#006994", primaryHover: "#005577", secondary: "#00acc1", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#e1f5fe", surface: "#b3e5fc", surfaceHover: "#81d4fa", @@ -162,6 +261,10 @@ const themes: Record = { shadow: "rgba(1, 87, 155, 0.1)", overlay: "rgba(1, 87, 155, 0.5)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, forest: { name: "forest", @@ -169,6 +272,8 @@ const themes: Record = { primary: "#2e7d32", primaryHover: "#1b5e20", secondary: "#689f38", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#f1f8e9", surface: "#dcedc8", surfaceHover: "#c5e1a5", @@ -186,6 +291,10 @@ const themes: Record = { shadow: "rgba(27, 94, 32, 0.1)", overlay: "rgba(27, 94, 32, 0.5)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, gruvbox: { name: "gruvbox", @@ -193,6 +302,8 @@ const themes: Record = { primary: "#fe8019", primaryHover: "#d65d0e", secondary: "#fabd2f", + accent: "#2a9d8f", + accentHover: "#238b7f", background: "#282828", surface: "#3c3836", surfaceHover: "#504945", @@ -210,6 +321,10 @@ const themes: Record = { shadow: "rgba(0, 0, 0, 0.3)", overlay: "rgba(40, 40, 40, 0.8)", }, + typography: commonTypography, + spacing: commonSpacing, + radius: commonRadius, + transitions: commonTransitions, }, }; @@ -254,11 +369,40 @@ export const ThemeProvider: React.FC = ({ root.setAttribute("data-theme", themeName); + // Set color variables Object.entries(theme.colors).forEach(([key, value]) => { const cssVarName = `--color-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`; root.style.setProperty(cssVarName, value); }); + // Set typography variables + Object.entries(theme.typography).forEach(([key, value]) => { + const cssVarName = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase().replace("font-", "font-").replace("size", "").replace("weight", "")}`; + root.style.setProperty(cssVarName, value); + }); + + // Set spacing variables + Object.entries(theme.spacing).forEach(([key, value]) => { + const cssVarName = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`; + root.style.setProperty(cssVarName, value); + }); + + // Set radius variables + Object.entries(theme.radius).forEach(([key, value]) => { + const cssVarName = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`; + root.style.setProperty(cssVarName, value); + }); + + // Set transition variables + Object.entries(theme.transitions).forEach(([key, value]) => { + const cssVarName = `--${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`; + root.style.setProperty(cssVarName, value); + }); + + // Legacy variables for backward compatibility + root.style.setProperty('--text-color', theme.colors.text); + root.style.setProperty('--background-color', theme.colors.background); + localStorage.setItem("theme", themeName); }, [themeName, theme]); diff --git a/front/src/styles/styles.css b/front/src/styles/styles.css index 42a2e3c..5affd4f 100644 --- a/front/src/styles/styles.css +++ b/front/src/styles/styles.css @@ -34,6 +34,21 @@ /* tailwindy */ +.global-center { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.centered { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + + .grow { flex-grow: 1; } -- cgit v1.2.3