Skip to content

Commit b8925ad

Browse files
committed
feat: add login button while public page.
1 parent 9d99060 commit b8925ad

4 files changed

Lines changed: 22 additions & 16 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Schema auto-migrates on startup in `database.py`.
6161

6262
### Wikilinks
6363

64-
Format: `[[slug]]` or `[[slug|display text]]`. Parsed on both backend (backlink tracking in `backlinks` table via `services/wikilink.py`) and frontend (navigation in viewer). Slug generation uses `pypinyin` for Chinese characters.
64+
Format: `[[slug]]` or `[[slug|display text]]`. Parsed on both backend (backlink tracking in `backlinks` table via `services/wikilink.py`) and frontend (navigation in viewer). Auto-generated slugs preserve CJK characters (Python 3 `\w` matches Unicode letters), so Chinese/Japanese/Korean titles appear in URLs as-is.
6565

6666
## API Structure
6767

backend/app/routers/pages.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,15 @@
1313

1414

1515
def slugify(title: str, existing_slug: str | None = None) -> str:
16-
"""Generate a URL-friendly slug from title. Supports Chinese via pypinyin."""
16+
"""Generate a URL-friendly slug from title. Preserves CJK characters so
17+
Chinese/Japanese/Korean titles show up in the URL as-is rather than pinyin."""
1718
if existing_slug:
1819
return existing_slug
1920

20-
try:
21-
from pypinyin import lazy_pinyin
22-
23-
parts = lazy_pinyin(title)
24-
text = "-".join(parts)
25-
except ImportError:
26-
text = title
27-
28-
text = unicodedata.normalize("NFKD", text)
29-
text = text.lower().strip()
21+
text = unicodedata.normalize("NFKC", title)
22+
text = text.strip().lower()
23+
# In Python 3 str regex, \w matches Unicode word characters including CJK,
24+
# so this strips punctuation while keeping letters from any script.
3025
text = re.sub(r"[^\w\s-]", "", text)
3126
text = re.sub(r"[-\s]+", "-", text)
3227
text = text.strip("-")

backend/tests/test_pages_advanced.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
def test_slugify():
55
assert slugify("Hello World") == "hello-world"
6-
assert slugify("測試標題") == "ce-shi-biao-ti"
7-
assert slugify("Mixed Title 測試") == "mixed-title-ce-shi"
6+
assert slugify("測試標題") == "測試標題"
7+
assert slugify("Mixed Title 測試") == "mixed-title-測試"
88
assert slugify("!@#$%^&*()") == "untitled"
9+
assert slugify("中文 標題!with 英文") == "中文-標題with-英文"
910

1011
@pytest.mark.asyncio
1112
async def test_unique_slug_collision(auth_client):

frontend/src/pages/PublicPageView.jsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useRef, useState } from 'react'
2-
import { useParams } from 'react-router-dom'
2+
import { Link, useLocation, useParams } from 'react-router-dom'
33
import publicApi from '../api/publicClient'
44
import MarkdownViewer from '../components/Viewer/MarkdownViewer'
55
import ThemeSwitcher from '../components/ThemeSwitcher'
@@ -17,6 +17,8 @@ import ThemeSwitcher from '../components/ThemeSwitcher'
1717
*/
1818
export default function PublicPageView({ notFound }) {
1919
const { slug } = useParams()
20+
const location = useLocation()
21+
const loginHref = `/login?redirect=${encodeURIComponent(location.pathname + location.search)}`
2022
const [state, setState] = useState({ status: 'loading', slug: null, page: null })
2123
const reqIdRef = useRef(0)
2224

@@ -83,7 +85,15 @@ export default function PublicPageView({ notFound }) {
8385
<div className="min-h-screen bg-bg text-text flex flex-col">
8486
<header className="flex justify-between items-center px-6 py-3 border-b border-border">
8587
<div className="text-sm font-semibold text-text">JustWiki</div>
86-
<ThemeSwitcher />
88+
<div className="flex items-center gap-3">
89+
<ThemeSwitcher />
90+
<Link
91+
to={loginHref}
92+
className="text-sm px-3 py-1.5 rounded-md bg-primary text-primary-text hover:bg-primary-hover transition-colors"
93+
>
94+
Login
95+
</Link>
96+
</div>
8797
</header>
8898
<main className="flex-1 w-full max-w-4xl mx-auto px-4 sm:px-8 py-8">
8999
<h1 className="text-3xl font-bold text-text mb-2">{page.title}</h1>

0 commit comments

Comments
 (0)