diff --git a/frontend/src/app/recollections/layout/RecollectionActionsContext.tsx b/frontend/src/app/recollections/layout/RecollectionActionsContext.tsx index aeda717..41f829d 100644 --- a/frontend/src/app/recollections/layout/RecollectionActionsContext.tsx +++ b/frontend/src/app/recollections/layout/RecollectionActionsContext.tsx @@ -120,7 +120,7 @@ export function RecollectionActionsProvider({ children }: { children: React.Reac pathnameRef.current = pathname const isFluxActive = pathname.endsWith('/flux') - const isLogosActive = pathname.endsWith('/logos') || /\/recollections\/[^/]+\/?$/.test(pathname) + const isLogosActive = pathname.includes('/logos') || /\/recollections\/[^/]+\/?$/.test(pathname) const activeSlot = isFluxActive ? flux : isLogosActive ? logos : null const setFluxSlot = useCallback((slot: FluxSlot | null) => { @@ -179,7 +179,7 @@ export function RecollectionActionsProvider({ children }: { children: React.Reac ) const currentPath = pathnameRef.current const fluxActive = currentPath.endsWith('/flux') - const logosActive = currentPath.endsWith('/logos') || /\/recollections\/[^/]+\/?$/.test(currentPath) + const logosActive = currentPath.includes('/logos') || /\/recollections\/[^/]+\/?$/.test(currentPath) if (written.graph && fluxActive && fluxRef.current?.onRefreshFromStore) { fluxRef.current.onRefreshFromStore(written.graph) } diff --git a/frontend/src/app/recollections/layout/RecollectionViewSwitcher.tsx b/frontend/src/app/recollections/layout/RecollectionViewSwitcher.tsx index 6895782..3a46190 100644 --- a/frontend/src/app/recollections/layout/RecollectionViewSwitcher.tsx +++ b/frontend/src/app/recollections/layout/RecollectionViewSwitcher.tsx @@ -5,7 +5,7 @@ import React, { useCallback, useEffect } from 'react' import { useLocation, useNavigate, useParams } from 'react-router-dom' -import { FluxIcon, LogosIcon, RecollectionsIcon } from '@/lib/icons' +import { FluxIcon, LogosIcon } from '@/lib/icons' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' function shortcutLabel() { @@ -19,17 +19,13 @@ export function RecollectionViewSwitcher() { const navigate = useNavigate() const base = recollectionId ? `/recollections/${recollectionId}` : '' - type Mode = 'logos' | 'katalogos' | 'flux' - const mode: Mode = pathname.endsWith('/flux') - ? 'flux' - : pathname.endsWith('/katalogos') - ? 'katalogos' - : 'logos' + type Mode = 'logos' | 'flux' + const mode: Mode = pathname.endsWith('/flux') ? 'flux' : 'logos' const goRelative = useCallback( (delta: 1 | -1) => { if (!base) return - const order: Mode[] = ['logos', 'katalogos', 'flux'] + const order: Mode[] = ['logos', 'flux'] const currentIndex = order.indexOf(mode) const nextMode = order[(currentIndex + (delta === 1 ? 1 : order.length - 1)) % order.length] navigate(`${base}/${nextMode}`) @@ -61,11 +57,7 @@ export function RecollectionViewSwitcher() { const shortcut = shortcutLabel() const tooltipText = - mode === 'logos' - ? `Logos · Next: Katalogos (${shortcut})` - : mode === 'katalogos' - ? `Katalogos · Next: Flux (${shortcut})` - : `Flux · Next: Logos (${shortcut})` + mode === 'logos' ? `Logos · Next: Flux (${shortcut})` : `Flux · Next: Logos (${shortcut})` return (
@@ -75,34 +67,20 @@ export function RecollectionViewSwitcher() { + + e.stopPropagation()}> + { + startRename(page) + onSelectPage(page.id) + }} + > + + Rename + + + movePage(page.id, -1)} disabled={!canMoveUp}> + + Move up + + movePage(page.id, 1)} disabled={!canMoveDown}> + + Move down + + + setDeleteTarget(page)} className="text-destructive focus:text-destructive"> + + Delete + + + + + ) + }, + [startRename, onSelectPage, movePage] + ) return ( -
-
- +
+
+ Pages
-
- {pages.length === 0 ? ( -
- No pages yet. -
- +
+ ) : ( + pages.map((page) => { + const canMoveUp = pages.indexOf(page) > 0 + const canMoveDown = pages.indexOf(page) < pages.length - 1 + const isActive = activePageId === page.id + return ( + + + {renderPageActions(page, { canMoveUp, canMoveDown })} + + ) + }) + )} + + + + + Katalogos + + + + + + + + + + + { + if (!open) { + setRenameTarget(null) + setRenameTitle('') + } + }} + > + + + Rename page + + setRenameTitle(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') commitRename() + if (e.key === 'Escape') { + setRenameTarget(null) + setRenameTitle('') + } + }} + aria-label="Page title" + /> + + -
- ) : ( -
    - {pages.map((page) => { - const subs = subpages(page.id) - const expanded = expandedPages.has(page.id) - const isActive = activePageId === page.id - return ( -
  • - startRename(page)} - onSelect={() => onSelectPage(page.id)} - onExpandToggle={() => toggleExpanded(page.id)} - expanded={expanded} - hasSubpages={subs.length > 0} - onAddSubpage={() => addSubpage(page.id)} - onMoveUp={() => movePage(page.id, -1)} - onMoveDown={() => movePage(page.id, 1)} - onDelete={() => setDeleteTarget(page)} - canMoveUp={pages.indexOf(page) > 0} - canMoveDown={pages.indexOf(page) < pages.length - 1} - /> - {expanded && ( -
      - {subs.map((sub, i) => { - const subActive = activePageId === sub.id - const subEditing = editingId === sub.id - return ( -
    • - startRename(sub)} - onSelect={() => onSelectPage(sub.id)} - onAddSubpage={undefined} - onMoveUp={() => movePage(sub.id, -1)} - onMoveDown={() => movePage(sub.id, 1)} - onDelete={() => setDeleteTarget(sub)} - canMoveUp={i > 0} - canMoveDown={i < subs.length - 1} - /> -
    • - ) - })} -
    - )} -
  • - ) - })} -
