feat(workspace): note actions in the workspace bar on desktop, one-row sidebar header
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m5s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m5s
On large screens the editor header is gone: undo/redo, share and the menu render into the workspace tab bar (portal) after avatars, live, shared and an icon-only save status; zoom breadcrumbs move above the title. Phones and tablets keep the editor header. The sidebar header drops its heading and puts search, new folder and new note in one row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
375dda982b
commit
d408e7d307
3 files changed
+252
-216
No files matched your search
@@ -427,6 +427,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
const [docSaveStatus, setDocSaveStatus] = useState<DocSaveStatus>('saved');
|
||||
const [docSaveError, setDocSaveError] = useState<string | null>(null);
|
||||
const [docHeaderStatus, setDocHeaderStatus] = useState<DocHeaderStatus | null>(null);
|
||||
const [docActionsTarget, setDocActionsTarget] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
|
||||
// Drag and Drop state
|
||||
@@ -1416,6 +1417,14 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
<div className="hidden sm:flex items-center gap-2">
|
||||
{subView === 'editor' && selectedDocId ? (
|
||||
<>
|
||||
{/* Fixed order: people first (they come and go without moving the buttons), status, actions */}
|
||||
{docHeaderStatus && (
|
||||
<PresenceAvatars
|
||||
people={docHeaderStatus.otherPeople}
|
||||
avatars={docHeaderStatus.avatarsById}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Live connection pill */}
|
||||
{docHeaderStatus?.showLive && (
|
||||
<span
|
||||
@@ -1443,14 +1452,6 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Who else has this note open */}
|
||||
{docHeaderStatus && (
|
||||
<PresenceAvatars
|
||||
people={docHeaderStatus.otherPeople}
|
||||
avatars={docHeaderStatus.avatarsById}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Share pill */}
|
||||
{selectedDocObj &&
|
||||
!selectedDocObj.isArchived &&
|
||||
@@ -1469,7 +1470,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openDocShareModal(selectedDocObj)}
|
||||
className="px-2 py-0.5 rounded text-[10px] font-semibold bg-accent/15 text-accent border border-accent/30 flex items-center gap-1 hover:bg-accent/25 hover:border-accent/50 transition-colors cursor-pointer"
|
||||
className="px-2 py-0.5 rounded-btn text-[11px] font-semibold bg-accent/15 text-accent border border-accent/30 flex items-center gap-1 hover:bg-accent/25 hover:border-accent/50 transition-colors cursor-pointer"
|
||||
title="Teilen-Menü öffnen: Geteilt mit Nutzern/Gruppen anzeigen & verwalten"
|
||||
>
|
||||
<Users className="w-2.5 h-2.5" />
|
||||
@@ -1479,7 +1480,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openDocShareModal(selectedDocObj)}
|
||||
className="px-2 py-0.5 rounded text-[10px] font-medium border border-border text-muted flex items-center gap-1 bg-surface-subtle hover:bg-panel-elevated hover:text-main transition-colors cursor-pointer"
|
||||
className="px-2 py-0.5 rounded-btn text-[11px] font-medium border border-border text-muted flex items-center gap-1 bg-surface-subtle hover:bg-panel-elevated hover:text-main transition-colors cursor-pointer"
|
||||
title="Teilen & Berechtigungen öffnen"
|
||||
>
|
||||
<Users className="w-2.5 h-2.5" />
|
||||
@@ -1488,9 +1489,19 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
);
|
||||
})()}
|
||||
|
||||
{/* Save Status Pill */}
|
||||
{/* Save status: icon only, the text is the tooltip */}
|
||||
<div
|
||||
className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-btn text-xs font-medium border transition-colors ${
|
||||
role="img"
|
||||
aria-label={
|
||||
docSaveStatus === 'saved'
|
||||
? 'Gespeichert'
|
||||
: docSaveStatus === 'saving'
|
||||
? 'Speichert…'
|
||||
: docSaveStatus === 'unsaved'
|
||||
? 'Ungespeichert'
|
||||
: 'Fehler beim Speichern'
|
||||
}
|
||||
className={`inline-flex items-center justify-center h-7 w-7 rounded-btn border transition-colors ${
|
||||
docSaveStatus === 'saved'
|
||||
? 'bg-canvas border-border text-muted'
|
||||
: docSaveStatus === 'saving'
|
||||
@@ -1512,30 +1523,28 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
{docSaveStatus === 'saved' && (
|
||||
<>
|
||||
<Check className="w-3.5 h-3.5 text-status-ok" />
|
||||
<span>Gespeichert</span>
|
||||
</>
|
||||
)}
|
||||
{docSaveStatus === 'saving' && (
|
||||
<>
|
||||
<Loader2 className="w-3.5 h-3.5 animate-spin text-accent" />
|
||||
<span>Speichert…</span>
|
||||
</>
|
||||
)}
|
||||
{docSaveStatus === 'unsaved' && (
|
||||
<>
|
||||
<Clock className="w-3.5 h-3.5 text-status-warn" />
|
||||
<span>Ungespeichert</span>
|
||||
</>
|
||||
)}
|
||||
{docSaveStatus === 'error' && (
|
||||
<>
|
||||
<AlertTriangle className="w-3.5 h-3.5 text-status-err" />
|
||||
<span>Fehler</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{/* The open note renders undo/redo, share and its menu here on large screens */}
|
||||
<div ref={setDocActionsTarget} className="hidden lg:flex items-center" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1551,47 +1560,42 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
: 'flex'
|
||||
} w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-sidebar border-r border-border transition-all duration-200`}
|
||||
>
|
||||
{/* Sidebar Header & Actions */}
|
||||
<div className="p-3 border-b border-border space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-xs font-bold text-main uppercase tracking-wider">
|
||||
Notizen & Outliner
|
||||
</h2>
|
||||
{/* On phones these are in the menu at the top (and the + button), so not repeated here */}
|
||||
<div className="hidden sm:flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleOpenNewFolder('', e)}
|
||||
className="btn-action p-1 text-muted hover:text-main"
|
||||
title="Neuer Ordner"
|
||||
>
|
||||
<FolderPlus className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCreateDocument('')}
|
||||
className="h-7 px-2.5 bg-canvas hover:bg-panel border border-border border-dashed rounded-btn text-xs text-muted hover:text-main inline-flex items-center gap-1 transition-colors cursor-pointer"
|
||||
title="Neue Notiz anlegen"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
<span>Notiz</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar header: search and the create buttons in one row */}
|
||||
<div className="p-3 border-b border-border flex items-center gap-1.5">
|
||||
{/* Opens the global full-text search, which also finds the content of every bullet */}
|
||||
{onOpenSearch && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenSearch}
|
||||
className="input-field w-full h-11 sm:h-7 px-2.5 flex items-center gap-2 text-xs text-faint hover:text-main cursor-pointer"
|
||||
className="input-field flex-1 min-w-0 h-11 sm:h-8 px-2.5 flex items-center gap-2 text-xs text-faint hover:text-main cursor-pointer"
|
||||
title="Globale Suche (Strg+K / ⌘K oder /)"
|
||||
>
|
||||
<Search className="w-3.5 h-3.5 shrink-0" />
|
||||
<span className="flex-1 text-left truncate">Notizen durchsuchen…</span>
|
||||
<span className="hidden sm:inline font-mono text-[10px]">⌘K</span>
|
||||
<span className="hidden xl:inline font-mono text-[10px]">⌘K</span>
|
||||
</button>
|
||||
)}
|
||||
{/* On phones these are in the menu at the top (and the + button), so not repeated here */}
|
||||
<div className="hidden sm:flex shrink-0 items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleOpenNewFolder('', e)}
|
||||
className="btn-icon h-8 w-8"
|
||||
title="Neuer Ordner"
|
||||
aria-label="Neuer Ordner"
|
||||
>
|
||||
<FolderPlus className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCreateDocument('')}
|
||||
className="btn-icon h-8 w-8"
|
||||
title="Neue Notiz anlegen"
|
||||
aria-label="Neue Notiz anlegen"
|
||||
>
|
||||
<FilePlus className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scrollable Navigation Tree */}
|
||||
@@ -1882,6 +1886,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
setDocSaveError(err || null);
|
||||
}}
|
||||
onHeaderStatusChange={setDocHeaderStatus}
|
||||
actionsTarget={docActionsTarget}
|
||||
/>
|
||||
) : subView === 'tags' ? (
|
||||
/* =========================================================================
|
||||
|
||||
Reference in new issue
Block a user