feat(workspace): add help page, folder drag and drop, pinned notes section, and clean up editor UX
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 52s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 52s
This commit is contained in:
1 parent
fe1d574787
commit
fb805b30a1
5 files changed
+610
-67
No files matched your search
@@ -0,0 +1,374 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Search,
|
||||
Layers,
|
||||
FileText,
|
||||
CheckSquare,
|
||||
Keyboard,
|
||||
Pin,
|
||||
FolderTree,
|
||||
Folder,
|
||||
Sparkles,
|
||||
Command,
|
||||
CornerDownLeft,
|
||||
CornerUpLeft,
|
||||
MoveVertical,
|
||||
Check,
|
||||
Share2,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface HelpViewProps {
|
||||
onBack: () => void;
|
||||
}
|
||||
|
||||
type HelpCategory = 'all' | 'workspace' | 'triage' | 'tasks' | 'shortcuts';
|
||||
|
||||
export const HelpView: React.FC<HelpViewProps> = ({ onBack }) => {
|
||||
const [activeCategory, setActiveCategory] = useState<HelpCategory>('all');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-5xl mx-auto space-y-4 pb-12">
|
||||
{/* Header Bar */}
|
||||
<div className="panel-surface p-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
className="btn-action text-xs flex items-center gap-1.5"
|
||||
title="Zurück zur vorherigen Ansicht"
|
||||
>
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
<span>Zurück</span>
|
||||
</button>
|
||||
|
||||
<div>
|
||||
<h1 className="text-base sm:text-lg font-bold text-main flex items-center gap-2">
|
||||
<FileText className="w-4 h-4 text-accent" />
|
||||
<span>Hilfe & Dokumentation</span>
|
||||
</h1>
|
||||
<p className="text-xs text-muted">
|
||||
Alles Wichtige zur Tastaturbedienung, Outliner-Notizen, Triage und Zusammenarbeit
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="relative w-full sm:w-64">
|
||||
<Search className="w-3.5 h-3.5 text-muted absolute left-2.5 top-1/2 -translate-y-1/2" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="In Hilfe suchen…"
|
||||
className="w-full h-8 pl-8 pr-3 input-field text-xs bg-canvas"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Category Filter Pills */}
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
{[
|
||||
{ id: 'all', label: 'Alle Themen', icon: Sparkles },
|
||||
{ id: 'workspace', label: 'Workspace & Outliner', icon: FileText },
|
||||
{ id: 'triage', label: 'Triage & Quick-Dump', icon: Layers },
|
||||
{ id: 'tasks', label: 'Aufgaben & Listen', icon: CheckSquare },
|
||||
{ id: 'shortcuts', label: 'Tastatur-Kürzel', icon: Keyboard },
|
||||
].map((cat) => {
|
||||
const Icon = cat.icon;
|
||||
const isActive = activeCategory === cat.id;
|
||||
return (
|
||||
<button
|
||||
key={cat.id}
|
||||
type="button"
|
||||
onClick={() => setActiveCategory(cat.id as HelpCategory)}
|
||||
className={`h-8 px-3 text-xs font-medium border rounded-btn flex items-center gap-1.5 transition-colors ${
|
||||
isActive
|
||||
? 'bg-panel-elevated border-accent text-main font-semibold'
|
||||
: 'bg-panel border-border text-muted hover:text-main'
|
||||
}`}
|
||||
>
|
||||
<Icon className="w-3.5 h-3.5 text-accent" />
|
||||
<span>{cat.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Grid of Documentation Cards */}
|
||||
<div className="space-y-4">
|
||||
{/* SECTION: WORKSPACE & OUTLINER */}
|
||||
{(activeCategory === 'all' || activeCategory === 'workspace') && (
|
||||
<div className="panel-surface p-5 space-y-4">
|
||||
<div className="border-b border-border pb-2.5">
|
||||
<h2 className="text-sm font-bold text-main flex items-center gap-2">
|
||||
<FileText className="w-4 h-4 text-accent" />
|
||||
<span>Workspace: Der hierarchische Logseq-Outliner</span>
|
||||
</h2>
|
||||
<p className="text-xs text-muted mt-0.5">
|
||||
Jede Notiz in MindSift ist ein flexibler hierarchischer Outliner und wird als Standard-Markdown auf der Festplatte gespeichert.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-2">
|
||||
<h3 className="font-semibold text-main flex items-center gap-1.5 text-xs">
|
||||
<Keyboard className="w-3.5 h-3.5 text-accent" />
|
||||
<span>Flüssige Tastatursteuerung</span>
|
||||
</h3>
|
||||
<ul className="space-y-1.5 text-muted leading-relaxed">
|
||||
<li>
|
||||
<strong className="text-main">Enter:</strong> Erzeugt sofort einen neuen Bullet-Point auf derselben Ebene.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-main">Tab:</strong> Rückt den aktuellen Bullet-Point ein und ordnet ihn dem vorherigen Punkt unter.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-main">Shift + Tab:</strong> Rückt den Bullet-Point wieder nach links aus (verringert die Tiefe).
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-main">Shift + Enter:</strong> Erzeugt einen weichen Zeilenumbruch innerhalb desselben Textblocks.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-main">Backspace:</strong> Auf einem leeren Block rückt er die Ebene aus bzw. löscht den leeren Bullet.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-2">
|
||||
<h3 className="font-semibold text-main flex items-center gap-1.5 text-xs">
|
||||
<Command className="w-3.5 h-3.5 text-accent" />
|
||||
<span>Slash-Befehle (/)</span>
|
||||
</h3>
|
||||
<p className="text-muted leading-relaxed">
|
||||
Tippe einfach <code className="text-accent bg-panel px-1 py-0.5 rounded font-mono">/</code> in einen leeren Block, um den Typ flexibel zu wechseln:
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-1 text-[11px] font-mono text-muted">
|
||||
<span>/todo (Checkbox-Aufgabe)</span>
|
||||
<span>/h1 (Haupt-Überschrift)</span>
|
||||
<span>/h2 (Abschnitts-Titel)</span>
|
||||
<span>/h3 (Unter-Überschrift)</span>
|
||||
<span>/code (Code-Block)</span>
|
||||
<span>/table (Markdown-Tabelle)</span>
|
||||
<span>/callout (Hinweis-Box)</span>
|
||||
<span>/progress (Fortschritt 0-100%)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-2">
|
||||
<h3 className="font-semibold text-main flex items-center gap-1.5 text-xs">
|
||||
<FolderTree className="w-3.5 h-3.5 text-accent" />
|
||||
<span>Ordnerstruktur & Drag & Drop</span>
|
||||
</h3>
|
||||
<p className="text-muted leading-relaxed">
|
||||
Notizen können im linken Datei-Explorer per <strong>Drag & Drop</strong> direkt auf beliebige Ordner gezogen werden, um sie zu verschieben.
|
||||
Ziehst du eine Notiz auf das Hauptverzeichnis, wird sie aus allen Unterordnern herausgenommen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-2">
|
||||
<h3 className="font-semibold text-main flex items-center gap-1.5 text-xs">
|
||||
<Pin className="w-3.5 h-3.5 text-accent" />
|
||||
<span>Zoom-Hoisting & Favoriten anpinnen</span>
|
||||
</h3>
|
||||
<ul className="space-y-1.5 text-muted leading-relaxed">
|
||||
<li>
|
||||
<strong>Klick auf den Bullet-Punkt (•):</strong> Fokussiert tief in diesen Ast hinein (Zoom-in Hoisting). Du kannst über die Breadcrumbs ganz oben jederzeit zurückspringen.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Bullet-Punkt ziehen:</strong> Verschiebt den Block samt aller eingerückten Unterelemente an eine neue Position.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Anpinnen:</strong> Wichtige Notizen heften sich ganz oben im Datei-Explorer unter „Angepinnte Notizen" an.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SECTION: TRIAGE & QUICK DUMP */}
|
||||
{(activeCategory === 'all' || activeCategory === 'triage') && (
|
||||
<div className="panel-surface p-5 space-y-4">
|
||||
<div className="border-b border-border pb-2.5">
|
||||
<h2 className="text-sm font-bold text-main flex items-center gap-2">
|
||||
<Layers className="w-4 h-4 text-accent" />
|
||||
<span>Triage & Quick Dump: Der Kopf frei in 3 Sekunden</span>
|
||||
</h2>
|
||||
<p className="text-xs text-muted mt-0.5">
|
||||
Sofortiges Erfassen ohne Reibung. Gedanken werden gesammelt und anschließend im Triage-Deck mit 1 Taste entschieden.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 text-xs">
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-1.5">
|
||||
<h4 className="font-semibold text-main">1. Blitz-Erfassung</h4>
|
||||
<p className="text-muted leading-relaxed">
|
||||
Drücke überall <kbd className="px-1 py-0.5 bg-panel border border-border rounded font-mono text-[10px]">C</kbd> oder <kbd className="px-1 py-0.5 bg-panel border border-border rounded font-mono text-[10px]">/</kbd>, tippe den Gedanken ein und drücke <kbd className="px-1 py-0.5 bg-panel border border-border rounded font-mono text-[10px]">Enter</kbd>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-1.5">
|
||||
<h4 className="font-semibold text-main">2. Triage Entscheidungs-Tasten</h4>
|
||||
<ul className="text-muted space-y-1">
|
||||
<li><strong className="text-status-err">H</strong> oder <strong>←</strong> : Wegwerfen / Erledigt</li>
|
||||
<li><strong className="text-accent">L</strong> oder <strong>→</strong> : Als Aufgabe anlegen</li>
|
||||
<li><strong>T</strong> : Aufgabe mit Fälligkeitsdatum</li>
|
||||
<li><strong>D</strong> : In Workspace-Notiz umwandeln</li>
|
||||
<li><strong>Leertaste</strong> : Später (ans Ende schieben)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-1.5">
|
||||
<h4 className="font-semibold text-main">3. Direkt-Zuordnung</h4>
|
||||
<p className="text-muted leading-relaxed">
|
||||
Über das Dropdown neben der Eingabeleiste kannst du einen Eintrag schon beim Schreiben direkt einer Aufgabenliste, Einkaufsliste oder Notiz zuweisen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SECTION: TASKS & LISTS */}
|
||||
{(activeCategory === 'all' || activeCategory === 'tasks') && (
|
||||
<div className="panel-surface p-5 space-y-4">
|
||||
<div className="border-b border-border pb-2.5">
|
||||
<h2 className="text-sm font-bold text-main flex items-center gap-2">
|
||||
<CheckSquare className="w-4 h-4 text-accent" />
|
||||
<span>Aufgaben, Listen & Team-Zusammenarbeit</span>
|
||||
</h2>
|
||||
<p className="text-xs text-muted mt-0.5">
|
||||
Verwalte Aufgabenlisten, Einkaufslisten und teile sie mit Teams und Räumen in Echtzeit.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 text-xs">
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-2">
|
||||
<h3 className="font-semibold text-main flex items-center gap-1.5">
|
||||
<Share2 className="w-3.5 h-3.5 text-accent" />
|
||||
<span>Aufgabenlisten teilen & Avatar-Stack</span>
|
||||
</h3>
|
||||
<p className="text-muted leading-relaxed">
|
||||
Jede Aufgabenliste kann über den Button <strong>„Teilen"</strong> einem gemeinsamen Team-Space zugeordnet werden.
|
||||
Im Header und auf den Tabs zeigt der Avatar-Stack sofort an, wer Eigentümer ist und welche Teammitglieder Zugriff haben.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-canvas border border-border rounded-card space-y-2">
|
||||
<h3 className="font-semibold text-main flex items-center gap-1.5">
|
||||
<CheckSquare className="w-3.5 h-3.5 text-accent" />
|
||||
<span>Globale To-Dos Rollup</span>
|
||||
</h3>
|
||||
<p className="text-muted leading-relaxed">
|
||||
Im Workspace-Tab <strong>„Globale To-Dos"</strong> werden automatisch alle offenen Checkboxen (<code className="text-accent bg-panel px-1 py-0.5 rounded font-mono">- [ ]</code>) aus sämtlichen Notizen und Ordnern aggregiert.
|
||||
Ein Klick auf die Checkbox synchronisiert die Aufgabe in Echtzeit direkt in der physischen Markdown-Datei!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SECTION: SHORTCUTS CHEAT SHEET */}
|
||||
{(activeCategory === 'all' || activeCategory === 'shortcuts') && (
|
||||
<div className="panel-surface p-5 space-y-4">
|
||||
<div className="border-b border-border pb-2.5">
|
||||
<h2 className="text-sm font-bold text-main flex items-center gap-2">
|
||||
<Keyboard className="w-4 h-4 text-accent" />
|
||||
<span>Tastatur-Kürzel Gesamtübersicht</span>
|
||||
</h2>
|
||||
<p className="text-xs text-muted mt-0.5">
|
||||
Schneller arbeiten ohne Maus: Alle Hotkeys auf einen Blick.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-xs border-collapse">
|
||||
<thead>
|
||||
<tr className="border-b border-border text-muted">
|
||||
<th className="py-2 px-3 font-semibold">Taste</th>
|
||||
<th className="py-2 px-3 font-semibold">Bereich</th>
|
||||
<th className="py-2 px-3 font-semibold">Funktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border text-main">
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">C oder /</td>
|
||||
<td className="py-2 px-3 text-muted">Global</td>
|
||||
<td className="py-2 px-3">Fokus sofort in die Schnellerfassungsleiste setzen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Enter</td>
|
||||
<td className="py-2 px-3 text-muted">Schnellerfassung</td>
|
||||
<td className="py-2 px-3">Gedanken sofort speichern</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Esc</td>
|
||||
<td className="py-2 px-3 text-muted">Schnellerfassung</td>
|
||||
<td className="py-2 px-3">Eingabe leeren & Menüs schließen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Enter</td>
|
||||
<td className="py-2 px-3 text-muted">Workspace Outliner</td>
|
||||
<td className="py-2 px-3">Neuen Bullet-Point auf gleicher Ebene anlegen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Tab</td>
|
||||
<td className="py-2 px-3 text-muted">Workspace Outliner</td>
|
||||
<td className="py-2 px-3">Bullet-Point einrücken (dem vorherigen unterordnen)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Shift + Tab</td>
|
||||
<td className="py-2 px-3 text-muted">Workspace Outliner</td>
|
||||
<td className="py-2 px-3">Bullet-Point ausrücken (Ebene verringern)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Shift + Enter</td>
|
||||
<td className="py-2 px-3 text-muted">Workspace Outliner</td>
|
||||
<td className="py-2 px-3">Weicher Zeilenumbruch innerhalb des aktuellen Blocks</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Backspace</td>
|
||||
<td className="py-2 px-3 text-muted">Workspace Outliner</td>
|
||||
<td className="py-2 px-3">Bei leerem Block: Ebene ausrücken bzw. Bullet löschen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">/</td>
|
||||
<td className="py-2 px-3 text-muted">Workspace Outliner</td>
|
||||
<td className="py-2 px-3">Slash-Command-Menü öffnen (/todo, /h1, /code, ...)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">H oder ←</td>
|
||||
<td className="py-2 px-3 text-muted">Triage-Deck</td>
|
||||
<td className="py-2 px-3">Gedanken verwerfen / erledigen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">L oder →</td>
|
||||
<td className="py-2 px-3 text-muted">Triage-Deck</td>
|
||||
<td className="py-2 px-3">Gedanken sofort zur Aufgabe machen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">T</td>
|
||||
<td className="py-2 px-3 text-muted">Triage-Deck</td>
|
||||
<td className="py-2 px-3">Aufgabe mit Fälligkeitsdatum anlegen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">D</td>
|
||||
<td className="py-2 px-3 text-muted">Triage-Deck</td>
|
||||
<td className="py-2 px-3">Gedanken in Notiz/Dokument umwandeln</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="py-2 px-3 font-mono text-accent">Leertaste</td>
|
||||
<td className="py-2 px-3 text-muted">Triage-Deck</td>
|
||||
<td className="py-2 px-3">Karte überspringen („Später" – ans Ende schieben)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user