- )} -
+ + + + !open && setDeleteTarget(null)}> - - {deleteTarget && isPage(deleteTarget) - ? 'Delete page and subpages?' - : 'Delete subpage?'} - + Delete page? {deleteTarget && (

- {isPage(deleteTarget) && subpages(deleteTarget.id).length > 0 ? ( - <> - "{deleteTarget.title}" and its {subpages(deleteTarget.id).length}{' '} - subpage(s) will be permanently removed. - - ) : ( - <> "{deleteTarget.title}" will be permanently removed. - )} + "{deleteTarget.title}" will be permanently removed.

)} @@ -322,160 +348,3 @@ export function LogosSidebar({
) } - -type PageRowProps = { - page: LogosPageMeta - isSubpage: boolean - isActive: boolean - isEditing: boolean - editTitle: string - onEditTitleChange: (v: string) => void - onCommitRename: () => void - onStartRename: () => void - onSelect: () => void - onExpandToggle?: () => void - expanded?: boolean - hasSubpages?: boolean - onAddSubpage?: () => void - onMoveUp: () => void - onMoveDown: () => void - onDelete: () => void - canMoveUp: boolean - canMoveDown: boolean -} - -function PageRow({ - page, - isSubpage, - isActive, - isEditing, - editTitle, - onEditTitleChange, - onCommitRename, - onStartRename, - onSelect, - onExpandToggle, - expanded, - hasSubpages, - onAddSubpage, - onMoveUp, - onMoveDown, - onDelete, - canMoveUp, - canMoveDown, -}: PageRowProps) { - const isParentRow = onExpandToggle != null - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') onCommitRename() - if (e.key === 'Escape') { - onEditTitleChange(page.title) - onCommitRename() - } - } - - return ( -
- {isParentRow ? ( - // VSCode-like "twisty gutter" so labels align nicely. - - {hasSubpages ? ( - - ) : ( - - )} - - ) : isSubpage ? ( - - - - ) : null} - {isSubpage && } - {isEditing ? ( - onEditTitleChange(e.target.value)} - onBlur={onCommitRename} - onKeyDown={handleKeyDown} - autoFocus - aria-label="Page title" - /> - ) : ( - - )} - {!isEditing && ( - - - - - e.stopPropagation()}> - { onStartRename(); onSelect(); }}> - - Rename - - {onAddSubpage != null && ( - - - New subpage - - )} - - - - Move up - - - - Move down - - - - - Delete - - - - )} -
- ) -} diff --git a/frontend/src/components/ui/sidebar.tsx b/frontend/src/components/ui/sidebar.tsx index c7ca99c..7863484 100644 --- a/frontend/src/components/ui/sidebar.tsx +++ b/frontend/src/components/ui/sidebar.tsx @@ -758,6 +758,7 @@ export { SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, + sidebarMenuButtonVariants, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 6aeeffc..de9d06a 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -10,7 +10,6 @@ import { RecollectionsPage } from './app/recollections/RecollectionsPage' import { RecollectionLayout } from './app/recollections/RecollectionLayout' import { LogosPage } from './app/recollections/logos/LogosPage' import { FluxRoute } from './app/recollections/flux/FluxRoute' -import { KatalogosPage } from './app/recollections/katalogos/KatalogosPage' import './lib/prismSetup' import 'prismjs/themes/prism.css' import './styles.css' @@ -30,7 +29,7 @@ createRoot(document.getElementById('root')!).render( }> } /> } /> - } /> + } /> } /> diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 079841c..9b2b466 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -373,18 +373,23 @@ pre { .token.nunjucks-var { color: #7c3aed; } + .token.nunjucks-tag { color: #c2410c; } + .token.nunjucks-comment { color: #0d9488; } + .dark .token.nunjucks-var { color: #a78bfa; } + .dark .token.nunjucks-tag { color: #fb923c; } + .dark .token.nunjucks-comment { color: #2dd4bf; } @@ -394,40 +399,4 @@ pre { .code-editor .token.entity, .code-editor .token.url { background: none; -} - -/* BlockNote (Logos editor): font sizes to match app typography (Tailwind text-sm–text-lg scale) */ -.logos-blocknote .bn-editor { - font-size: 0.875rem; /* text-sm */ - line-height: 1.5; -} -.logos-blocknote .bn-inline-content { - font-size: inherit; - line-height: inherit; -} -.logos-blocknote [data-content-type="heading"][data-level="1"] .bn-inline-content { - font-size: 1.25rem; /* text-xl */ - font-weight: 600; - line-height: 1.3; -} -.logos-blocknote [data-content-type="heading"][data-level="2"] .bn-inline-content { - font-size: 1.125rem; /* text-lg */ - font-weight: 600; - line-height: 1.35; -} -.logos-blocknote [data-content-type="heading"][data-level="3"] .bn-inline-content { - font-size: 1rem; /* text-base */ - font-weight: 600; - line-height: 1.4; -} -.logos-blocknote [data-content-type="paragraph"] .bn-inline-content { - font-size: 0.875rem; -} -.logos-blocknote [data-content-type="bulletListItem"] .bn-inline-content, -.logos-blocknote [data-content-type="numberedListItem"] .bn-inline-content { - font-size: 0.875rem; -} -.logos-blocknote [data-content-type="codeBlock"] .bn-inline-content, -.logos-blocknote [data-content-type="code"] .bn-inline-content { - font-size: 0.8125rem; /* slightly smaller for code */ -} +} \ No newline at end of file