Add feature shortcuts to header, remove AI branding, reorganize app categories
- Add Open Notebook, Unlock Article, and Transcribe buttons to header - Move search bar to center of header across all pages - Rename "AI Notebook" to "Open Notebook" throughout - Support ?type= query param on /notes/new for deep-linking - Reorganize AppSwitcher categories (Creating, Sharing, Observing) - Move rTube and rSwag to Creating category - Update EcosystemFooter link order to match new categories - Update header bg to bg-slate-900/85 - Add key emojis to UserMenu sign-in states Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5cc17e05e7
commit
cce178671b
|
|
@ -208,7 +208,7 @@ export default function NotebookDetailPage() {
|
|||
: 'text-slate-400 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
AI Notebook
|
||||
Open Notebook
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { NotebookCard } from '@/components/NotebookCard';
|
||||
import { SearchBar } from '@/components/SearchBar';
|
||||
import { Header } from '@/components/Header';
|
||||
|
||||
interface NotebookData {
|
||||
|
|
@ -32,25 +31,15 @@ export default function NotebooksPage() {
|
|||
<Header
|
||||
breadcrumbs={[{ label: 'Notebooks' }]}
|
||||
actions={
|
||||
<>
|
||||
<div className="hidden md:block w-64">
|
||||
<SearchBar />
|
||||
</div>
|
||||
<Link
|
||||
href="/notebooks/new"
|
||||
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"
|
||||
>
|
||||
<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
|
||||
href="/notebooks/new"
|
||||
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"
|
||||
>
|
||||
<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>
|
||||
}
|
||||
/>
|
||||
{/* 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-4 md:px-6 py-6 md:py-8">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-20">
|
||||
|
|
|
|||
|
|
@ -42,11 +42,14 @@ function NewNoteForm() {
|
|||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const preselectedNotebook = searchParams.get('notebookId');
|
||||
const preselectedType = searchParams.get('type');
|
||||
|
||||
const [title, setTitle] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
const [bodyJson, setBodyJson] = useState<object | null>(null);
|
||||
const [type, setType] = useState('NOTE');
|
||||
const [type, setType] = useState(
|
||||
NOTE_TYPES.some((t) => t.value === preselectedType) ? preselectedType! : 'NOTE'
|
||||
);
|
||||
const [url, setUrl] = useState('');
|
||||
const [language, setLanguage] = useState('');
|
||||
const [tags, setTags] = useState('');
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { NotebookCard } from '@/components/NotebookCard';
|
||||
import { SearchBar } from '@/components/SearchBar';
|
||||
import { EcosystemFooter } from '@/components/EcosystemFooter';
|
||||
import { TranscriptionDemo } from '@/components/TranscriptionDemo';
|
||||
|
||||
|
|
@ -30,11 +29,6 @@ export default function HomePage() {
|
|||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#0a0a0a]">
|
||||
{/* Mobile search */}
|
||||
<div className="md:hidden px-4 py-3 border-b border-slate-800">
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
||||
{/* Hero */}
|
||||
<section className="py-12 md:py-20 px-4 md:px-6">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
|
|
|
|||
|
|
@ -713,7 +713,7 @@ export default function VoicePage() {
|
|||
return (
|
||||
<div className="min-h-screen bg-[#0a0a0a] flex flex-col">
|
||||
{/* Header */}
|
||||
<header className="border-b border-slate-800 backdrop-blur-sm bg-[#0a0a0a]/90 sticky top-0 z-50">
|
||||
<header className="border-b border-slate-800 backdrop-blur-sm bg-slate-900/85 sticky top-0 z-50">
|
||||
<div className="px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-1">
|
||||
<AppSwitcher current="notes" />
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ const MODULES: AppModule[] = [
|
|||
{ id: 'space', name: 'rSpace', badge: 'rS', color: 'bg-teal-300', emoji: '🎨', description: 'Real-time collaborative canvas', domain: 'rspace.online' },
|
||||
{ id: 'notes', name: 'rNotes', badge: 'rN', color: 'bg-amber-300', emoji: '📝', description: 'Group note-taking & knowledge capture', domain: 'rnotes.online' },
|
||||
{ id: 'pubs', name: 'rPubs', badge: 'rP', color: 'bg-rose-300', emoji: '📖', description: 'Collaborative publishing platform', domain: 'rpubs.online' },
|
||||
{ id: 'tube', name: 'rTube', badge: 'rTu', color: 'bg-pink-300', emoji: '🎬', description: 'Community video platform', domain: 'rtube.online' },
|
||||
{ id: 'swag', name: 'rSwag', badge: 'rSw', color: 'bg-red-200', emoji: '👕', description: 'Community merch & swag store', domain: 'rswag.online' },
|
||||
// Planning
|
||||
{ id: 'cal', name: 'rCal', badge: 'rC', color: 'bg-sky-300', emoji: '📅', description: 'Collaborative scheduling & events', domain: 'rcal.online' },
|
||||
{ id: 'trips', name: 'rTrips', badge: 'rT', color: 'bg-emerald-300', emoji: '✈️', description: 'Group travel planning in real time', domain: 'rtrips.online' },
|
||||
|
|
@ -34,13 +36,12 @@ const MODULES: AppModule[] = [
|
|||
{ id: 'wallet', name: 'rWallet', badge: 'rW', color: 'bg-yellow-300', emoji: '💰', description: 'Multi-chain crypto wallet', domain: 'rwallet.online' },
|
||||
{ id: 'cart', name: 'rCart', badge: 'rCt', color: 'bg-orange-300', emoji: '🛒', description: 'Group commerce & shared shopping', domain: 'rcart.online' },
|
||||
{ id: 'auctions', name: 'rAuctions', badge: 'rA', color: 'bg-red-300', emoji: '🔨', description: 'Live auction platform', domain: 'rauctions.online' },
|
||||
{ id: 'swag', name: 'rSwag', badge: 'rSw', color: 'bg-red-200', emoji: '👕', description: 'Community merch & swag store', domain: 'rswag.online' },
|
||||
// Social & Media
|
||||
{ id: 'photos', name: 'rPhotos', badge: 'rPh', color: 'bg-pink-200', emoji: '📸', description: 'Shared community photo albums', domain: 'rphotos.online' },
|
||||
{ id: 'tube', name: 'rTube', badge: 'rTu', color: 'bg-pink-300', emoji: '🎬', description: 'Group video platform', domain: 'rtube.online' },
|
||||
// Sharing
|
||||
{ id: 'photos', name: 'rPhotos', badge: 'rPh', color: 'bg-pink-200', emoji: '📸', description: 'Community photo commons', domain: 'rphotos.online' },
|
||||
{ id: 'network', name: 'rNetwork', badge: 'rNe', color: 'bg-blue-300', emoji: '🕸️', description: 'Community network & social graph', domain: 'rnetwork.online' },
|
||||
{ id: 'socials', name: 'rSocials', badge: 'rSo', color: 'bg-sky-200', emoji: '📢', description: 'Social media management', domain: 'rsocials.online' },
|
||||
{ id: 'files', name: 'rFiles', badge: 'rFi', color: 'bg-cyan-300', emoji: '📁', description: 'Collaborative file storage', domain: 'rfiles.online' },
|
||||
{ id: 'socials', name: 'rSocials', badge: 'rSo', color: 'bg-sky-200', emoji: '📢', description: 'Social media management', domain: 'rsocials.online' },
|
||||
// Observing
|
||||
{ id: 'data', name: 'rData', badge: 'rD', color: 'bg-purple-300', emoji: '📊', description: 'Analytics & insights dashboard', domain: 'rdata.online' },
|
||||
// Work & Productivity
|
||||
{ id: 'work', name: 'rWork', badge: 'rWo', color: 'bg-slate-300', emoji: '📋', description: 'Project & task management', domain: 'rwork.online' },
|
||||
|
|
@ -53,6 +54,8 @@ const MODULE_CATEGORIES: Record<string, string> = {
|
|||
space: 'Creating',
|
||||
notes: 'Creating',
|
||||
pubs: 'Creating',
|
||||
tube: 'Creating',
|
||||
swag: 'Creating',
|
||||
cal: 'Planning',
|
||||
trips: 'Planning',
|
||||
maps: 'Planning',
|
||||
|
|
@ -66,13 +69,11 @@ const MODULE_CATEGORIES: Record<string, string> = {
|
|||
wallet: 'Funding & Commerce',
|
||||
cart: 'Funding & Commerce',
|
||||
auctions: 'Funding & Commerce',
|
||||
swag: 'Funding & Commerce',
|
||||
photos: 'Social & Media',
|
||||
tube: 'Social & Media',
|
||||
network: 'Social & Media',
|
||||
socials: 'Social & Media',
|
||||
files: 'Social & Media',
|
||||
data: 'Social & Media',
|
||||
photos: 'Sharing',
|
||||
network: 'Sharing',
|
||||
files: 'Sharing',
|
||||
socials: 'Sharing',
|
||||
data: 'Observing',
|
||||
work: 'Work & Productivity',
|
||||
ids: 'Identity & Infrastructure',
|
||||
stack: 'Identity & Infrastructure',
|
||||
|
|
@ -84,7 +85,8 @@ const CATEGORY_ORDER = [
|
|||
'Communicating',
|
||||
'Deciding',
|
||||
'Funding & Commerce',
|
||||
'Social & Media',
|
||||
'Sharing',
|
||||
'Observing',
|
||||
'Work & Productivity',
|
||||
'Identity & Infrastructure',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,30 +1,39 @@
|
|||
'use client';
|
||||
|
||||
const FOOTER_LINKS = [
|
||||
// Creating
|
||||
{ name: 'rSpace', href: 'https://rspace.online' },
|
||||
{ name: 'rNotes', href: 'https://rnotes.online' },
|
||||
{ name: 'rPubs', href: 'https://rpubs.online' },
|
||||
{ name: 'rTube', href: 'https://rtube.online' },
|
||||
{ name: 'rSwag', href: 'https://rswag.online' },
|
||||
// Planning
|
||||
{ name: 'rCal', href: 'https://rcal.online' },
|
||||
{ name: 'rTrips', href: 'https://rtrips.online' },
|
||||
{ name: 'rMaps', href: 'https://rmaps.online' },
|
||||
// Communicating
|
||||
{ name: 'rChats', href: 'https://rchats.online' },
|
||||
{ name: 'rInbox', href: 'https://rinbox.online' },
|
||||
{ name: 'rMail', href: 'https://rmail.online' },
|
||||
{ name: 'rForum', href: 'https://rforum.online' },
|
||||
// Deciding
|
||||
{ name: 'rChoices', href: 'https://rchoices.online' },
|
||||
{ name: 'rVote', href: 'https://rvote.online' },
|
||||
// Funding & Commerce
|
||||
{ name: 'rFunds', href: 'https://rfunds.online' },
|
||||
{ name: 'rWallet', href: 'https://rwallet.online' },
|
||||
{ name: 'rCart', href: 'https://rcart.online' },
|
||||
{ name: 'rAuctions', href: 'https://rauctions.online' },
|
||||
{ name: 'rSwag', href: 'https://rswag.online' },
|
||||
// Sharing
|
||||
{ name: 'rPhotos', href: 'https://rphotos.online' },
|
||||
{ name: 'rTube', href: 'https://rtube.online' },
|
||||
{ name: 'rNetwork', href: 'https://rnetwork.online' },
|
||||
{ name: 'rSocials', href: 'https://rsocials.online' },
|
||||
{ name: 'rFiles', href: 'https://rfiles.online' },
|
||||
{ name: 'rSocials', href: 'https://rsocials.online' },
|
||||
// Observing
|
||||
{ name: 'rData', href: 'https://rdata.online' },
|
||||
// Work & Productivity
|
||||
{ name: 'rWork', href: 'https://rwork.online' },
|
||||
// Identity & Infrastructure
|
||||
{ name: 'rIDs', href: 'https://ridentity.online' },
|
||||
{ name: 'rStack', href: 'https://rstack.online' },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { AppSwitcher } from './AppSwitcher';
|
||||
import { SpaceSwitcher } from './SpaceSwitcher';
|
||||
import { SearchBar } from './SearchBar';
|
||||
import { UserMenu } from './UserMenu';
|
||||
|
||||
export interface BreadcrumbItem {
|
||||
|
|
@ -22,7 +24,7 @@ interface HeaderProps {
|
|||
|
||||
export function Header({ current = 'notes', breadcrumbs, actions, maxWidth = 'max-w-6xl' }: HeaderProps) {
|
||||
return (
|
||||
<nav className="border-b border-slate-800 backdrop-blur-sm bg-[#0a0a0a]/90 sticky top-0 z-50">
|
||||
<nav className="border-b border-slate-800 backdrop-blur-sm bg-slate-900/85 sticky top-0 z-50">
|
||||
<div className={`${maxWidth} mx-auto px-4 md:px-6 py-3 flex items-center justify-between gap-2`}>
|
||||
{/* Left: App switcher + Space switcher + Breadcrumbs */}
|
||||
<div className="flex items-center gap-1 min-w-0">
|
||||
|
|
@ -49,6 +51,45 @@ export function Header({ current = 'notes', breadcrumbs, actions, maxWidth = 'ma
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Center: Search */}
|
||||
<div className="hidden md:block flex-1 max-w-md mx-4">
|
||||
<SearchBar />
|
||||
</div>
|
||||
|
||||
{/* Feature shortcuts */}
|
||||
<div className="hidden lg:flex items-center gap-1.5 flex-shrink-0">
|
||||
<Link
|
||||
href="/opennotebook"
|
||||
className="flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-slate-400 hover:text-amber-400 hover:bg-amber-500/10 rounded-lg transition-colors"
|
||||
title="Open Notebook"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
||||
</svg>
|
||||
Open Notebook
|
||||
</Link>
|
||||
<Link
|
||||
href="/notes/new?type=CLIP"
|
||||
className="flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-slate-400 hover:text-green-400 hover:bg-green-500/10 rounded-lg transition-colors"
|
||||
title="Unlock a paywalled article"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Unlock Article
|
||||
</Link>
|
||||
<Link
|
||||
href="/notes/new?type=AUDIO"
|
||||
className="flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium text-slate-400 hover:text-violet-400 hover:bg-violet-500/10 rounded-lg transition-colors"
|
||||
title="Record and transcribe a voice note"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
|
||||
</svg>
|
||||
Transcribe
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Right: Actions + UserMenu */}
|
||||
<div className="flex items-center gap-2 md:gap-3 flex-shrink-0">
|
||||
{actions}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export function OpenNotebookEmbed({ className = '' }: OpenNotebookEmbedProps) {
|
|||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
<span className="text-sm text-slate-400">Loading AI Notebook...</span>
|
||||
<span className="text-sm text-slate-400">Loading Notebook...</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -37,7 +37,7 @@ export function OpenNotebookEmbed({ className = '' }: OpenNotebookEmbedProps) {
|
|||
src={notebookUrl}
|
||||
className="w-full h-full border-0"
|
||||
allow="clipboard-write; clipboard-read"
|
||||
title="AI Notebook — OpenNotebook"
|
||||
title="OpenNotebook"
|
||||
onLoad={() => setLoading(false)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function UserMenu() {
|
|||
href="https://auth.ridentity.online"
|
||||
className="px-3 py-1.5 text-sm bg-cyan-500 hover:bg-cyan-400 text-black font-medium rounded-lg transition-colors no-underline"
|
||||
>
|
||||
Sign In
|
||||
🔑 Sign In
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ export function UserMenu() {
|
|||
<div className="w-7 h-7 rounded-full bg-gradient-to-br from-cyan-400 to-violet-500 flex items-center justify-center text-xs font-bold text-white flex-shrink-0">
|
||||
{(user.username || 'U')[0].toUpperCase()}
|
||||
</div>
|
||||
<span className="text-sm text-slate-300 hidden sm:inline">{displayName}</span>
|
||||
<span className="text-sm text-slate-300 hidden sm:inline">🔐 {displayName}</span>
|
||||
<span className="text-[0.7em] text-slate-500 hidden sm:inline">▾</span>
|
||||
</button>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue