diff --git a/frontend/src/app/recollections/FlippingCardView.tsx b/frontend/src/app/recollections/FlippingCardView.tsx index 3c93932..37d9c47 100644 --- a/frontend/src/app/recollections/FlippingCardView.tsx +++ b/frontend/src/app/recollections/FlippingCardView.tsx @@ -1,6 +1,6 @@ /** - * Flipping card container: Logos on the front, Flux on the back. - * Uses CSS 3D transform for a performant flip animation when switching via the menubar. + * Recollection view switcher: Logos or Flux based on pathname. + * Single container, conditional render — only the active view is mounted (no CSS flip). */ import React from 'react' @@ -14,22 +14,14 @@ export function FlippingCardView() { const isFlux = pathname.endsWith('/flux') return ( -
-
-
- -
-
- {recollectionId && } -
-
+
+ {isFlux ? ( + recollectionId ? ( + + ) : null + ) : ( + + )}
) } diff --git a/frontend/src/app/recollections/RecollectionLayout.tsx b/frontend/src/app/recollections/RecollectionLayout.tsx index bbbe184..5549503 100644 --- a/frontend/src/app/recollections/RecollectionLayout.tsx +++ b/frontend/src/app/recollections/RecollectionLayout.tsx @@ -9,6 +9,7 @@ import { RecollectionMenubarProvider } from './RecollectionMenubarContext' import { RecollectionActionsProvider } from './RecollectionActionsContext' import { RecollectionTitleContent } from './RecollectionTitleContent' import { RecollectionMenubar } from './RecollectionMenubar' +import { RecollectionViewSwitcher } from './RecollectionViewSwitcher' import { FlippingCardView } from './FlippingCardView' export function RecollectionLayout() { @@ -37,11 +38,12 @@ export function RecollectionLayout() { -
+
+
diff --git a/frontend/src/app/recollections/RecollectionMenubar.tsx b/frontend/src/app/recollections/RecollectionMenubar.tsx index 9a729a8..4ef31e0 100644 --- a/frontend/src/app/recollections/RecollectionMenubar.tsx +++ b/frontend/src/app/recollections/RecollectionMenubar.tsx @@ -1,12 +1,11 @@ /** - * Recollection menubar: Back | title + save status | registered menus (middle) | Logos | Flux. + * Recollection menubar: Back | title + save status | registered menus (middle). */ import React from 'react' -import { Link, NavLink, useParams } from 'react-router-dom' +import { Link, useParams } from 'react-router-dom' import { usePlatform } from '@/app/kosmos/KosmosContext' import { ArrowLeft } from 'lucide-react' -import { FluxIcon, LogosIcon } from '@/lib/icons' import { useRecollectionMenubar } from './RecollectionMenubarContext' import { RecollectionEditViewMenus } from './RecollectionEditViewMenus' @@ -16,7 +15,6 @@ export function RecollectionMenubar() { const { titleContent } = useRecollectionMenubar() const recollection = recollectionId ? recollections.find((p) => p.id === recollectionId) : null - const base = recollectionId ? `/recollections/${recollectionId}` : '' const defaultTitle = recollection ? ( {recollection.name} ) : null @@ -36,29 +34,6 @@ export function RecollectionMenubar() {
- {base && ( - - )}
) } diff --git a/frontend/src/app/recollections/RecollectionViewSwitcher.tsx b/frontend/src/app/recollections/RecollectionViewSwitcher.tsx new file mode 100644 index 0000000..d5cfc5d --- /dev/null +++ b/frontend/src/app/recollections/RecollectionViewSwitcher.tsx @@ -0,0 +1,88 @@ +/** + * Elevator-style view switcher: circular button moves between top (Logos) and bottom (Flux). + * Click toggles; shortcut and tooltip on hover. + */ + +import React, { useCallback, useEffect } from 'react' +import { useLocation, useNavigate, useParams } from 'react-router-dom' +import { usePlatform } from '@/app/kosmos/KosmosContext' +import { FluxIcon, LogosIcon } from '@/lib/icons' +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' +import { Kbd } from '@/components/ui/kbd' + +const VIEW_TOGGLE_KEYS = { key: 'v', shiftKey: true } + +function matchKey(ev: KeyboardEvent, want: { key: string; shiftKey: boolean }) { + const mod = ev.ctrlKey || ev.metaKey + return ev.key.toLowerCase() === want.key && !!mod && ev.shiftKey === want.shiftKey +} + +function shortcutLabel() { + if (typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform)) { + return '⌘⇧V' + } + return 'Ctrl+⇧+V' +} + +export function RecollectionViewSwitcher() { + const { recollectionId } = useParams<{ recollectionId: string }>() + const { pathname } = useLocation() + const navigate = useNavigate() + const base = recollectionId ? `/recollections/${recollectionId}` : '' + + const isFlux = pathname.endsWith('/flux') + + const toggle = useCallback(() => { + if (!base) return + navigate(isFlux ? `${base}/logos` : `${base}/flux`) + }, [base, isFlux, navigate]) + + useEffect(() => { + const onKeyDown = (ev: KeyboardEvent) => { + if (matchKey(ev, VIEW_TOGGLE_KEYS)) { + ev.preventDefault() + toggle() + } + } + window.addEventListener('keydown', onKeyDown, true) + return () => window.removeEventListener('keydown', onKeyDown, true) + }, [toggle]) + + if (!base) return null + + const tooltipText = isFlux + ? `Switch to Logos` + : `Switch to Flux` + + return ( +
+ + + + + + {tooltipText} + + + {shortcutLabel()} +
+ ) +} diff --git a/frontend/src/app/recollections/logos/LogosPage.tsx b/frontend/src/app/recollections/logos/LogosPage.tsx index 6ee54de..11f5d0b 100644 --- a/frontend/src/app/recollections/logos/LogosPage.tsx +++ b/frontend/src/app/recollections/logos/LogosPage.tsx @@ -5,6 +5,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState, forwardRef } from 'react' import { useParams } from 'react-router-dom' +import { usePlatform } from '@/app/kosmos/KosmosContext' import { FluxIcon } from '@/lib/icons' import { useCreateBlockNote, getDefaultReactSlashMenuItems, SuggestionMenuController } from '@blocknote/react' import { BlockNoteView } from '@blocknote/shadcn' @@ -49,8 +50,11 @@ function patchBlockNoteRefWarning() { export function LogosPage() { const { recollectionId } = useParams<{ recollectionId: string }>() + const { recollections } = usePlatform() const { theme } = useTheme() const { setLogosSlot } = useRecollectionActions() + const recollection = recollectionId ? recollections.find((p) => p.id === recollectionId) : null + const title = recollection?.name ?? 'Untitled' const [reloadKey, setReloadKey] = useState(0) const saveTimeoutRef = useRef | null>(null) const [saveStatus, setSaveStatus] = useState<'saved' | 'unsaved' | 'saving'>('saved') @@ -171,6 +175,9 @@ export function LogosPage() { return (
+

+ {title} +