feat(workspace): sliding sidebar in focus mode, calmer workspace bar, smoother journal
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m47s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m47s
- focus mode: folders slide in from the left (button, title click, or the left edge on wide screens); picking a note keeps focus mode - workspace bar: borderless live/shared/save pills in one style, divider before the note actions; footer without top line - journal panel and calendar open with an animation, the view fades in - paste and markdown import nest a list under the paragraph introducing it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
226c863a2a
commit
b136e5dfad
9 files changed
+183
-60
No files matched your search
@@ -24,9 +24,9 @@ describe('parsePastedLines', () => {
|
||||
expect(parse('# Titel\nText danach\n- [x] erledigt\n* Punkt\n + tiefer\n---\nOhne Abschnitt')).toEqual([
|
||||
{ type: 'h1', content: 'Titel', indent: 0 },
|
||||
{ type: 'text', content: 'Text danach', indent: 1 },
|
||||
{ type: 'todo', checked: true, content: 'erledigt', indent: 1 },
|
||||
{ type: 'text', content: 'Punkt', indent: 1 },
|
||||
{ type: 'text', content: 'tiefer', indent: 2 },
|
||||
{ type: 'todo', checked: true, content: 'erledigt', indent: 2 },
|
||||
{ type: 'text', content: 'Punkt', indent: 2 },
|
||||
{ type: 'text', content: 'tiefer', indent: 3 },
|
||||
{ type: 'divider', content: '---', indent: 0 },
|
||||
{ type: 'text', content: 'Ohne Abschnitt', indent: 0 },
|
||||
]);
|
||||
@@ -49,4 +49,17 @@ describe('parsePastedLines', () => {
|
||||
expect(items[0].content).toBe('```go\nfunc main() {}\n\n```');
|
||||
expect(items[1]).toEqual({ type: 'text', content: 'Danach', indent: 0 });
|
||||
});
|
||||
|
||||
it('nests a list under the paragraph that introduces it', () => {
|
||||
expect(parse('# Plan\nFolgende Punkte:\n\n- eins\n - tiefer\n- zwei\n\nDanach Text\n1. a\n2. b')).toEqual([
|
||||
{ type: 'h1', content: 'Plan', indent: 0 },
|
||||
{ type: 'text', content: 'Folgende Punkte:', indent: 1 },
|
||||
{ type: 'text', content: 'eins', indent: 2 },
|
||||
{ type: 'text', content: 'tiefer', indent: 3 },
|
||||
{ type: 'text', content: 'zwei', indent: 2 },
|
||||
{ type: 'text', content: 'Danach Text', indent: 1 },
|
||||
{ type: 'numbered', content: 'a', indent: 2 },
|
||||
{ type: 'numbered', content: 'b', indent: 2 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -33,6 +33,11 @@ export function parsePastedLines(rawLines: string[]): PastedItem[] {
|
||||
let inCode = false;
|
||||
let afterHeading = false;
|
||||
const headings: { rank: number; level: number }[] = []; // open headings, innermost last
|
||||
// A list right after a paragraph ("Folgende Punkte:") nests under it: level of that paragraph,
|
||||
// and the extra indent of the current list run
|
||||
let paragraphLevel: number | null = null;
|
||||
let listBoost = 0;
|
||||
let inList = false;
|
||||
const sectionLevel = () => (headings.length ? headings[headings.length - 1].level + 1 : 0);
|
||||
|
||||
const flush = () => {
|
||||
@@ -57,6 +62,8 @@ export function parsePastedLines(rawLines: string[]): PastedItem[] {
|
||||
flush();
|
||||
current = { type: 'text', content: rawLine, indent: sectionLevel() };
|
||||
afterHeading = false;
|
||||
paragraphLevel = null;
|
||||
inList = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -74,6 +81,8 @@ export function parsePastedLines(rawLines: string[]): PastedItem[] {
|
||||
if (heading) {
|
||||
flush();
|
||||
afterHeading = true;
|
||||
paragraphLevel = null;
|
||||
inList = false;
|
||||
const rank = heading[1].length;
|
||||
while (headings.length && headings[headings.length - 1].rank >= rank) headings.pop();
|
||||
const level = sectionLevel();
|
||||
@@ -85,6 +94,8 @@ export function parsePastedLines(rawLines: string[]): PastedItem[] {
|
||||
flush();
|
||||
afterHeading = false;
|
||||
headings.length = 0; // a divider closes the sections
|
||||
paragraphLevel = null;
|
||||
inList = false;
|
||||
items.push({ type: 'divider', content: '---', indent: 0 });
|
||||
continue;
|
||||
}
|
||||
@@ -95,11 +106,16 @@ export function parsePastedLines(rawLines: string[]): PastedItem[] {
|
||||
if (todo || bullet || numbered) {
|
||||
flush();
|
||||
afterHeading = false;
|
||||
if (!inList) {
|
||||
inList = true;
|
||||
listBoost = paragraphLevel !== null && indent <= paragraphLevel ? paragraphLevel + 1 - indent : 0;
|
||||
}
|
||||
const level = indent + listBoost;
|
||||
current = todo
|
||||
? { type: 'todo', checked: todo[1].toLowerCase() === 'x', content: todo[2], indent }
|
||||
? { type: 'todo', checked: todo[1].toLowerCase() === 'x', content: todo[2], indent: level }
|
||||
: numbered
|
||||
? { type: 'numbered', content: numbered[1], indent }
|
||||
: { type: 'text', content: bullet![1], indent };
|
||||
? { type: 'numbered', content: numbered[1], indent: level }
|
||||
: { type: 'text', content: bullet![1], indent: level };
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -109,6 +125,8 @@ export function parsePastedLines(rawLines: string[]): PastedItem[] {
|
||||
} else {
|
||||
flush();
|
||||
current = { type: 'text', content: line, indent };
|
||||
paragraphLevel = indent;
|
||||
inList = false;
|
||||
}
|
||||
afterHeading = false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user