Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 16 additions & 67 deletions CLAUDE.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "cipher-note",
"private": true,
"version": "0.0.0",
"version": "1.0.0",
"packageManager": "pnpm@9.6.0",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/app/layouts/ProtectedLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function AuthenticatedLayout() {
useNavigationBlocker()

return (
<div className="text-foreground bg-background flex h-[calc(100vh-var(--banner-height))]">
<div className="text-foreground bg-background flex h-dvh">
{/* Desktop sidebar */}
<aside
className="bg-sidebar text-sidebar-foreground border-sidebar-border hidden shrink-0 flex-col border-r md:flex"
Expand Down
2 changes: 1 addition & 1 deletion src/app/layouts/PublicLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PublicHeader } from '@/shared/ui/nav/PublicHeader'

function PublicLayout() {
return (
<div className="bg-background text-foreground relative flex min-h-[calc(100vh-var(--banner-height))] flex-col overflow-hidden">
<div className="bg-background text-foreground relative flex min-h-dvh flex-col overflow-hidden">
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_top,var(--color-primary)_0,transparent_70%)] opacity-[0.08]" />
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_bottom,var(--color-primary)_0,transparent_50%)] opacity-[0.03]" />
<PublicHeader />
Expand Down
8 changes: 4 additions & 4 deletions src/app/layouts/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function Sidebar({ onClose, onLogout, className }: SidebarProps) {
return (
<div className={cn('flex h-full flex-col', className)}>
{/* Header */}
<div className="flex items-center justify-between p-4">
<div className="border-sidebar-border flex min-h-14 items-center justify-between border-b px-4">
<AppLogo />
{onClose && (
<Button
Expand All @@ -58,8 +58,6 @@ function Sidebar({ onClose, onLogout, className }: SidebarProps) {
)}
</div>

<Separator />

{/* Entry list */}
<nav aria-label={t('common:nav.mainNav')} className="flex-1 overflow-y-auto p-2">
<div className="flex items-center justify-between px-1 pb-1">
Expand Down Expand Up @@ -100,7 +98,9 @@ function Sidebar({ onClose, onLogout, className }: SidebarProps) {
{user && (
<div className="text-muted-foreground flex min-w-0 flex-1 items-center gap-2 text-sm">
<User className="size-4" />
<span className="min-w-0 truncate">{user.username}</span>
<span className="min-w-0 truncate" title={user.username}>
{user.username}
</span>
</div>
)}

Expand Down
2 changes: 0 additions & 2 deletions src/app/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Toaster } from '@/shared/ui/sonner'
import { PageSkeleton } from '@/app/Pending'
import { RouteErrorBoundary } from '@/app/RouteErrorBoundary'
import { NotFoundPage } from '@/app/NotFoundPage'
import { PreAlphaBanner } from '@/shared/ui/PreAlphaBanner'
import type { AuthContext } from '@/shared/auth/auth-context'

interface RouterContext {
Expand All @@ -21,7 +20,6 @@ export const Route = createRootRouteWithContext<RouterContext>()({
function RootLayout() {
return (
<ThemeProvider defaultTheme="dark">
<PreAlphaBanner />
<Outlet />
<Toaster />
</ThemeProvider>
Expand Down
1 change: 0 additions & 1 deletion src/app/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@

:root {
color-scheme: light;
--banner-height: 32px;
--background: oklch(0.98 0.004 285);
--foreground: oklch(0.18 0.015 285);
--card: oklch(1 0 0);
Expand Down
6 changes: 3 additions & 3 deletions src/features/landing/ui/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ function LandingPage() {
const { t } = useTranslation('landing')

return (
<div className="bg-background text-foreground relative min-h-[calc(100vh-var(--banner-height))]">
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_top,_var(--color-primary)_0,_transparent_70%)] opacity-[0.06]" />
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_right,_var(--color-primary)_0,_transparent_50%)] opacity-[0.03]" />
<div className="bg-background text-foreground relative min-h-dvh">
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_top,var(--color-primary)_0,transparent_70%)] opacity-[0.06]" />
<div className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_bottom_right,var(--color-primary)_0,transparent_50%)] opacity-[0.03]" />

<PublicHeader />

Expand Down
55 changes: 55 additions & 0 deletions src/features/settings/ui/AboutSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useTranslation } from 'react-i18next'
import { Info, Scale } from 'lucide-react'

import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/shared/ui/card'
import { Separator } from '@/shared/ui/separator'
import { CipherNoteIcon } from '@/shared/ui/brand/CipherNoteIcon'
import { GithubIcon } from '@/shared/ui/brand/GithubIcon'

function AboutSection() {
const { t } = useTranslation(['settings', 'common'])

return (
<Card>
<CardHeader className="items-center text-center">
<CipherNoteIcon className="size-10" />
<CardTitle>{t('settings:about.title')}</CardTitle>
<CardDescription>{t('settings:about.description')}</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-0">
<div className="flex items-center justify-between py-2">
<span className="flex items-center gap-3 text-sm">
<Info className="size-4" />
{t('settings:about.version')}
</span>
<span className="text-sm font-medium">{t('common:app.version')}</span>
</div>
<Separator />
<div className="flex items-center justify-between py-2">
<span className="flex items-center gap-3 text-sm">
<Scale className="size-4" />
{t('settings:about.license')}
</span>
<span className="text-sm font-medium">{t('common:app.license')}</span>
</div>
<Separator />
<div className="flex items-center justify-between py-2">
<span className="flex items-center gap-3 text-sm">
<GithubIcon className="size-4" />
{t('settings:about.sourceCode')}
</span>
<a
href={t('common:app.githubUrl')}
target="_blank"
rel="noopener noreferrer"
className="text-primary text-sm font-medium hover:underline"
>
GitHub
</a>
</div>
</CardContent>
</Card>
)
}

export { AboutSection }
7 changes: 4 additions & 3 deletions src/features/settings/ui/SettingsPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ describe('SettingsPage', () => {
expect(screen.getByText('Settings')).toBeInTheDocument()
})

it('renders all three section titles', () => {
it('renders all section titles', () => {
render(<SettingsPage />)
expect(screen.getByText('Account')).toBeInTheDocument()
expect(screen.getByText('Preferences')).toBeInTheDocument()
expect(screen.getByText('Security')).toBeInTheDocument()
expect(screen.getByText('About')).toBeInTheDocument()
})

it('renders sections in Account → Preferences → Security order', () => {
it('renders sections in Account → Preferences → Security → About order', () => {
const { container } = render(<SettingsPage />)
const sectionTitles = Array.from(container.querySelectorAll('[data-slot="card-title"]'))
const titleTexts = sectionTitles.map((el) => el.textContent)
expect(titleTexts).toEqual(['Account', 'Preferences', 'Security'])
expect(titleTexts).toEqual(['Account', 'Preferences', 'Security', 'About'])
})

it('renders username from auth store in account section', () => {
Expand Down
2 changes: 2 additions & 0 deletions src/features/settings/ui/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
import { AccountSection } from '@/features/settings/ui/AccountSection'
import { PreferencesSection } from '@/features/settings/ui/PreferencesSection'
import { SecuritySection } from '@/features/settings/ui/SecuritySection'
import { AboutSection } from '@/features/settings/ui/AboutSection'

function SettingsPage() {
const { t } = useTranslation('settings')
Expand All @@ -14,6 +15,7 @@ function SettingsPage() {
<AccountSection />
<PreferencesSection />
<SecuritySection />
<AboutSection />
</div>
</div>
)
Expand Down
6 changes: 6 additions & 0 deletions src/shared/i18n/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,19 @@ describe('i18n configuration', () => {
it('translates English common strings', () => {
expect(testI18n.t('app.name')).toBe('Cipher Note')
expect(testI18n.t('app.tagline')).toBe('Your notes. Your privacy. Your control.')
expect(testI18n.t('app.version')).toBe('v1.0.0')
expect(testI18n.t('app.githubUrl')).toBe('https://github.com/VitekHub/cipher-note/')
expect(testI18n.t('app.license')).toBe('MIT License')
})

it('switches to Czech and translates strings', async () => {
await testI18n.changeLanguage('cs')
expect(testI18n.language).toBe('cs')
expect(testI18n.t('app.name')).toBe('Cipher Note')
expect(testI18n.t('app.tagline')).toBe('Vaše poznámky. Vaše soukromí. Vaše kontrola.')
expect(testI18n.t('app.version')).toBe('v1.0.0')
expect(testI18n.t('app.githubUrl')).toBe('https://github.com/VitekHub/cipher-note/')
expect(testI18n.t('app.license')).toBe('MIT Licence')
})

it('loads crypto namespace in Czech', async () => {
Expand Down
12 changes: 6 additions & 6 deletions src/shared/i18n/locales/cs/common.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"app": {
"name": "Cipher Note",
"tagline": "Vaše poznámky. Vaše soukromí. Vaše kontrola."
"tagline": "Vaše poznámky. Vaše soukromí. Vaše kontrola.",
"version": "v1.0.0",
"githubUrl": "https://github.com/VitekHub/cipher-note/",
"license": "MIT Licence"
},
"actions": {
"save": "Uložit",
Expand All @@ -25,10 +28,6 @@
"unexpectedError": "Došlo k neočekávané chybě. Zkuste to prosím znovu.",
"pageNotFound": "Stránka, kterou hledáte, neexistuje."
},
"preAlpha": {
"ariaLabel": "Upozornění na předběžnou verzi",
"message": "Cipher Note je předběžná alfa verze. Očekávejte zásadní změny a občasné resety dat."
},
"nav": {
"dashboard": "Nástěnka",
"settings": "Nastavení",
Expand All @@ -43,6 +42,7 @@
"languageSelection": "Výběr jazyka",
"switchLanguage": "Přepnout jazyk",
"themeSelection": "Výběr motivu",
"switchTheme": "Přepnout motiv"
"switchTheme": "Přepnout motiv",
"github": "GitHub"
}
}
7 changes: 7 additions & 0 deletions src/shared/i18n/locales/cs/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
"changePassword": "Změnit heslo",
"deleteAccount": "Smazat účet"
},
"about": {
"title": "O aplikaci",
"description": "Informace o aplikaci.",
"version": "Verze",
"license": "Licence",
"sourceCode": "Zdrojový kód"
},
"keyRotation": {
"field": {
"title": "Název",
Expand Down
12 changes: 6 additions & 6 deletions src/shared/i18n/locales/en/common.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"app": {
"name": "Cipher Note",
"tagline": "Your notes. Your privacy. Your control."
"tagline": "Your notes. Your privacy. Your control.",
"version": "v1.0.0",
"githubUrl": "https://github.com/VitekHub/cipher-note/",
"license": "MIT License"
},
"actions": {
"save": "Save",
Expand All @@ -25,10 +28,6 @@
"unexpectedError": "An unexpected error occurred. Please try again.",
"pageNotFound": "The page you're looking for doesn't exist."
},
"preAlpha": {
"ariaLabel": "Pre-alpha software notice",
"message": "Cipher Note is in pre-alpha version. Expect breaking changes and periodic data resets."
},
"nav": {
"dashboard": "Dashboard",
"settings": "Settings",
Expand All @@ -43,6 +42,7 @@
"languageSelection": "Language selection",
"switchLanguage": "Switch language",
"themeSelection": "Theme selection",
"switchTheme": "Switch theme"
"switchTheme": "Switch theme",
"github": "GitHub"
}
}
7 changes: 7 additions & 0 deletions src/shared/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
"changePassword": "Change password",
"deleteAccount": "Delete account"
},
"about": {
"title": "About",
"description": "App information.",
"version": "Version",
"license": "License",
"sourceCode": "Source code"
},
"keyRotation": {
"field": {
"title": "Title",
Expand Down
21 changes: 0 additions & 21 deletions src/shared/ui/PreAlphaBanner.tsx

This file was deleted.

7 changes: 1 addition & 6 deletions src/shared/ui/nav/PublicHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@ function PublicHeader() {
<span className="text-foreground text-lg font-semibold">Cipher Note</span>
</Link>
<div className="flex items-center gap-2">
<a
href="https://github.com/VitekHub/cipher-note/"
target="_blank"
rel="noopener noreferrer"
aria-label="GitHub"
>
<a href={t('app.githubUrl')} target="_blank" rel="noopener noreferrer" aria-label={t('nav.github')}>
<GithubIcon className="text-muted-foreground hover:text-foreground transition-colors" />
</a>
<LanguageSwitcher />
Expand Down
Loading