feat(workspace): journal panel toggle, view kept across tabs, sections nest under headings
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m9s

- the journal opens as a framed panel and closes on a second click,
  returning to the previous note; journal state and the last regular note
  survive tab switches, the fallback note is never a journal day
- pasted and imported markdown nests content under its heading (H2 below
  H1), Enter on a heading creates a child, '- ' / '* ' / '+ ' start a bullet

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-30 20:11:09 +01:00
1 parent bc7bdc481e
commit 375dda982b
9 files changed
+145 -36

No files matched your search

+15 -6
View File
@@ -740,9 +740,10 @@ func indentWidth(s string) int {
func MarkdownToBlocks(md string) []domain.DocumentBlock {
lines := strings.Split(md, "\n")
var blocks []domain.DocumentBlock
base := 0 // 1 inside a heading section
var indents []int // indent widths of the open list levels
inList := false // the previous line belonged to a list item (for continuation lines)
base := 0 // level of content in the current heading section
var headings []int // ranks (1–3) of the open headings: H2 below H1 nests one level deeper
var indents []int // indent widths of the open list levels
inList := false // the previous line belonged to a list item (for continuation lines)
listLevel := func(width int) int {
for len(indents) > 0 && indents[len(indents)-1] > width {
indents = indents[:len(indents)-1]
@@ -789,15 +790,23 @@ func MarkdownToBlocks(md string) []domain.DocumentBlock {
if typ == "" {
typ = "h3"
}
blocks = append(blocks, newBlock(typ, strings.TrimSpace(m[2]), 0))
base, indents, inList = 1, nil, false
rank := len(m[1])
if rank > 3 {
rank = 3
}
for len(headings) > 0 && headings[len(headings)-1] >= rank {
headings = headings[:len(headings)-1]
}
blocks = append(blocks, newBlock(typ, strings.TrimSpace(m[2]), len(headings)))
headings = append(headings, rank)
base, indents, inList = len(headings), nil, false
continue
}
if trimmed == "---" || trimmed == "***" || trimmed == "___" {
if !strings.HasPrefix(line, " ") && !strings.HasPrefix(line, "\t") {
blocks = append(blocks, newBlock("divider", "", 0))
base, indents, inList = 0, nil, false
base, headings, indents, inList = 0, nil, nil, false
continue
}
}
+16
View File
@@ -4,6 +4,7 @@ import (
"archive/zip"
"bytes"
"encoding/json"
"fmt"
"path/filepath"
"strings"
"testing"
@@ -219,3 +220,18 @@ func TestImportBackupZip(t *testing.T) {
}
checkRestored(t, store, ben)
}
func TestMarkdownHeadingsNestTheirSections(t *testing.T) {
blocks := MarkdownToBlocks("# Projekt\nEinleitung\n## Teil A\n- Punkt\n - Unterpunkt\n## Teil B\n1. Erstens\n# Anhang\nText")
var got []string
for _, b := range blocks {
got = append(got, fmt.Sprintf("%d:%s:%s", b.Level, b.Type, b.Content))
}
want := []string{
"0:h1:Projekt", "1:bullet:Einleitung", "1:h2:Teil A", "2:bullet:Punkt", "3:bullet:Unterpunkt",
"1:h2:Teil B", "2:numbered:Erstens", "0:h1:Anhang", "1:bullet:Text",
}
if strings.Join(got, "|") != strings.Join(want, "|") {
t.Fatalf("got %v\nwant %v", got, want)
}
}