feat(workspace): align workspace UI with tasks and lists, add top tabs and collapsible sidebar
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
This commit is contained in:
1 parent
7baab8faa2
commit
59ddca91f3
2 files changed
+252
-362
No files matched your search
@@ -4,7 +4,7 @@ import {
|
||||
Users,
|
||||
Trash2,
|
||||
Download,
|
||||
Folder as FolderIcon,
|
||||
FileText,
|
||||
Radio,
|
||||
Loader2,
|
||||
ArrowLeft,
|
||||
@@ -162,7 +162,6 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
const [loading, setLoading] = useState<boolean>(!initialDoc);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState<boolean>(false);
|
||||
const [showFolderMenu, setShowFolderMenu] = useState<boolean>(false);
|
||||
const [zoomedBlockId, setZoomedBlockId] = useState<string | null>(null);
|
||||
|
||||
const titleInputRef = useRef<HTMLInputElement | null>(null);
|
||||
@@ -543,18 +542,6 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleMoveToFolder = async (folderPath: string) => {
|
||||
setShowFolderMenu(false);
|
||||
if (!doc || !canEdit) return;
|
||||
try {
|
||||
const updated = await api.moveDocument(doc.id, folderPath);
|
||||
setDoc(updated);
|
||||
onDocMetadataChange(updated);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportMarkdown = () => {
|
||||
const lines: string[] = [`# ${title || 'Unbenannte Notiz'}`, ''];
|
||||
for (const b of blocks) {
|
||||
@@ -626,46 +613,58 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Folder Selector */}
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => canEdit && setShowFolderMenu((prev) => !prev)}
|
||||
className="btn-action inline-flex items-center gap-1.5 px-2 py-1 text-xs text-muted hover:text-main"
|
||||
title="Ordner wechseln"
|
||||
{/* Breadcrumbs Navigation (Fokus-Modus) or Doc Indicator */}
|
||||
{zoomedBlock && breadcrumbs.length > 0 ? (
|
||||
<nav
|
||||
aria-label="Breadcrumb Navigation"
|
||||
className="flex items-center flex-wrap gap-1 text-xs text-muted"
|
||||
>
|
||||
<FolderIcon className="w-3.5 h-3.5 text-accent" />
|
||||
<span className="truncate max-w-[140px]">
|
||||
{doc?.folderPath ? doc.folderPath : 'Hauptebene'}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setZoomedBlockId(null)}
|
||||
className="hover:text-accent inline-flex items-center gap-1 font-medium transition-colors cursor-pointer"
|
||||
title="Zurück zum Hauptdokument"
|
||||
>
|
||||
<Home className="w-3.5 h-3.5 text-accent shrink-0" />
|
||||
<span className="truncate max-w-[130px]">
|
||||
{title || doc?.title || 'Dokument'}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{showFolderMenu && canEdit && (
|
||||
<div className="absolute left-0 mt-1 w-52 bg-canvas shadow-panel border border-border rounded-card py-1 z-30 max-h-60 overflow-y-auto">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleMoveToFolder('')}
|
||||
className={`w-full text-left px-3 py-1.5 text-xs hover:bg-panel-elevated ${
|
||||
!doc?.folderPath ? 'font-bold text-accent' : 'text-main'
|
||||
}`}
|
||||
>
|
||||
/ (Hauptebene)
|
||||
</button>
|
||||
{availableFolders.map((folder) => (
|
||||
<button
|
||||
key={folder}
|
||||
type="button"
|
||||
onClick={() => handleMoveToFolder(folder)}
|
||||
className={`w-full text-left px-3 py-1.5 text-xs hover:bg-panel-elevated truncate ${
|
||||
doc?.folderPath === folder ? 'font-bold text-accent' : 'text-main'
|
||||
}`}
|
||||
>
|
||||
📁 {folder}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{breadcrumbs.slice(1).map((crumb, idx) => {
|
||||
const isCurrent = idx === breadcrumbs.length - 2;
|
||||
return (
|
||||
<React.Fragment key={crumb.id || idx}>
|
||||
<ChevronRight className="w-3 h-3 text-faint shrink-0" />
|
||||
{isCurrent ? (
|
||||
<span
|
||||
className="font-bold text-accent truncate max-w-[160px]"
|
||||
title={crumb.title}
|
||||
>
|
||||
{crumb.title || 'Punkt'}
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setZoomedBlockId(crumb.id)}
|
||||
className="hover:text-main truncate max-w-[120px] transition-colors cursor-pointer"
|
||||
title={`Zoom zu "${crumb.title}"`}
|
||||
>
|
||||
{crumb.title || 'Punkt'}
|
||||
</button>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted">
|
||||
<FileText className="w-3.5 h-3.5 text-accent shrink-0" />
|
||||
<span className="truncate max-w-[180px] font-medium text-main">
|
||||
{title || doc?.title || 'Dokument'}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Read-only indicator */}
|
||||
{!canEdit && (
|
||||
@@ -821,52 +820,6 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
FOKUS-MODUS & ZOOM-IN (HOISTED SUBTREE VIEW)
|
||||
========================================================================= */
|
||||
<div className="space-y-3">
|
||||
{/* Clickable Breadcrumb Bar */}
|
||||
{breadcrumbs.length > 0 && (
|
||||
<nav
|
||||
aria-label="Breadcrumb Navigation"
|
||||
className="flex items-center flex-wrap gap-1.5 py-1.5 px-3 bg-canvas border border-border rounded-btn text-xs text-muted"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setZoomedBlockId(null)}
|
||||
className="hover:text-accent inline-flex items-center gap-1 font-medium transition-colors"
|
||||
title="Zurück zum Hauptdokument"
|
||||
>
|
||||
<Home className="w-3.5 h-3.5 text-accent" />
|
||||
<span className="truncate max-w-[120px]">
|
||||
{title || doc?.title || 'Dokument'}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{breadcrumbs.slice(1).map((crumb, idx) => {
|
||||
const isCurrent = idx === breadcrumbs.length - 2;
|
||||
return (
|
||||
<React.Fragment key={crumb.id || idx}>
|
||||
<ChevronRight className="w-3 h-3 text-faint shrink-0" />
|
||||
{isCurrent ? (
|
||||
<span
|
||||
className="font-bold text-accent truncate max-w-[200px]"
|
||||
title={crumb.title}
|
||||
>
|
||||
{crumb.title || 'Punkt'}
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setZoomedBlockId(crumb.id)}
|
||||
className="hover:text-main truncate max-w-[140px] transition-colors"
|
||||
title={`Zoom zu "${crumb.title}"`}
|
||||
>
|
||||
{crumb.title || 'Punkt'}
|
||||
</button>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
)}
|
||||
|
||||
{/* Prominent Zoomed Ast-Heading */}
|
||||
<div className="flex items-start justify-between gap-3 pb-4 mb-2 border-b border-border">
|
||||
<div className="flex-1 min-w-0">
|
||||
|
||||
Reference in new issue
Block a user