feat: add a new logo
13
README.md
@@ -1,6 +1,15 @@
|
|||||||
# jumble
|
<div align="center">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="./resources/logo-dark.svg">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="./resources/logo-light.svg">
|
||||||
|
<img src="./resources/logo-light.svg" alt="Jumble Logo" width="400" />
|
||||||
|
</picture>
|
||||||
|
<p>logo designed by <a href="http://wolfertdan.com/">Daniel David</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
Yet another Nostr client
|
# Jumble
|
||||||
|
|
||||||
|
A beautiful nostr client focused on browsing relay feeds
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|||||||
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 13 KiB |
BIN
build/icon.png
|
Before Width: | Height: | Size: 816 KiB After Width: | Height: | Size: 23 KiB |
BIN
build/icon@2x.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 816 KiB After Width: | Height: | Size: 23 KiB |
1
resources/icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1080 1228" version="1.1" fill="currentColor" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path id="path1" d="M360.047,1225.75c-31.046,-3.901 -75.11,-14.46 -106.756,-25.58c-101.676,-35.727 -175.164,-93.066 -215.387,-168.055c-12.079,-22.521 -30.071,-71.422 -27.297,-74.195c0.736,-0.736 11.648,5.578 24.249,14.031c135.436,90.86 301.047,169.043 465.056,219.547l32.77,10.091l-20.27,7.416c-43.455,15.896 -105.159,22.678 -152.365,16.745Zm166.293,-59.234c-168.523,-50.004 -331.475,-126.514 -481.755,-226.196c-37.737,-25.031 -41.489,-28.372 -43.419,-38.663c-3.585,-19.109 1.498,-83.894 9.798,-124.886c7.343,-36.266 27.664,-106.034 32.278,-110.818c2.023,-2.099 217.924,48.207 221.274,51.557c0.975,0.975 -1.132,11.339 -4.682,23.032c-24.542,80.842 -27.217,127.586 -9.935,173.593c22.507,59.917 114.521,99.888 177.281,77.012c29.23,-10.654 56.593,-41.085 82.629,-91.894c29.288,-57.155 32.348,-64.988 196.483,-503.076c81.138,-216.562 148.499,-394.821 149.692,-396.131c2.1,-2.304 217.949,76.926 223.076,81.884c2.056,1.988 -262.476,712.505 -307.806,826.747c-18.422,46.426 -56.939,123.045 -77.918,154.993c-10.157,15.469 -30.753,40.901 -45.769,56.515c-27.821,28.93 -66.46,58.952 -75.447,58.621c-2.738,-0.106 -23.339,-5.631 -45.78,-12.29Z" style="fill-rule:nonzero;"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
1
resources/logo-dark.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
1
resources/logo-light.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
resources/og-image.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
@@ -3,14 +3,21 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Jumble</title>
|
<link rel="icon" href="/src/assets/favicon-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml" />
|
||||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
<link rel="icon" href="/src/assets/favicon-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml" />
|
||||||
<!-- <meta
|
|
||||||
http-equiv="Content-Security-Policy"
|
<meta property="og:url" content="https://jumble.social" />
|
||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
|
<meta property="og:type" content="website" />
|
||||||
/> -->
|
<meta property="og:title" content="Jumble" />
|
||||||
|
<meta property="og:description" content="A beautiful nostr client focused on browsing relay feeds" />
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
<title>Jumble</title>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import {
|
|||||||
} from '@renderer/components/ui/resizable'
|
} from '@renderer/components/ui/resizable'
|
||||||
import { cn } from '@renderer/lib/utils'
|
import { cn } from '@renderer/lib/utils'
|
||||||
import HomePage from '@renderer/pages/secondary/HomePage'
|
import HomePage from '@renderer/pages/secondary/HomePage'
|
||||||
import NotFoundPage from '@renderer/pages/secondary/NotFoundPage'
|
|
||||||
import { cloneElement, createContext, useContext, useEffect, useState } from 'react'
|
import { cloneElement, createContext, useContext, useEffect, useState } from 'react'
|
||||||
import { routes } from './routes'
|
|
||||||
import { useScreenSize } from './providers/ScreenSizeProvider'
|
import { useScreenSize } from './providers/ScreenSizeProvider'
|
||||||
|
import { routes } from './routes'
|
||||||
|
|
||||||
type TPrimaryPageContext = {
|
type TPrimaryPageContext = {
|
||||||
refresh: () => void
|
refresh: () => void
|
||||||
@@ -210,14 +209,16 @@ function findAndCreateComponent(url: string) {
|
|||||||
const match = matcher(path)
|
const match = matcher(path)
|
||||||
if (!match) continue
|
if (!match) continue
|
||||||
|
|
||||||
if (!element) return <NotFoundPage />
|
if (!element) return null
|
||||||
return cloneElement(element, match.params)
|
return cloneElement(element, match.params)
|
||||||
}
|
}
|
||||||
return <NotFoundPage />
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushNewPageToStack(stack: TStackItem[], url: string, maxStackSize = 5) {
|
function pushNewPageToStack(stack: TStackItem[], url: string, maxStackSize = 5) {
|
||||||
const component = findAndCreateComponent(url)
|
const component = findAndCreateComponent(url)
|
||||||
|
if (!component) return { newStack: stack, newItem: null }
|
||||||
|
|
||||||
const currentStack = stack[stack.length - 1]
|
const currentStack = stack[stack.length - 1]
|
||||||
const newItem = { component, url, index: currentStack ? currentStack.index + 1 : 0 }
|
const newItem = { component, url, index: currentStack ? currentStack.index + 1 : 0 }
|
||||||
const newStack = [...stack, newItem]
|
const newStack = [...stack, newItem]
|
||||||
|
|||||||
26
src/renderer/src/assets/Logo.tsx
Normal file
1
src/renderer/src/assets/favicon-dark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1080 1228" version="1.1" fill="#ffffff" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path id="path1" d="M360.047,1225.75c-31.046,-3.901 -75.11,-14.46 -106.756,-25.58c-101.676,-35.727 -175.164,-93.066 -215.387,-168.055c-12.079,-22.521 -30.071,-71.422 -27.297,-74.195c0.736,-0.736 11.648,5.578 24.249,14.031c135.436,90.86 301.047,169.043 465.056,219.547l32.77,10.091l-20.27,7.416c-43.455,15.896 -105.159,22.678 -152.365,16.745Zm166.293,-59.234c-168.523,-50.004 -331.475,-126.514 -481.755,-226.196c-37.737,-25.031 -41.489,-28.372 -43.419,-38.663c-3.585,-19.109 1.498,-83.894 9.798,-124.886c7.343,-36.266 27.664,-106.034 32.278,-110.818c2.023,-2.099 217.924,48.207 221.274,51.557c0.975,0.975 -1.132,11.339 -4.682,23.032c-24.542,80.842 -27.217,127.586 -9.935,173.593c22.507,59.917 114.521,99.888 177.281,77.012c29.23,-10.654 56.593,-41.085 82.629,-91.894c29.288,-57.155 32.348,-64.988 196.483,-503.076c81.138,-216.562 148.499,-394.821 149.692,-396.131c2.1,-2.304 217.949,76.926 223.076,81.884c2.056,1.988 -262.476,712.505 -307.806,826.747c-18.422,46.426 -56.939,123.045 -77.918,154.993c-10.157,15.469 -30.753,40.901 -45.769,56.515c-27.821,28.93 -66.46,58.952 -75.447,58.621c-2.738,-0.106 -23.339,-5.631 -45.78,-12.29Z" style="fill-rule:nonzero;"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
1
src/renderer/src/assets/favicon-light.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 1080 1228" version="1.1" fill="#000000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path id="path1" d="M360.047,1225.75c-31.046,-3.901 -75.11,-14.46 -106.756,-25.58c-101.676,-35.727 -175.164,-93.066 -215.387,-168.055c-12.079,-22.521 -30.071,-71.422 -27.297,-74.195c0.736,-0.736 11.648,5.578 24.249,14.031c135.436,90.86 301.047,169.043 465.056,219.547l32.77,10.091l-20.27,7.416c-43.455,15.896 -105.159,22.678 -152.365,16.745Zm166.293,-59.234c-168.523,-50.004 -331.475,-126.514 -481.755,-226.196c-37.737,-25.031 -41.489,-28.372 -43.419,-38.663c-3.585,-19.109 1.498,-83.894 9.798,-124.886c7.343,-36.266 27.664,-106.034 32.278,-110.818c2.023,-2.099 217.924,48.207 221.274,51.557c0.975,0.975 -1.132,11.339 -4.682,23.032c-24.542,80.842 -27.217,127.586 -9.935,173.593c22.507,59.917 114.521,99.888 177.281,77.012c29.23,-10.654 56.593,-41.085 82.629,-91.894c29.288,-57.155 32.348,-64.988 196.483,-503.076c81.138,-216.562 148.499,-394.821 149.692,-396.131c2.1,-2.304 217.949,76.926 223.076,81.884c2.056,1.988 -262.476,712.505 -307.806,826.747c-18.422,46.426 -56.939,123.045 -77.918,154.993c-10.157,15.469 -30.753,40.901 -45.769,56.515c-27.821,28.93 -66.46,58.952 -75.447,58.621c-2.738,-0.106 -23.339,-5.631 -45.78,-12.29Z" style="fill-rule:nonzero;"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,3 +1,4 @@
|
|||||||
|
import Logo from '@renderer/assets/Logo'
|
||||||
import { Button } from '@renderer/components/ui/button'
|
import { Button } from '@renderer/components/ui/button'
|
||||||
import { IS_ELECTRON } from '@renderer/lib/env'
|
import { IS_ELECTRON } from '@renderer/lib/env'
|
||||||
import { toHome } from '@renderer/lib/link'
|
import { toHome } from '@renderer/lib/link'
|
||||||
@@ -14,10 +15,12 @@ import SearchButton from '../SearchButton'
|
|||||||
export default function PrimaryPageSidebar() {
|
export default function PrimaryPageSidebar() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<div className="draggable w-52 h-full shrink-0 hidden xl:flex flex-col pb-8 pt-9 pl-4 justify-between">
|
<div className="draggable w-52 h-full shrink-0 hidden xl:flex flex-col pb-8 pt-10 pl-4 justify-between">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="text-3xl font-extrabold font-mono text-center mb-4">
|
<div className="ml-4 mb-8 w-40">
|
||||||
<SecondaryPageLink to={toHome()}>Jumble</SecondaryPageLink>
|
<SecondaryPageLink to={toHome()}>
|
||||||
|
<Logo />
|
||||||
|
</SecondaryPageLink>
|
||||||
</div>
|
</div>
|
||||||
<PostButton variant="sidebar" />
|
<PostButton variant="sidebar" />
|
||||||
<RelaySettingsButton variant="sidebar" />
|
<RelaySettingsButton variant="sidebar" />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Logo from '@renderer/assets/Logo'
|
||||||
import AccountButton from '@renderer/components/AccountButton'
|
import AccountButton from '@renderer/components/AccountButton'
|
||||||
import PostButton from '@renderer/components/PostButton'
|
import PostButton from '@renderer/components/PostButton'
|
||||||
import RefreshButton from '@renderer/components/RefreshButton'
|
import RefreshButton from '@renderer/components/RefreshButton'
|
||||||
@@ -79,7 +80,9 @@ function PrimaryPageTitlebar({ visible = true }: { visible?: boolean }) {
|
|||||||
visible={visible}
|
visible={visible}
|
||||||
>
|
>
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex gap-1 items-center">
|
||||||
<div className="text-2xl font-extrabold font-mono">Jumble</div>
|
<div className="-translate-y-0.5">
|
||||||
|
<Logo className="h-8" />
|
||||||
|
</div>
|
||||||
<ThemeToggle variant="small-screen-titlebar" />
|
<ThemeToggle variant="small-screen-titlebar" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex gap-1 items-center">
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
export default function SecondaryPageLayout({
|
export default function SecondaryPageLayout({
|
||||||
children,
|
children,
|
||||||
titlebarContent,
|
titlebarContent,
|
||||||
hideBackButton = false
|
hideBackButton = false,
|
||||||
|
hideScrollToTopButton = false
|
||||||
}: {
|
}: {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
titlebarContent?: React.ReactNode
|
titlebarContent?: React.ReactNode
|
||||||
hideBackButton?: boolean
|
hideBackButton?: boolean
|
||||||
|
hideScrollToTopButton?: boolean
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const scrollAreaRef = useRef<HTMLDivElement>(null)
|
const scrollAreaRef = useRef<HTMLDivElement>(null)
|
||||||
const [visible, setVisible] = useState(true)
|
const [visible, setVisible] = useState(true)
|
||||||
@@ -50,7 +52,10 @@ export default function SecondaryPageLayout({
|
|||||||
<div className={cn('sm:px-4 pb-4 pt-11 w-full h-full', isMacOS() ? 'max-sm:pt-20' : '')}>
|
<div className={cn('sm:px-4 pb-4 pt-11 w-full h-full', isMacOS() ? 'max-sm:pt-20' : '')}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<ScrollToTopButton scrollAreaRef={scrollAreaRef} visible={visible} />
|
<ScrollToTopButton
|
||||||
|
scrollAreaRef={scrollAreaRef}
|
||||||
|
visible={!hideScrollToTopButton && visible}
|
||||||
|
/>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<SecondaryPageLayout hideBackButton>
|
<SecondaryPageLayout hideBackButton hideScrollToTopButton>
|
||||||
<div className="text-muted-foreground w-full h-full flex items-center justify-center">
|
<div className="text-muted-foreground w-full h-full flex items-center justify-center">
|
||||||
{t('Welcome! 🥳')}
|
{t('Welcome! 🥳')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||