Commit Graph

49 Commits

Author SHA1 Message Date
mroxso
4c20938098 Add notifications center with unread badge (#41)
* Add notifications center

* Fix notification event selection and account read-state sync

Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>

* Fix notification bell triggers

* Keep notification badge within menu bar

* Place notification count beside bell

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-09-06 22:32:40 +02:00
mroxso
adc65f9819 feat: Spells app for saved, shareable Nostr queries (grimoire kind 777) (#33)
* feat: Spells app for saved, shareable Nostr queries (grimoire kind 777)

The original feedback mentioned "grimoire spells" — traced to
github.com/purrgrammer/grimoire, a third-party Nostr client with its
own draft NIP for kind 777 "Spell" events: a REQ filter (kinds,
authors, one tag filter, limit, time window) encoded as portable,
shareable tags, with $me/$contacts runtime variables and relative
timestamps ("7d", "now").

- src/hooks/useSpells.ts implements that draft NIP as-is (same tags,
  same variables, same relative-time grammar) rather than a
  reinterpretation, so a spell saved here round-trips with Grimoire.
- src/apps/spells: browse "My Spells" / "Discover", build one with
  NewSpellForm, and Run it on demand against the resolved filter,
  rendering kind-1 results with NoteCard.
- Only the "Spell" half is implemented; "Spellbook" (kind 30777,
  saved window layouts) is left as a documented follow-up.

Closes #24

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: address review feedback on the Spells app

Per review:
- Scope is now derived (forced to "discover" when signed out) rather
  than stored as the requested value directly, matching the Feed
  app's pattern — signing out mid-session can no longer leave "My
  Spells" selected.
- resolveSpellFilter() now validates a spell's tag-filter letter
  (single a-zA-Z char) before using it as a "#<letter>" filter key,
  and clamps limit to [1, 500] instead of trusting a relay-sourced
  spell's number outright — a malformed or hostile spell can no
  longer produce a "#undefined" filter key or an enormous/NaN/zero
  limit. Added regression tests for all of these.
- NewSpellForm's Field now renders a real <label htmlFor> connected
  to each input's id (via useId()), and the Authors button group
  moved to a <fieldset>/<legend> instead of a label sitting over
  unrelated buttons — screen readers can now name every control.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: validate spell tag filter/limit at write time, and hide malformed badges

Per review:
- encodeSpellTags() now validates the tag filter's letter and the
  limit before writing them, instead of only resolveSpellFilter()
  catching bad values on Run — a spell authored through this app can
  no longer save a filter it will silently fail to apply later.
  Exported isValidTagLetter() so both sides share one definition of
  "valid."
- The spell detail view's tag-filter badge now hides itself for a
  malformed tag filter (e.g. from a relay-sourced spell this app
  didn't author) instead of rendering "#undefined:" or similar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: reject non-integer/negative spell kinds, de-flake a timing test

Per remaining "previously missed" findings:
- parseSpell() now requires k tags to be non-negative integers
  (Number.isInteger && >= 0), not just finite — a relay-sourced spell
  claiming kind "1.5" or "-1" no longer passes through into a
  malformed filter.
- The resolveTimestamp wall-clock tests asserted toBeCloseTo a single
  captured `now`, which a slow runner or timing skew between the two
  Date.now() calls could flake. Replaced with a before/after range
  assertion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:55:22 +02:00
mroxso
ae7d27d63e feat: Live app for NIP-53 broadcast streams and chat (#32)
Adds a Live app (kind 30311 live events + kind 1311 live chat),
scoped to broadcast streams only per the issue discussion — Spaces/
interactive rooms (kind 30312/30313) are a separate, larger effort
tracked in a follow-up issue.

- src/apps/live/index.tsx: sidebar list (live streams first, then
  planned, then ended, newest within each bucket) and a detail pane
  with title/summary/host/status/topics and a link out to the
  `streaming` (or `recording`, once ended) URL.
- src/apps/live/LiveChat.tsx: kind 1311 messages tagged to the stream
  via `a`, with a composer.
- No embedded video player: NIP-53 streams are typically HLS, which
  needs a library (hls.js) to play in-browser — deferred rather than
  pulled in for a first cut. docs/apps.md explains the tradeoff.

Closes #22


Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:52:53 +02:00
mroxso
d4a83f22b5 feat: web bookmarks for arbitrary URLs (NIP-B0) (#30)
* feat: web bookmarks for arbitrary URLs (NIP-B0)

Adds a Web Bookmarks app backed by NIP-B0 (kind 39701): one
addressable event per saved URL, distinct from the NIP-51 bookmark
list (#21) since it carries its own title/description/tags per page
rather than being an entry in a list.

- src/hooks/useWebBookmarks.ts: create/list/delete, plus
  bookmarkDTag/bookmarkUrl implementing the spec's "strip https://"
  d-tag rule (round-tripped by a unit test).
- Delete publishes a NIP-09 kind 5 request and also drops the item
  from the local query cache directly, since relays aren't obligated
  to honor the deletion.
- New src/apps/web-bookmarks/index.tsx: inline add form, list with
  title/description/tags, opens the saved URL in a new tab.

Closes #25

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: address review feedback on web bookmarks

Per review:
- bookmarkDTag() now matches the https scheme case-insensitively, so
  "HTTPS://…" and "https://…" collapse to the same d tag instead of
  creating duplicate bookmarks.
- The form now accepts every scheme sanitizeUrl() allows (https,
  http, mailto, nostr) via a dedicated isBookmarkableUrl() check —
  not sanitizeUrl() itself, which resolves relative URLs against this
  app's own origin and would have "validated" a bare hostname like
  "example.com" as a link back into the app.
- WebBookmarkRow no longer falls back to the raw unsanitized URL when
  sanitizeUrl() rejects it (e.g. a malicious "d" tag) — it renders
  plain text with no link instead of defeating the sanitization.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: round-trip mailto:/nostr: bookmarks and preserve published_at

Per review:
- bookmarkUrl() required "scheme://" to recognize an already-schemed
  d tag, so opaque URIs with no "//" — mailto: and nostr: — were
  incorrectly prefixed with "https://". Fixed by also checking a
  closed list of the opaque schemes this app supports, alongside the
  existing "://" check (kept as-is so a hierarchical scheme like
  gemini:// still round-trips, and so a stripped https URL containing
  a port, e.g. alice.blog:8080/post, still isn't misread as scheme
  "alice.blog"). Added regression tests for all three cases.
- useCreateWebBookmark now looks up the existing bookmark for the
  same d tag before publishing and carries its published_at forward,
  instead of resetting it to now on every edit — per NIP-B0,
  published_at is "the first time the bookmark was published."

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: dedupe multiple revisions of the same web bookmark

Per an earlier "previously missed" finding: useMyWebBookmarks()
returned every kind-39701 event a relay handed back, but for an
addressable event the pool can return more than one revision of the
same d tag (an edit history, or relays disagreeing on what's
current), which showed up as duplicate rows for the same URL.
Extracted dedupeLatestByDTag() (keeps the newest per d, newest-first)
and covered it with regression tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:50:15 +02:00
mroxso
50795167f5 feat: highlight text in the Reader (NIP-84) (#31)
* feat: highlight text in the Reader (NIP-84)

Adds NIP-84 highlights (kind 9802) to the article reader:
- Selecting text in an article shows a floating "Highlight" button
  (src/apps/articles/HighlightLayer.tsx), publishing the selected
  plain text tagged to the article (`a`) and its author (`p`, role
  "author").
- Existing highlights for the article are listed underneath it, with
  the highlighter's identity and timestamp.

Closes #23

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: harden selection handling and clamp the highlight button

Per review:
- Guard sel.rangeCount === 0 before calling getRangeAt(0), which
  throws otherwise.
- Scope containment by the range's commonAncestorContainer instead of
  just anchorNode, so a selection that starts inside the article but
  is dragged out past its boundary is correctly rejected.
- Clamp the floating button's top so a selection near the top of the
  viewport doesn't push it off-screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: don't highlight against a malformed address, gate the listener

Per a "needs a closer look" review pass:
- articles/index.tsx now passes an empty string, not a malformed
  "kind:pubkey:" address, when an article has no d tag. HighlightLayer
  treats a falsy address as "highlighting isn't available here."
- The selectionchange listener is only registered when both user and
  address are present (in the effect's deps), instead of always
  running selection tracking regardless of whether a highlight could
  ever be published.
- handleHighlight and the floating button both guard on address too,
  not just selection, so stale selection state from before a prop
  change went missing can't still trigger a publish.
- docs/apps.md corrected: the saved text comes from Selection.toString()
  (window.getSelection()), not Range.toString().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:47:47 +02:00
mroxso
179d3682f8 feat: bookmarks for notes and articles (NIP-51) (#29)
* feat: bookmarks for notes and articles (NIP-51)

Adds a Bookmarks app backed by a kind 10003 NIP-51 bookmark list:
- BookmarkButton toggles a note (`e` tag) or article (`a` tag) in and
  out of the signed-in user's list, reading it back before publishing
  so an update never clobbers other entries — the same whole-list
  replacement trap follow lists have.
- Wired into NoteCard's action row and the Reader's article toolbar.
- The new Bookmarks app lists saved notes and articles, opening
  articles back in the Reader.

Closes #21

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: address review feedback and add a Bookmarked tab to the Reader

Per review:
- useToggleBookmark now fetches the bookmark list fresh from relays
  right before writing instead of trusting the query cache (60s
  staleTime), which could otherwise clobber concurrent edits from
  another tab or device.
- The article BookmarkButton only renders when the article actually
  has a `d` tag, instead of falling back to an unresolvable
  "kind:pubkey:" address.
- Bookmarked note/article ids are filtered for a non-empty tag value
  before use, and article addresses are parsed properly (kind,
  author, `d`) instead of a naive split(':')[2] — the relay query is
  now also constrained by kind and author, not just `d`, and
  identifiers containing ':' round-trip correctly.
- BookmarkButton sets type="button" so it can't misbehave as a form
  submit button.

Per a reviewer comment: added a "Recent" / "Bookmarked" tab to the
Reader's sidebar (src/apps/articles/index.tsx) so bookmarked articles
are reachable without leaving the app — the dedicated Bookmarks app
stays as-is. Both now share useMyBookmarkedArticles from
src/hooks/useBookmarks.ts rather than duplicating the address-parsing
logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: reject empty-identifier addresses and surface bookmark load errors

Per review:
- parseAddress() now rejects an empty d-identifier as malformed
  (e.g. "30023:<pubkey>:") instead of producing a "#d: ['']" relay
  query and an unopenable bookmark.
- useMyBookmarkedArticles() filters out matched events with empty
  content, the same non-renderable criteria the Reader's own list
  uses, so a broken/blank article can't land in the Bookmarked view.
- BookmarksApp now distinguishes "the query failed" from "there are
  no bookmarks" — React Query leaves data undefined in both cases, so
  a relay/network failure no longer reads as an empty list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:45:21 +02:00
mroxso
e7cf9c4677 feat: local draft notes with a blank "new note" entry point (#28)
* feat: local draft notes with a blank "new note" entry point

Writing was tied to publishing: the Feed composer either sits empty
or fires a note straight to relays, with nowhere to keep something
you're not ready to publish yet.

The Note app now supports a draft mode when opened without an id: a
blank note kept in localStorage until you publish it or discard it,
reachable via a new "New note" button in the Feed toolbar, the Go
menu, or the command palette (all already open the Note app with no
params).

Closes #19

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: guard against double-publish and dropped relay params

Per review:
- handlePublish now also checks publish.isPending itself, not just
  the button's disabled state — a second click landing before React
  re-renders could otherwise fire mutateAsync twice.
- Publishing a draft now merges into the existing params instead of
  replacing them outright, so relay hints (or anything else already
  in params) survive the id being added.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* docs: mark notes app's id param as optional

Per review — the draft mode added by this PR means id is no longer
required to open the Note app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: sync useLocalStorage across same-tab consumers of one key

Per review: the Notes app is explicitly non-singleton, so opening two
"New Note" windows meant two DraftNote instances writing the same
localStorage key independently — the native `storage` event only
fires in *other* tabs/documents, never the one that wrote, so the two
windows would silently diverge (discard/publish in one wouldn't
update the other).

useLocalStorage now also dispatches a same-document custom event on
every write, and every instance sharing that key listens for it —
verified live with two open draft windows staying in sync as one is
typed into.

Also dropped a redundant `{}` params argument on an openApp() call
that every other call site omits when opening with no parameters.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-09-06 18:40:40 +02:00
mroxso
c180e0eb51 fix: exclude replies from the Feed and Profile timelines (#27)
* fix: exclude replies from the Feed and Profile timelines

Per NIP-10 a kind-1 event with an `e` tag is a reply, but the Feed and
Profile timelines rendered every kind-1 event with no such check, so
replies showed up indistinguishable from root posts. `isReply()` was
already written for this in nostrUtils but never used anywhere.

Fixes #20

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: don't treat mention-only e tags as replies

Per review: isReply() flagged any e tag as a reply, including one
marked "mention" — a citation, not a thread reply per NIP-10. That
would have hidden quote-notes from the Feed/Profile timelines they
belong in. Also fixed rootReference()'s docstring, which claimed to
fall back to the *last* positional e tag when the code (correctly)
uses the first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

* fix: rootReference() no longer treats a mention as the root

Follow-up to the isReply() fix: rootReference()'s positional fallback
still matched any e tag regardless of marker, so an event with only a
mention-marked e tag would incorrectly return the mentioned id as the
thread root. The fallback now only considers unmarked e tags, per the
deprecated positional NIP-10 scheme.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BYiUtZMQeA5RHggQw73wto

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 18:37:13 +02:00
mroxso
809077d054 fix: open the picked app from the mobile app switcher (#18)
The switcher only focused the window it was given. `FOCUS_WINDOW` raises
and focuses but never un-minimizes, while the mobile shell renders a
window only when it is focused *and* not minimized — so picking a
minimized app updated the URL and the document title (both follow
`focusedId`) while the home screen stayed on screen.

Restore a minimized window instead of merely focusing it, the same way the
menu bar's Window menu and the command palette already do.

Fixes #15


Claude-Session: https://claude.ai/code/session_01Trku191Ww2a2YmDWQF3fTS

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 12:09:21 +02:00
mroxso
dc1701afd9 fix: stop the render loop that crashes the mobile app switcher (#17)
`mapWindow` flagged the state as changed whenever the target window id
matched, even when the updater handed the very same window object back.
`SET_TITLE` is guarded against a no-op title, but that guard was defeated:
the reducer still returned a fresh state object, so the context value
changed, every consumer re-rendered, and each app's `setTitle` effect —
keyed on a `setTitle` callback that the mobile shell re-created on every
render — dispatched `SET_TITLE` again. That loop ran continuously while an
app was open on mobile; opening the app-switcher Sheet on top of it made
Radix re-attach its composed refs on every one of those renders until
React bailed out with "Maximum update depth exceeded" and the
ErrorBoundary took over.

Only count a window as changed when the updater actually returned a
different object, and memoise the mobile shell's `setTitle`/`setParams`
per window, matching what `WindowFrame` already does.

Fixes #14


Claude-Session: https://claude.ai/code/session_01Trku191Ww2a2YmDWQF3fTS

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 12:09:11 +02:00
mroxso
cde5be0874 fix: keep a window in place when focus switches (#16)
Raising a window made it visibly jump to the top-left corner and animate
back. Two things combined: the enter keyframes animated `transform`, which
overrode the inline `translate3d` a window's position is applied with, and
the window layer rendered the z-sorted list, so focusing a window moved its
DOM node and made the browser replay that animation.

Animate the standalone `scale` property instead — it composes with the
inline transform rather than replacing it — scope the animation to
`.os-window` (it was hitting every child of the layer, including the snap
preview, which is positioned the same way), and render windows in their
stable creation order, since the inline `z-index` already handles stacking.

Fixes #13


Claude-Session: https://claude.ai/code/session_01Trku191Ww2a2YmDWQF3fTS

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 12:09:00 +02:00
mroxso
949a551492 feat: New Web OS design (#12)
* new web os frontend

* add docs

* Add CNAME and restore NIP-05 nostr.json for GitHub Pages

The Pages custom domain (layer.systems) is only stored in repo settings;
a CNAME file in the build output makes it survive Pages reconfiguration.
Restore public/.well-known/nostr.json, which this branch had dropped —
removing it would break the existing NIP-05 identifiers on layer.systems.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YtQoCzkP7Bo8nruhxojPi

* Ignore eslint and tsc build caches

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YtQoCzkP7Bo8nruhxojPi

* Rebrand page title and metadata to LAYER.systems

The site ships on layer.systems, so the document title, meta and OG
description, and the web manifest now carry that name instead of
"Nostr OS". OsShell sets the title at runtime, so it is updated too —
otherwise the tab would fall back to the old branding after hydration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YtQoCzkP7Bo8nruhxojPi

* Rename remaining visible "Nostr OS" strings to LAYER.systems

Covers the About window heading, the mobile shell header and the app
icon's aria-label, so the visible branding matches the page title.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YtQoCzkP7Bo8nruhxojPi

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 11:37:43 +02:00
4acdb31b8a agents frontend-design skill 2026-03-28 21:23:52 +01:00
0492427582 fix: add DashboardExport page and integrate into routing and sidebar navigation 2026-01-04 20:40:46 +01:00
038a629a47 fix: remove limit on user events query for comprehensive data retrieval 2026-01-01 21:32:39 +01:00
d464e8a44c fix: enhance layout and overflow handling in EventExplorer and DashboardEvents components 2026-01-01 21:00:37 +01:00
8f75803797 fix: add overflow handling to charts and dashboard components for better layout 2026-01-01 21:00:30 +01:00
38541d1635 fix: update feature icons and descriptions for clarity on Index page 2026-01-01 20:28:50 +01:00
0a0c4adb09 fix: update terminology for clarity in DashboardStats and RecentActivityChart components 2025-12-28 22:32:52 +01:00
2d2fa6ada9 feat: display current user's name on Dashboard welcome message 2025-12-28 22:30:53 +01:00
e7e0215da4 fix: update main heading gradient colors for consistency 2025-12-28 22:25:33 +01:00
af94fa7a3b fix: update chart color variables to use HSL format for consistency 2025-12-28 22:19:52 +01:00
7c8d2cff31 fix: update color variables for consistency in RecentActivityChart and CSS 2025-12-28 22:15:40 +01:00
91d51a35f9 feat: add RecentActivityChart component to Dashboard 2025-12-28 22:13:13 +01:00
686c31aca8 feat: add DashboardEvents page and integrate EventExplorer component 2025-12-28 21:48:13 +01:00
2d78a683da feat: add chart color variables to light and dark themes in CSS 2025-12-28 15:12:46 +01:00
42e234105b fix: remove modal prop from DropdownMenu in AccountSwitcher component 2025-12-28 15:10:30 +01:00
3ca5966d03 feat: add theme selection toggle to AccountSwitcher component 2025-12-28 15:04:30 +01:00
0f145d556d fix: update default theme in App configuration from light to system 2025-12-28 14:58:06 +01:00
462741c3bd feat: add dashboard navigation to AccountSwitcher component 2025-12-28 14:43:12 +01:00
1972c1e27c refactor: comment out unused imports and effect in NostrSync component 2025-12-28 14:40:15 +01:00
da73c6091b feat: add relay management drawer to AccountSwitcher component 2025-12-28 14:34:01 +01:00
mroxso
8ce5c23fff Merge pull request #3 from layer-systems/dashboard-2
Dashboard
2025-12-28 14:25:49 +01:00
00b6e7247d fix: update default theme in App configuration from system to light 2025-12-28 14:25:03 +01:00
ecf597d072 feat: refactor EventKindsChart to use current user context and update chart colors 2025-12-28 14:24:40 +01:00
098626fbea feat: add Dashboard page with user statistics and recent activity components 2025-12-28 13:54:11 +01:00
007725dd7a chore: update Radix UI dependencies and add mobile hook
- Updated @radix-ui/react-dialog from ^1.1.2 to ^1.1.15
- Updated @radix-ui/react-separator from ^1.1.0 to ^1.1.8
- Updated @radix-ui/react-slot from ^1.1.0 to ^1.2.4
- Updated @radix-ui/react-tooltip from ^1.1.4 to ^1.2.8
- Added a new hook `useIsMobile` to determine if the viewport is mobile-sized
- Refactored tailwind.config.ts for better readability and maintainability
2025-12-28 12:48:21 +01:00
c4f6474fe7 Add LoginArea component to header for user authentication 2025-12-28 12:24:13 +01:00
25d02b1f9b Add highperfocused name entry to nostr.json 2025-12-28 00:30:45 +01:00
3707c1b89c Add nostr.json file with initial names configuration 2025-12-28 00:30:05 +01:00
58f33f04fd Refactor layout of Relay URL section for improved responsiveness 2025-12-28 00:20:55 +01:00
eeaee30071 Add Terms and Privacy pages and update routing in AppRouter 2025-12-27 23:36:41 +01:00
a1608d19a6 Add Explore page and implement useExploreEvents hook for fetching notes and profiles 2025-12-27 23:18:51 +01:00
19b0491714 Update theme configuration to 'system' and streamline relay URLs in App component 2025-12-27 23:05:18 +01:00
8d84d72ad6 Remove mode parameter from Vite config export function 2025-12-27 22:49:14 +01:00
f6efa465bc Comment out base URL configuration in Vite setup 2025-12-27 22:47:36 +01:00
2d7e72d244 Refactor Index page layout and enhance user interaction with relay URL copy functionality 2025-12-27 22:07:22 +01:00
fd9b6b183f Add SEO metadata and manifest file; clean up placeholder comments 2025-12-27 21:47:41 +01:00
a833997770 initial code commit 2025-12-27 21:42:31 +01:00