feat: mobile-responsive UI + canvas sync callback
- Responsive navigation: compact buttons with icons on mobile, hidden search bar (moved to dedicated row below nav on mobile) - Responsive typography: hero text scales from 3xl to 5xl - Responsive grids: sm:grid-cols-2 breakpoint for cards - Canvas split view: full-screen overlay on mobile with close button - Breadcrumbs collapse on mobile, truncate long titles - Buttons show icon-only on small screens (add, delete, pin) - Wire up onShapeUpdate callback in notebook detail page for bidirectional canvas sync (rSpace → rnotes via /api/sync) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2351339241
commit
cf27a54caa
|
|
@ -7,6 +7,7 @@ import { NoteCard } from '@/components/NoteCard';
|
||||||
import { CanvasEmbed } from '@/components/CanvasEmbed';
|
import { CanvasEmbed } from '@/components/CanvasEmbed';
|
||||||
import { UserMenu } from '@/components/UserMenu';
|
import { UserMenu } from '@/components/UserMenu';
|
||||||
import { authFetch } from '@/lib/authFetch';
|
import { authFetch } from '@/lib/authFetch';
|
||||||
|
import type { CanvasShapeMessage } from '@/lib/canvas-sync';
|
||||||
|
|
||||||
interface NoteData {
|
interface NoteData {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -51,6 +52,23 @@ export default function NotebookDetailPage() {
|
||||||
fetchNotebook();
|
fetchNotebook();
|
||||||
}, [fetchNotebook]);
|
}, [fetchNotebook]);
|
||||||
|
|
||||||
|
const handleShapeUpdate = useCallback(async (message: CanvasShapeMessage) => {
|
||||||
|
try {
|
||||||
|
await fetch('/api/sync', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
shapeId: message.shapeId,
|
||||||
|
type: message.type,
|
||||||
|
data: message.data,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
fetchNotebook();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Canvas sync error:', err);
|
||||||
|
}
|
||||||
|
}, [fetchNotebook]);
|
||||||
|
|
||||||
const handleCreateCanvas = async () => {
|
const handleCreateCanvas = async () => {
|
||||||
if (creatingCanvas) return;
|
if (creatingCanvas) return;
|
||||||
setCreatingCanvas(true);
|
setCreatingCanvas(true);
|
||||||
|
|
@ -98,51 +116,54 @@ export default function NotebookDetailPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0a0a0a]">
|
<div className="min-h-screen bg-[#0a0a0a]">
|
||||||
<nav className="border-b border-slate-800 px-6 py-4">
|
<nav className="border-b border-slate-800 px-4 md:px-6 py-4">
|
||||||
<div className="max-w-6xl mx-auto flex items-center justify-between">
|
<div className="max-w-6xl mx-auto flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2 md:gap-3 min-w-0">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex-shrink-0">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
||||||
rN
|
rN
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600 hidden sm:inline">/</span>
|
||||||
<Link href="/notebooks" className="text-slate-400 hover:text-white transition-colors">Notebooks</Link>
|
<Link href="/notebooks" className="text-slate-400 hover:text-white transition-colors hidden sm:inline">Notebooks</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600 hidden sm:inline">/</span>
|
||||||
<span className="text-white">{notebook.title}</span>
|
<span className="text-white truncate">{notebook.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2 md:gap-3 flex-shrink-0">
|
||||||
{notebook.canvasSlug ? (
|
{notebook.canvasSlug ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowCanvas(!showCanvas)}
|
onClick={() => setShowCanvas(!showCanvas)}
|
||||||
className={`px-3 py-1.5 text-sm rounded-lg transition-colors ${
|
className={`px-2 md:px-3 py-1.5 text-sm rounded-lg transition-colors ${
|
||||||
showCanvas
|
showCanvas
|
||||||
? 'bg-amber-500/20 text-amber-400 border border-amber-500/30'
|
? 'bg-amber-500/20 text-amber-400 border border-amber-500/30'
|
||||||
: 'bg-slate-800 text-slate-400 border border-slate-700 hover:text-white'
|
: 'bg-slate-800 text-slate-400 border border-slate-700 hover:text-white'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{showCanvas ? 'Hide Canvas' : 'Show Canvas'}
|
<span className="hidden sm:inline">{showCanvas ? 'Hide Canvas' : 'Show Canvas'}</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" /></svg>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={handleCreateCanvas}
|
onClick={handleCreateCanvas}
|
||||||
disabled={creatingCanvas}
|
disabled={creatingCanvas}
|
||||||
className="px-3 py-1.5 text-sm bg-slate-800 text-slate-400 border border-slate-700 rounded-lg hover:text-white transition-colors"
|
className="px-2 md:px-3 py-1.5 text-sm bg-slate-800 text-slate-400 border border-slate-700 rounded-lg hover:text-white transition-colors hidden sm:inline-flex"
|
||||||
>
|
>
|
||||||
{creatingCanvas ? 'Creating...' : 'Create Canvas'}
|
{creatingCanvas ? 'Creating...' : 'Create Canvas'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Link
|
<Link
|
||||||
href={`/notes/new?notebookId=${notebook.id}`}
|
href={`/notes/new?notebookId=${notebook.id}`}
|
||||||
className="px-4 py-2 bg-amber-500 hover:bg-amber-400 text-black text-sm font-medium rounded-lg transition-colors"
|
className="px-3 md:px-4 py-2 bg-amber-500 hover:bg-amber-400 text-black text-sm font-medium rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Add Note
|
<span className="hidden sm:inline">Add Note</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
|
||||||
</Link>
|
</Link>
|
||||||
<button
|
<button
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
className="px-3 py-1.5 text-sm text-red-400 hover:text-red-300 border border-red-900/30 hover:border-red-800 rounded-lg transition-colors"
|
className="px-2 md:px-3 py-1.5 text-sm text-red-400 hover:text-red-300 border border-red-900/30 hover:border-red-800 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Delete
|
<span className="hidden sm:inline">Delete</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||||
</button>
|
</button>
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -151,12 +172,12 @@ export default function NotebookDetailPage() {
|
||||||
|
|
||||||
<div className={`flex ${showCanvas ? 'gap-0' : ''}`}>
|
<div className={`flex ${showCanvas ? 'gap-0' : ''}`}>
|
||||||
{/* Notes panel */}
|
{/* Notes panel */}
|
||||||
<main className={`${showCanvas ? 'w-3/5' : 'w-full'} max-w-6xl mx-auto px-6 py-8`}>
|
<main className={`${showCanvas ? 'hidden md:block md:w-3/5' : 'w-full'} max-w-6xl mx-auto px-4 md:px-6 py-6 md:py-8`}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8">
|
<div className="mb-6 md:mb-8">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<div className="w-4 h-4 rounded-full" style={{ backgroundColor: notebook.coverColor }} />
|
<div className="w-4 h-4 rounded-full" style={{ backgroundColor: notebook.coverColor }} />
|
||||||
<h1 className="text-3xl font-bold text-white">{notebook.title}</h1>
|
<h1 className="text-2xl md:text-3xl font-bold text-white">{notebook.title}</h1>
|
||||||
</div>
|
</div>
|
||||||
{notebook.description && (
|
{notebook.description && (
|
||||||
<p className="text-slate-400 ml-7">{notebook.description}</p>
|
<p className="text-slate-400 ml-7">{notebook.description}</p>
|
||||||
|
|
@ -194,7 +215,7 @@ export default function NotebookDetailPage() {
|
||||||
{tab === 'pinned' ? 'No pinned notes' : 'No notes yet. Add one!'}
|
{tab === 'pinned' ? 'No pinned notes' : 'No notes yet. Add one!'}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-3">
|
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||||
{filteredNotes.map((note) => (
|
{filteredNotes.map((note) => (
|
||||||
<NoteCard
|
<NoteCard
|
||||||
key={note.id}
|
key={note.id}
|
||||||
|
|
@ -216,10 +237,19 @@ export default function NotebookDetailPage() {
|
||||||
)}
|
)}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{/* Canvas sidebar */}
|
{/* Canvas sidebar — full screen on mobile, split on desktop */}
|
||||||
{showCanvas && notebook.canvasSlug && (
|
{showCanvas && notebook.canvasSlug && (
|
||||||
<div className="w-2/5 border-l border-slate-800 sticky top-0 h-screen">
|
<div className="fixed inset-0 z-40 md:relative md:inset-auto md:w-2/5 md:z-auto border-l border-slate-800 md:sticky md:top-0 md:h-screen bg-[#0a0a0a]">
|
||||||
<CanvasEmbed canvasSlug={notebook.canvasSlug} className="h-full" />
|
<div className="md:hidden flex items-center justify-between px-4 py-3 border-b border-slate-800">
|
||||||
|
<span className="text-sm font-medium text-white">Canvas</span>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowCanvas(false)}
|
||||||
|
className="p-1 text-slate-400 hover:text-white"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<CanvasEmbed canvasSlug={notebook.canvasSlug} className="h-full" onShapeUpdate={handleShapeUpdate} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -42,23 +42,23 @@ export default function NewNotebookPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0a0a0a]">
|
<div className="min-h-screen bg-[#0a0a0a]">
|
||||||
<nav className="border-b border-slate-800 px-6 py-4">
|
<nav className="border-b border-slate-800 px-4 md:px-6 py-4">
|
||||||
<div className="max-w-6xl mx-auto flex items-center gap-3">
|
<div className="max-w-6xl mx-auto flex items-center gap-3">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
||||||
rN
|
rN
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600 hidden sm:inline">/</span>
|
||||||
<Link href="/notebooks" className="text-slate-400 hover:text-white transition-colors">Notebooks</Link>
|
<Link href="/notebooks" className="text-slate-400 hover:text-white transition-colors hidden sm:inline">Notebooks</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600 hidden sm:inline">/</span>
|
||||||
<span className="text-white">New</span>
|
<span className="text-white">New</span>
|
||||||
<div className="ml-auto"><UserMenu /></div>
|
<div className="ml-auto"><UserMenu /></div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main className="max-w-2xl mx-auto px-6 py-12">
|
<main className="max-w-2xl mx-auto px-4 md:px-6 py-8 md:py-12">
|
||||||
<h1 className="text-3xl font-bold text-white mb-8">Create Notebook</h1>
|
<h1 className="text-2xl md:text-3xl font-bold text-white mb-6 md:mb-8">Create Notebook</h1>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -29,34 +29,39 @@ export default function NotebooksPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0a0a0a]">
|
<div className="min-h-screen bg-[#0a0a0a]">
|
||||||
<nav className="border-b border-slate-800 px-6 py-4">
|
<nav className="border-b border-slate-800 px-4 md:px-6 py-4">
|
||||||
<div className="max-w-6xl mx-auto flex items-center justify-between">
|
<div className="max-w-6xl mx-auto flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
||||||
rN
|
rN
|
||||||
</div>
|
</div>
|
||||||
<span className="text-lg font-semibold text-white">rNotes</span>
|
<span className="text-lg font-semibold text-white hidden sm:inline">rNotes</span>
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600">/</span>
|
||||||
<span className="text-slate-400">Notebooks</span>
|
<span className="text-slate-400">Notebooks</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-2 md:gap-4">
|
||||||
<div className="w-64">
|
<div className="hidden md:block w-64">
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/notebooks/new"
|
href="/notebooks/new"
|
||||||
className="px-4 py-2 bg-amber-500 hover:bg-amber-400 text-black text-sm font-medium rounded-lg transition-colors"
|
className="px-3 md:px-4 py-2 bg-amber-500 hover:bg-amber-400 text-black text-sm font-medium rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
New Notebook
|
<span className="hidden sm:inline">New Notebook</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
|
||||||
</Link>
|
</Link>
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
{/* Mobile search */}
|
||||||
|
<div className="md:hidden px-4 py-3 border-b border-slate-800">
|
||||||
|
<SearchBar />
|
||||||
|
</div>
|
||||||
|
|
||||||
<main className="max-w-6xl mx-auto px-6 py-8">
|
<main className="max-w-6xl mx-auto px-4 md:px-6 py-6 md:py-8">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-20">
|
<div className="flex items-center justify-center py-20">
|
||||||
<svg className="animate-spin h-8 w-8 text-amber-400" viewBox="0 0 24 24">
|
<svg className="animate-spin h-8 w-8 text-amber-400" viewBox="0 0 24 24">
|
||||||
|
|
@ -75,7 +80,7 @@ export default function NotebooksPage() {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid md:grid-cols-3 gap-4">
|
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-3 md:gap-4">
|
||||||
{notebooks.map((nb) => (
|
{notebooks.map((nb) => (
|
||||||
<NotebookCard
|
<NotebookCard
|
||||||
key={nb.id}
|
key={nb.id}
|
||||||
|
|
|
||||||
|
|
@ -123,44 +123,45 @@ export default function NoteDetailPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0a0a0a]">
|
<div className="min-h-screen bg-[#0a0a0a]">
|
||||||
<nav className="border-b border-slate-800 px-6 py-4">
|
<nav className="border-b border-slate-800 px-4 md:px-6 py-4">
|
||||||
<div className="max-w-4xl mx-auto flex items-center justify-between">
|
<div className="max-w-4xl mx-auto flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2 md:gap-3 min-w-0">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex-shrink-0">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
||||||
rN
|
rN
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600 hidden sm:inline">/</span>
|
||||||
{note.notebook ? (
|
{note.notebook ? (
|
||||||
<>
|
<>
|
||||||
<Link href={`/notebooks/${note.notebook.id}`} className="text-slate-400 hover:text-white transition-colors">
|
<Link href={`/notebooks/${note.notebook.id}`} className="text-slate-400 hover:text-white transition-colors hidden sm:inline truncate max-w-[120px]">
|
||||||
{note.notebook.title}
|
{note.notebook.title}
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-slate-600">/</span>
|
<span className="text-slate-600 hidden sm:inline">/</span>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
<span className="text-white truncate max-w-[200px]">{note.title}</span>
|
<span className="text-white truncate max-w-[120px] md:max-w-[200px]">{note.title}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1.5 md:gap-2 flex-shrink-0">
|
||||||
<button
|
<button
|
||||||
onClick={handleTogglePin}
|
onClick={handleTogglePin}
|
||||||
className={`px-3 py-1.5 text-sm rounded-lg border transition-colors ${
|
className={`px-2 md:px-3 py-1.5 text-sm rounded-lg border transition-colors ${
|
||||||
note.isPinned
|
note.isPinned
|
||||||
? 'bg-amber-500/20 text-amber-400 border-amber-500/30'
|
? 'bg-amber-500/20 text-amber-400 border-amber-500/30'
|
||||||
: 'text-slate-400 border-slate-700 hover:text-white'
|
: 'text-slate-400 border-slate-700 hover:text-white'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{note.isPinned ? 'Unpin' : 'Pin to Canvas'}
|
<span className="hidden sm:inline">{note.isPinned ? 'Unpin' : 'Pin to Canvas'}</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" /></svg>
|
||||||
</button>
|
</button>
|
||||||
{editing ? (
|
{editing ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="px-3 py-1.5 text-sm bg-amber-500 hover:bg-amber-400 text-black font-medium rounded-lg transition-colors"
|
className="px-2 md:px-3 py-1.5 text-sm bg-amber-500 hover:bg-amber-400 text-black font-medium rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{saving ? 'Saving...' : 'Save'}
|
{saving ? '...' : 'Save'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -168,7 +169,7 @@ export default function NoteDetailPage() {
|
||||||
setEditTitle(note.title);
|
setEditTitle(note.title);
|
||||||
setEditContent(note.content);
|
setEditContent(note.content);
|
||||||
}}
|
}}
|
||||||
className="px-3 py-1.5 text-sm text-slate-400 border border-slate-700 rounded-lg hover:text-white transition-colors"
|
className="px-2 md:px-3 py-1.5 text-sm text-slate-400 border border-slate-700 rounded-lg hover:text-white transition-colors hidden sm:inline-flex"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -176,25 +177,26 @@ export default function NoteDetailPage() {
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => setEditing(true)}
|
onClick={() => setEditing(true)}
|
||||||
className="px-3 py-1.5 text-sm text-slate-400 border border-slate-700 rounded-lg hover:text-white transition-colors"
|
className="px-2 md:px-3 py-1.5 text-sm text-slate-400 border border-slate-700 rounded-lg hover:text-white transition-colors"
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
className="px-3 py-1.5 text-sm text-red-400 hover:text-red-300 border border-red-900/30 rounded-lg transition-colors"
|
className="px-2 md:px-3 py-1.5 text-sm text-red-400 hover:text-red-300 border border-red-900/30 rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
Delete
|
<span className="hidden sm:inline">Delete</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||||
</button>
|
</button>
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main className="max-w-4xl mx-auto px-6 py-8">
|
<main className="max-w-4xl mx-auto px-4 md:px-6 py-6 md:py-8">
|
||||||
{/* Metadata */}
|
{/* Metadata */}
|
||||||
<div className="flex items-center gap-3 mb-6">
|
<div className="flex flex-wrap items-center gap-2 md:gap-3 mb-6">
|
||||||
<span className={`text-xs font-bold uppercase px-2 py-1 rounded ${TYPE_COLORS[note.type] || TYPE_COLORS.NOTE}`}>
|
<span className={`text-xs font-bold uppercase px-2 py-1 rounded ${TYPE_COLORS[note.type] || TYPE_COLORS.NOTE}`}>
|
||||||
{note.type}
|
{note.type}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -264,7 +266,7 @@ export default function NoteDetailPage() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-white mb-6">{note.title}</h1>
|
<h1 className="text-2xl md:text-3xl font-bold text-white mb-6">{note.title}</h1>
|
||||||
{note.type === 'CODE' ? (
|
{note.type === 'CODE' ? (
|
||||||
<pre className="bg-slate-800/50 border border-slate-700 rounded-lg p-4 overflow-x-auto">
|
<pre className="bg-slate-800/50 border border-slate-700 rounded-lg p-4 overflow-x-auto">
|
||||||
<code className="text-sm text-slate-200 font-mono">
|
<code className="text-sm text-slate-200 font-mono">
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ function NewNoteForm() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0a0a0a]">
|
<div className="min-h-screen bg-[#0a0a0a]">
|
||||||
<nav className="border-b border-slate-800 px-6 py-4">
|
<nav className="border-b border-slate-800 px-4 md:px-6 py-4">
|
||||||
<div className="max-w-6xl mx-auto flex items-center gap-3">
|
<div className="max-w-6xl mx-auto flex items-center gap-3">
|
||||||
<Link href="/" className="flex items-center gap-3">
|
<Link href="/" className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
||||||
|
|
@ -121,8 +121,8 @@ function NewNoteForm() {
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<main className="max-w-3xl mx-auto px-6 py-12">
|
<main className="max-w-3xl mx-auto px-4 md:px-6 py-8 md:py-12">
|
||||||
<h1 className="text-3xl font-bold text-white mb-8">Create Note</h1>
|
<h1 className="text-2xl md:text-3xl font-bold text-white mb-6 md:mb-8">Create Note</h1>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
{/* Type selector */}
|
{/* Type selector */}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-[#0a0a0a]">
|
<div className="min-h-screen bg-[#0a0a0a]">
|
||||||
{/* Nav */}
|
{/* Nav */}
|
||||||
<nav className="border-b border-slate-800 px-6 py-4">
|
<nav className="border-b border-slate-800 px-4 md:px-6 py-4">
|
||||||
<div className="max-w-6xl mx-auto flex items-center justify-between">
|
<div className="max-w-6xl mx-auto flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center text-sm font-bold text-black">
|
||||||
|
|
@ -38,51 +38,57 @@ export default function HomePage() {
|
||||||
</div>
|
</div>
|
||||||
<span className="text-lg font-semibold text-white">rNotes</span>
|
<span className="text-lg font-semibold text-white">rNotes</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-2 md:gap-4">
|
||||||
<div className="w-64">
|
<div className="hidden md:block w-64">
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
href="/notebooks"
|
href="/notebooks"
|
||||||
className="text-sm text-slate-400 hover:text-white transition-colors"
|
className="text-sm text-slate-400 hover:text-white transition-colors hidden sm:inline"
|
||||||
>
|
>
|
||||||
Notebooks
|
Notebooks
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/notes/new"
|
href="/notes/new"
|
||||||
className="px-4 py-2 bg-amber-500 hover:bg-amber-400 text-black text-sm font-medium rounded-lg transition-colors"
|
className="px-3 md:px-4 py-2 bg-amber-500 hover:bg-amber-400 text-black text-sm font-medium rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
New Note
|
<span className="hidden sm:inline">New Note</span>
|
||||||
|
<svg className="w-4 h-4 sm:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" /></svg>
|
||||||
</Link>
|
</Link>
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
{/* Mobile search */}
|
||||||
|
<div className="md:hidden px-4 py-3 border-b border-slate-800">
|
||||||
|
<SearchBar />
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Hero */}
|
{/* Hero */}
|
||||||
<section className="py-20 px-6">
|
<section className="py-12 md:py-20 px-4 md:px-6">
|
||||||
<div className="max-w-4xl mx-auto text-center">
|
<div className="max-w-4xl mx-auto text-center">
|
||||||
<h1 className="text-5xl font-bold mb-4">
|
<h1 className="text-3xl md:text-5xl font-bold mb-4">
|
||||||
<span className="bg-gradient-to-r from-amber-400 to-orange-500 bg-clip-text text-transparent">
|
<span className="bg-gradient-to-r from-amber-400 to-orange-500 bg-clip-text text-transparent">
|
||||||
Capture Everything
|
Capture Everything
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<span className="text-white">Find Anything</span>
|
<span className="text-white">Find Anything</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg text-slate-400 mb-8 max-w-2xl mx-auto">
|
<p className="text-base md:text-lg text-slate-400 mb-6 md:mb-8 max-w-2xl mx-auto">
|
||||||
Notes, clips, bookmarks, code, images, and files — all in one place.
|
Notes, clips, bookmarks, code, images, and files — all in one place.
|
||||||
Organize in notebooks, tag freely, and collaborate on a visual canvas shared across r*Spaces.
|
Organize in notebooks, tag freely, and collaborate on a visual canvas shared across r*Spaces.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-center gap-4">
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4">
|
||||||
<Link
|
<Link
|
||||||
href="/notebooks/new"
|
href="/notebooks/new"
|
||||||
className="px-6 py-3 bg-amber-500 hover:bg-amber-400 text-black font-semibold rounded-lg transition-colors"
|
className="w-full sm:w-auto px-6 py-3 bg-amber-500 hover:bg-amber-400 text-black font-semibold rounded-lg transition-colors text-center"
|
||||||
>
|
>
|
||||||
Create Notebook
|
Create Notebook
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/notes/new"
|
href="/notes/new"
|
||||||
className="px-6 py-3 border border-slate-700 hover:border-slate-600 text-white rounded-lg transition-colors"
|
className="w-full sm:w-auto px-6 py-3 border border-slate-700 hover:border-slate-600 text-white rounded-lg transition-colors text-center"
|
||||||
>
|
>
|
||||||
Quick Note
|
Quick Note
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -91,10 +97,10 @@ export default function HomePage() {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* How it works */}
|
{/* How it works */}
|
||||||
<section className="py-16 px-6 border-t border-slate-800">
|
<section className="py-12 md:py-16 px-4 md:px-6 border-t border-slate-800">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<h2 className="text-2xl font-bold text-white text-center mb-12">How It Works</h2>
|
<h2 className="text-xl md:text-2xl font-bold text-white text-center mb-8 md:mb-12">How It Works</h2>
|
||||||
<div className="grid md:grid-cols-3 gap-8">
|
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-6 md:gap-8">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="w-12 h-12 rounded-xl bg-amber-500/10 border border-amber-500/20 flex items-center justify-center mx-auto mb-4">
|
<div className="w-12 h-12 rounded-xl bg-amber-500/10 border border-amber-500/20 flex items-center justify-center mx-auto mb-4">
|
||||||
<svg className="w-6 h-6 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-6 h-6 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
|
@ -128,15 +134,15 @@ export default function HomePage() {
|
||||||
|
|
||||||
{/* Recent notebooks */}
|
{/* Recent notebooks */}
|
||||||
{!loading && notebooks.length > 0 && (
|
{!loading && notebooks.length > 0 && (
|
||||||
<section className="py-16 px-6 border-t border-slate-800">
|
<section className="py-12 md:py-16 px-4 md:px-6 border-t border-slate-800">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className="flex items-center justify-between mb-8">
|
<div className="flex items-center justify-between mb-6 md:mb-8">
|
||||||
<h2 className="text-2xl font-bold text-white">Recent Notebooks</h2>
|
<h2 className="text-xl md:text-2xl font-bold text-white">Recent Notebooks</h2>
|
||||||
<Link href="/notebooks" className="text-sm text-amber-400 hover:text-amber-300">
|
<Link href="/notebooks" className="text-sm text-amber-400 hover:text-amber-300">
|
||||||
View all
|
View all
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid md:grid-cols-3 gap-4">
|
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-3 md:gap-4">
|
||||||
{notebooks.slice(0, 6).map((nb) => (
|
{notebooks.slice(0, 6).map((nb) => (
|
||||||
<NotebookCard
|
<NotebookCard
|
||||||
key={nb.id}
|
key={nb.id}
|
||||||
|
|
@ -154,8 +160,8 @@ export default function HomePage() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<footer className="border-t border-slate-800 px-6 py-8">
|
<footer className="border-t border-slate-800 px-4 md:px-6 py-6 md:py-8">
|
||||||
<div className="max-w-6xl mx-auto flex items-center justify-between text-sm text-slate-500">
|
<div className="max-w-6xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-2 text-sm text-slate-500">
|
||||||
<span>rNotes.online — Part of the r* ecosystem</span>
|
<span>rNotes.online — Part of the r* ecosystem</span>
|
||||||
<a href="https://rspace.online" className="hover:text-amber-400 transition-colors">
|
<a href="https://rspace.online" className="hover:text-amber-400 transition-colors">
|
||||||
rSpace.online
|
rSpace.online
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue