style: folders without arrow, hashtag row cut off with an expander
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m53s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m53s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6fb98d4002
commit
3de6c6088b
6 files changed
+53
-16
No files matched your search
+8
-1
@@ -6,6 +6,12 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.45.1] - 2026-11-01
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Workspace: Ordner haben keinen Aufklapp-Pfeil mehr; das offene bzw. geschlossene Ordnersymbol zeigt den Zustand. Leere und gefüllte Ordner stehen damit bündig, Notizen in einem Ordner sind gleich weit eingerückt wie Unterordner.
|
||||||
|
- Aufgaben am PC: Die Hashtag-Zeile scrollt nicht mehr seitlich. Passen nicht alle Hashtags hinein, endet sie mit „…“; ein Klick darauf zeigt alle in mehreren Zeilen, „Weniger“ klappt sie wieder ein.
|
||||||
|
|
||||||
## [0.45.0] - 2026-11-01
|
## [0.45.0] - 2026-11-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -728,7 +734,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
|||||||
- Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken.
|
- Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken.
|
||||||
- Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung.
|
- Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung.
|
||||||
|
|
||||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.45.0...dev
|
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.45.1...dev
|
||||||
|
[0.45.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.45.0...v0.45.1
|
||||||
[0.45.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.44.0...v0.45.0
|
[0.45.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.44.0...v0.45.0
|
||||||
[0.44.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.43.0...v0.44.0
|
[0.44.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.43.0...v0.44.0
|
||||||
[0.43.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.42.0...v0.43.0
|
[0.43.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.42.0...v0.43.0
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
[](#)
|
[](#)
|
||||||
[](#-schnellstart)
|
[](#-schnellstart)
|
||||||
[](#-deine-daten-gehören-dir)
|
[](#-deine-daten-gehören-dir)
|
||||||
[](CHANGELOG.md)
|
[](CHANGELOG.md)
|
||||||
|
|
||||||
**MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server.
|
**MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server.
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mindsift-frontend",
|
"name": "mindsift-frontend",
|
||||||
"version": "0.45.0",
|
"version": "0.45.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mindsift-frontend",
|
"name": "mindsift-frontend",
|
||||||
"version": "0.45.0",
|
"version": "0.45.1",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/inter": "5.3.0",
|
"@fontsource-variable/inter": "5.3.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "mindsift-frontend",
|
"name": "mindsift-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.45.0",
|
"version": "0.45.1",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
|||||||
|
|
||||||
// Hashtag filtering, grouping and sorting state
|
// Hashtag filtering, grouping and sorting state
|
||||||
const [selectedTag, setSelectedTag] = useState<string | null>(null);
|
const [selectedTag, setSelectedTag] = useState<string | null>(null);
|
||||||
|
// Hashtag row on desktop: whether it is cut off, and whether it is opened up into several lines
|
||||||
|
const tagRowRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [tagsExpanded, setTagsExpanded] = useState(false);
|
||||||
|
const [tagsOverflow, setTagsOverflow] = useState(false);
|
||||||
const [groupBy, setGroupBy] = useState<'none' | 'tag' | 'status' | 'priority' | 'due'>('none');
|
const [groupBy, setGroupBy] = useState<'none' | 'tag' | 'status' | 'priority' | 'due'>('none');
|
||||||
const [sortBy, setSortBy] = useState<'custom' | 'created' | 'due' | 'priority' | 'alpha'>('custom');
|
const [sortBy, setSortBy] = useState<'custom' | 'created' | 'due' | 'priority' | 'alpha'>('custom');
|
||||||
|
|
||||||
@@ -491,6 +495,17 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
|||||||
return Array.from(set).sort();
|
return Array.from(set).sort();
|
||||||
}, [safeTasks]);
|
}, [safeTasks]);
|
||||||
|
|
||||||
|
// Cut off? Measured while the row is one line (on resize and when the tags change)
|
||||||
|
useEffect(() => {
|
||||||
|
const el = tagRowRef.current;
|
||||||
|
if (!el || tagsExpanded) return;
|
||||||
|
const measure = () => setTagsOverflow(el.scrollWidth > el.clientWidth + 1);
|
||||||
|
measure();
|
||||||
|
const ro = new ResizeObserver(measure);
|
||||||
|
ro.observe(el);
|
||||||
|
return () => ro.disconnect();
|
||||||
|
}, [allAvailableTags, tagsExpanded, viewMode]);
|
||||||
|
|
||||||
// Filtered tasks for List view
|
// Filtered tasks for List view
|
||||||
const openTasks = safeTasks.filter((t) => !t.isCompleted);
|
const openTasks = safeTasks.filter((t) => !t.isCompleted);
|
||||||
const doneTasks = safeTasks.filter((t) => t.isCompleted);
|
const doneTasks = safeTasks.filter((t) => t.isCompleted);
|
||||||
@@ -2055,8 +2070,11 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
|||||||
|
|
||||||
{/* Hashtag Filter & Grouping / Sorting Toolbar (Desktop only - on mobile accessible via Logo Menu Filter & Sort) */}
|
{/* Hashtag Filter & Grouping / Sorting Toolbar (Desktop only - on mobile accessible via Logo Menu Filter & Sort) */}
|
||||||
<div className="hidden sm:flex flex-wrap items-center justify-between gap-2 pt-1 pb-0.5">
|
<div className="hidden sm:flex flex-wrap items-center justify-between gap-2 pt-1 pb-0.5">
|
||||||
{/* Hashtag filter chips */}
|
{/* Hashtag filter chips: one line, cut off with "…" when too many; "…" opens them all in lines */}
|
||||||
<div className="flex items-center gap-1.5 overflow-x-auto max-w-full pb-1">
|
<div
|
||||||
|
ref={tagRowRef}
|
||||||
|
className={`flex-1 min-w-0 flex items-center gap-1.5 pb-1 ${tagsExpanded ? 'flex-wrap' : 'overflow-hidden'}`}
|
||||||
|
>
|
||||||
<span className="text-[11px] font-semibold text-muted flex items-center gap-1 flex-shrink-0">
|
<span className="text-[11px] font-semibold text-muted flex items-center gap-1 flex-shrink-0">
|
||||||
<Hash className="w-3 h-3 text-accent" />
|
<Hash className="w-3 h-3 text-accent" />
|
||||||
<span>Hashtags:</span>
|
<span>Hashtags:</span>
|
||||||
@@ -2089,7 +2107,25 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{tagsExpanded && (
|
||||||
|
<button
|
||||||
|
onClick={() => setTagsExpanded(false)}
|
||||||
|
className="h-6 px-2 text-[11px] rounded-pill text-accent hover:underline flex-shrink-0"
|
||||||
|
>
|
||||||
|
Weniger
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{!tagsExpanded && tagsOverflow && (
|
||||||
|
<button
|
||||||
|
onClick={() => setTagsExpanded(true)}
|
||||||
|
className="h-6 px-2 -ml-1 mb-1 text-xs rounded-pill border border-border bg-canvas text-muted hover:text-main flex-shrink-0"
|
||||||
|
title="Alle Hashtags zeigen"
|
||||||
|
aria-label="Alle Hashtags zeigen"
|
||||||
|
>
|
||||||
|
…
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Kanban: manage columns and width (the list view has grouping and sorting above the list) */}
|
{/* Kanban: manage columns and width (the list view has grouping and sorting above the list) */}
|
||||||
<div className="flex items-center gap-2 flex-shrink-0 ml-auto">
|
<div className="flex items-center gap-2 flex-shrink-0 ml-auto">
|
||||||
|
|||||||
@@ -164,13 +164,7 @@ const FolderTreeItem: React.FC<FolderTreeItemProps> = ({
|
|||||||
}
|
}
|
||||||
className="flex items-center gap-1.5 flex-1 min-w-0 text-left h-full cursor-pointer"
|
className="flex items-center gap-1.5 flex-1 min-w-0 text-left h-full cursor-pointer"
|
||||||
>
|
>
|
||||||
{hasContent ? (
|
{/* No arrow: the open/closed folder icon shows the state, and empty folders line up the same */}
|
||||||
isExpanded ? (
|
|
||||||
<ChevronDown className="w-3.5 h-3.5 text-faint shrink-0" />
|
|
||||||
) : (
|
|
||||||
<ChevronRight className="w-3.5 h-3.5 text-faint shrink-0" />
|
|
||||||
)
|
|
||||||
) : null}
|
|
||||||
{isExpanded ? (
|
{isExpanded ? (
|
||||||
<FolderOpen className="w-3.5 h-3.5 text-accent shrink-0" />
|
<FolderOpen className="w-3.5 h-3.5 text-accent shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
@@ -271,7 +265,7 @@ const FolderTreeItem: React.FC<FolderTreeItemProps> = ({
|
|||||||
{...docDropProps(d.id)}
|
{...docDropProps(d.id)}
|
||||||
onMouseDown={(e) => e.shiftKey && e.preventDefault()}
|
onMouseDown={(e) => e.shiftKey && e.preventDefault()}
|
||||||
onClick={(e) => onDocRowClick(e, d.id, () => onSelectDoc(d.id))}
|
onClick={(e) => onDocRowClick(e, d.id, () => onSelectDoc(d.id))}
|
||||||
style={{ paddingLeft: `${(depth + 1) * 12 + 16}px` }}
|
style={{ paddingLeft: `${(depth + 1) * 12 + 9}px` }}
|
||||||
className={`w-full flex items-center gap-2 pr-2 py-1.5 min-h-11 sm:min-h-0 rounded-btn text-left text-sm sm:text-xs cursor-pointer border transition-colors ${
|
className={`w-full flex items-center gap-2 pr-2 py-1.5 min-h-11 sm:min-h-0 rounded-btn text-left text-sm sm:text-xs cursor-pointer border transition-colors ${
|
||||||
mergeHoverId === d.id
|
mergeHoverId === d.id
|
||||||
? 'border-accent bg-accent/15 ring-2 ring-accent/30 text-main'
|
? 'border-accent bg-accent/15 ring-2 ring-accent/30 text-main'
|
||||||
@@ -289,7 +283,7 @@ const FolderTreeItem: React.FC<FolderTreeItemProps> = ({
|
|||||||
|
|
||||||
{!hasContent && (
|
{!hasContent && (
|
||||||
<div
|
<div
|
||||||
style={{ paddingLeft: `${(depth + 1) * 12 + 16}px` }}
|
style={{ paddingLeft: `${(depth + 1) * 12 + 9}px` }}
|
||||||
className="py-1 text-[10px] text-faint italic"
|
className="py-1 text-[10px] text-faint italic"
|
||||||
>
|
>
|
||||||
Leer
|
Leer
|
||||||
|
|||||||
Reference in new issue
Block a user