From 938cdeb27bc5957f27e1e7a5c9af69f84075d374 Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Mon, 17 Nov 2025 14:59:32 +0100 Subject: [PATCH] LENZ BRIEFAUSGABE --- combin.txt | 321 -------------------------------- controllers/brief.go | 14 +- controllers/uebersicht.go | 169 ++++++++++++++++- out.pdf | Bin 13641 -> 0 bytes views/assets/style.css | 215 ++++++++++++++++++++- views/routes/brief/body.gohtml | 21 ++- views/routes/briefe/body.gohtml | 225 ++++++++++++++++++---- views/transform/site.css | 3 +- xmlmodels/letter_text.go | 212 ++++++++++++++++----- xmlmodels/letter_text_test.go | 23 +++ xmlmodels/meta.go | 52 ++++++ 11 files changed, 842 insertions(+), 413 deletions(-) delete mode 100644 combin.txt delete mode 100644 out.pdf create mode 100644 xmlmodels/letter_text_test.go diff --git a/combin.txt b/combin.txt deleted file mode 100644 index a6b3a7d..0000000 --- a/combin.txt +++ /dev/null @@ -1,321 +0,0 @@ -opus -opus/document -opus/document/letterText -opus/document/letterText/address -opus/document/letterText/address/align -opus/document/letterText/address/align/line -opus/document/letterText/address/align/line/@type -opus/document/letterText/address/align/@pos -opus/document/letterText/address/align/ul -opus/document/letterText/address/aq -opus/document/letterText/address/aq/line -opus/document/letterText/address/aq/line/@type -opus/document/letterText/address/aq/ru -opus/document/letterText/address/aq/ru/line -opus/document/letterText/address/aq/ru/line/@type -opus/document/letterText/address/aq/ul -opus/document/letterText/address/del -opus/document/letterText/address/dul -opus/document/letterText/address/line -opus/document/letterText/address/line/@type -opus/document/letterText/address/sidenote -opus/document/letterText/address/sidenote/@annotation -opus/document/letterText/address/sidenote/aq -opus/document/letterText/address/sidenote/@page -opus/document/letterText/address/sidenote/@pos -opus/document/letterText/address/tl -opus/document/letterText/address/ul -opus/document/letterText/address/ul/aq -opus/document/letterText/align -opus/document/letterText/align/align -opus/document/letterText/align/align/@pos -opus/document/letterText/align/aq -opus/document/letterText/align/aq/fn -opus/document/letterText/align/aq/fn/anchor -opus/document/letterText/align/aq/fn/@index -opus/document/letterText/align/del -opus/document/letterText/align/del/nr -opus/document/letterText/align/fn -opus/document/letterText/align/fn/anchor -opus/document/letterText/align/fn/@index -opus/document/letterText/align/gr -opus/document/letterText/align/hand -opus/document/letterText/align/hand/line -opus/document/letterText/align/hand/line/@type -opus/document/letterText/align/hand/@ref -opus/document/letterText/align/insertion -opus/document/letterText/align/insertion/@pos -opus/document/letterText/align/it -opus/document/letterText/align/line -opus/document/letterText/align/line/@tab -opus/document/letterText/align/line/@type -opus/document/letterText/align/note -opus/document/letterText/align/page -opus/document/letterText/align/page/@index -opus/document/letterText/align/@pos -opus/document/letterText/align/ru -opus/document/letterText/align/ru/line -opus/document/letterText/align/ru/line/@type -opus/document/letterText/align/ul -opus/document/letterText/aq -opus/document/letterText/aq/align -opus/document/letterText/aq/align/line -opus/document/letterText/aq/align/line/@type -opus/document/letterText/aq/align/@pos -opus/document/letterText/aq/del -opus/document/letterText/aq/del/nr -opus/document/letterText/aq/dul -opus/document/letterText/aq/gr -opus/document/letterText/aq/insertion -opus/document/letterText/aq/insertion/@pos -opus/document/letterText/aq/line -opus/document/letterText/aq/line/@tab -opus/document/letterText/aq/line/@type -opus/document/letterText/aq/note -opus/document/letterText/aq/nr -opus/document/letterText/aq/page -opus/document/letterText/aq/page/@index -opus/document/letterText/aq/pe -opus/document/letterText/aq/pe/line -opus/document/letterText/aq/pe/line/@tab -opus/document/letterText/aq/pe/note -opus/document/letterText/aq/pe/page -opus/document/letterText/aq/pe/page/@index -opus/document/letterText/aq/pe/tl -opus/document/letterText/aq/ru -opus/document/letterText/aq/ru/ul -opus/document/letterText/aq/sidenote -opus/document/letterText/aq/sidenote/@annotation -opus/document/letterText/aq/sidenote/del -opus/document/letterText/aq/sidenote/insertion -opus/document/letterText/aq/sidenote/insertion/@pos -opus/document/letterText/aq/sidenote/@page -opus/document/letterText/aq/sidenote/pe -opus/document/letterText/aq/sidenote/@pos -opus/document/letterText/aq/tl -opus/document/letterText/aq/ul -opus/document/letterText/aq/ul/ru -opus/document/letterText/b -opus/document/letterText/del -opus/document/letterText/del/del -opus/document/letterText/del/del/nr -opus/document/letterText/del/line -opus/document/letterText/del/line/@tab -opus/document/letterText/del/line/@type -opus/document/letterText/del/nr -opus/document/letterText/dul -opus/document/letterText/er -opus/document/letterText/er/nr -opus/document/letterText/fn -opus/document/letterText/fn/anchor -opus/document/letterText/fn/anchor/note -opus/document/letterText/fn/@index -opus/document/letterText/gr -opus/document/letterText/hand -opus/document/letterText/hand/address -opus/document/letterText/hand/address/aq -opus/document/letterText/hand/address/line -opus/document/letterText/hand/address/line/@type -opus/document/letterText/hand/address/ul -opus/document/letterText/hand/align -opus/document/letterText/hand/align/line -opus/document/letterText/hand/align/line/@type -opus/document/letterText/hand/align/@pos -opus/document/letterText/hand/aq -opus/document/letterText/hand/del -opus/document/letterText/hand/del/line -opus/document/letterText/hand/del/line/@type -opus/document/letterText/hand/del/nr -opus/document/letterText/hand/fn -opus/document/letterText/hand/fn/anchor -opus/document/letterText/hand/fn/@index -opus/document/letterText/hand/insertion -opus/document/letterText/hand/insertion/@pos -opus/document/letterText/hand/it -opus/document/letterText/hand/line -opus/document/letterText/hand/line/@tab -opus/document/letterText/hand/line/@type -opus/document/letterText/hand/note -opus/document/letterText/hand/nr -opus/document/letterText/hand/page -opus/document/letterText/hand/page/@index -opus/document/letterText/hand/pe -opus/document/letterText/hand/pe/aq -opus/document/letterText/hand/pe/del -opus/document/letterText/hand/pe/del/line -opus/document/letterText/hand/pe/del/line/@type -opus/document/letterText/hand/pe/del/nr -opus/document/letterText/hand/pe/line -opus/document/letterText/hand/pe/line/@type -opus/document/letterText/hand/pe/nr -opus/document/letterText/hand/pe/sidenote -opus/document/letterText/hand/pe/sidenote/@annotation -opus/document/letterText/hand/pe/sidenote/line -opus/document/letterText/hand/pe/sidenote/line/@type -opus/document/letterText/hand/pe/sidenote/@page -opus/document/letterText/hand/pe/sidenote/@pos -opus/document/letterText/hand/pe/ul -opus/document/letterText/hand/@ref -opus/document/letterText/hand/sidenote -opus/document/letterText/hand/sidenote/@annotation -opus/document/letterText/hand/sidenote/line -opus/document/letterText/hand/sidenote/line/@type -opus/document/letterText/hand/sidenote/@page -opus/document/letterText/hand/sidenote/@pos -opus/document/letterText/hand/tabs -opus/document/letterText/hand/tabs/line -opus/document/letterText/hand/tabs/line/@index -opus/document/letterText/hand/tabs/tab -opus/document/letterText/hand/tabs/tab/@value -opus/document/letterText/hand/tl -opus/document/letterText/hand/ul -opus/document/letterText/hand/ul/aq -opus/document/letterText/hb -opus/document/letterText/ink -opus/document/letterText/ink/@ref -opus/document/letterText/insertion -opus/document/letterText/insertion/del -opus/document/letterText/insertion/del/nr -opus/document/letterText/insertion/fn -opus/document/letterText/insertion/fn/anchor -opus/document/letterText/insertion/fn/@index -opus/document/letterText/insertion/line -opus/document/letterText/insertion/line/@tab -opus/document/letterText/insertion/@pos -opus/document/letterText/it -opus/document/letterText/@letter -opus/document/letterText/line -opus/document/letterText/line/@tab -opus/document/letterText/line/@type -opus/document/letterText/note -opus/document/letterText/nr -opus/document/letterText/p -opus/document/letterText/page -opus/document/letterText/page/@index -opus/document/letterText/p/align -opus/document/letterText/p/align/@pos -opus/document/letterText/pe -opus/document/letterText/pe/aq -opus/document/letterText/pe/aq/del -opus/document/letterText/pe/aq/insertion -opus/document/letterText/pe/aq/insertion/@pos -opus/document/letterText/pe/aq/line -opus/document/letterText/pe/aq/line/@tab -opus/document/letterText/pe/aq/line/@type -opus/document/letterText/pe/aq/note -opus/document/letterText/pe/aq/page -opus/document/letterText/pe/aq/page/@index -opus/document/letterText/pe/aq/ul -opus/document/letterText/pe/hand -opus/document/letterText/pe/hand/aq -opus/document/letterText/pe/hand/@ref -opus/document/letterText/pe/line -opus/document/letterText/pe/line/@tab -opus/document/letterText/pe/line/@type -opus/document/letterText/pe/nr -opus/document/letterText/pe/page -opus/document/letterText/pe/page/@index -opus/document/letterText/pe/ul -opus/document/letterText/p/line -opus/document/letterText/p/line/@tab -opus/document/letterText/p/line/@type -opus/document/letterText/p/page -opus/document/letterText/p/page/@index -opus/document/letterText/p/sidenote -opus/document/letterText/p/sidenote/@annotation -opus/document/letterText/p/sidenote/line -opus/document/letterText/p/sidenote/line/@tab -opus/document/letterText/p/sidenote/@page -opus/document/letterText/p/sidenote/@pos -opus/document/letterText/ru -opus/document/letterText/ru/line -opus/document/letterText/ru/line/@type -opus/document/letterText/sidenote -opus/document/letterText/sidenote/align -opus/document/letterText/sidenote/align/gr -opus/document/letterText/sidenote/align/@pos -opus/document/letterText/sidenote/@annotation -opus/document/letterText/sidenote/aq -opus/document/letterText/sidenote/aq/ru -opus/document/letterText/sidenote/del -opus/document/letterText/sidenote/dul -opus/document/letterText/sidenote/fn -opus/document/letterText/sidenote/fn/anchor -opus/document/letterText/sidenote/fn/anchor/note -opus/document/letterText/sidenote/fn/@index -opus/document/letterText/sidenote/gr -opus/document/letterText/sidenote/hand -opus/document/letterText/sidenote/hand/aq -opus/document/letterText/sidenote/hand/aq/line -opus/document/letterText/sidenote/hand/aq/line/@type -opus/document/letterText/sidenote/hand/insertion -opus/document/letterText/sidenote/hand/insertion/nr -opus/document/letterText/sidenote/hand/insertion/@pos -opus/document/letterText/sidenote/hand/line -opus/document/letterText/sidenote/hand/line/@type -opus/document/letterText/sidenote/hand/@ref -opus/document/letterText/sidenote/ink -opus/document/letterText/sidenote/ink/line -opus/document/letterText/sidenote/ink/line/@type -opus/document/letterText/sidenote/ink/@ref -opus/document/letterText/sidenote/ink/ul -opus/document/letterText/sidenote/insertion -opus/document/letterText/sidenote/insertion/@pos -opus/document/letterText/sidenote/line -opus/document/letterText/sidenote/line/@tab -opus/document/letterText/sidenote/line/@type -opus/document/letterText/sidenote/note -opus/document/letterText/sidenote/@page -opus/document/letterText/sidenote/page -opus/document/letterText/sidenote/page/@index -opus/document/letterText/sidenote/pe -opus/document/letterText/sidenote/@pos -opus/document/letterText/sidenote/ru -opus/document/letterText/sidenote/ru/aq -opus/document/letterText/sidenote/ru/aq/ul -opus/document/letterText/sidenote/ul -opus/document/letterText/sidenote/ul/line -opus/document/letterText/sidenote/ul/line/@tab -opus/document/letterText/subst -opus/document/letterText/subst/del -opus/document/letterText/subst/del/nr -opus/document/letterText/subst/del/nr/@extent -opus/document/letterText/subst/insertion -opus/document/letterText/tabs -opus/document/letterText/tabs/line -opus/document/letterText/tabs/line/@index -opus/document/letterText/tabs/line/@tab -opus/document/letterText/tabs/line/@type -opus/document/letterText/tabs/note -opus/document/letterText/tabs/page -opus/document/letterText/tabs/page/@index -opus/document/letterText/tabs/tab -opus/document/letterText/tabs/tab/align -opus/document/letterText/tabs/tab/align/aq -opus/document/letterText/tabs/tab/align/@pos -opus/document/letterText/tabs/tab/aq -opus/document/letterText/tabs/tab/del -opus/document/letterText/tabs/tab/fn -opus/document/letterText/tabs/tab/fn/anchor -opus/document/letterText/tabs/tab/fn/@index -opus/document/letterText/tabs/tab/insertion -opus/document/letterText/tabs/tab/insertion/@pos -opus/document/letterText/tabs/tab/line -opus/document/letterText/tabs/tab/line/@tab -opus/document/letterText/tabs/tab/line/@type -opus/document/letterText/tabs/tab/note -opus/document/letterText/tabs/tab/ul -opus/document/letterText/tabs/tab/ul/aq -opus/document/letterText/tabs/tab/@value -opus/document/letterText/tl -opus/document/letterText/ul -opus/document/letterText/ul/aq -opus/document/letterText/ul/del -opus/document/letterText/ul/fn -opus/document/letterText/ul/fn/anchor -opus/document/letterText/ul/fn/@index -opus/document/letterText/ul/inc -opus/document/letterText/ul/inc/@ref -opus/document/letterText/ul/insertion -opus/document/letterText/ul/insertion/@pos -opus/document/letterText/ul/ru diff --git a/controllers/brief.go b/controllers/brief.go index c3b0658..8e5ff79 100644 --- a/controllers/brief.go +++ b/controllers/brief.go @@ -26,11 +26,19 @@ func GetLetter(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusNotFound) } - html := "" + text := fiber.Map{ + "count": "", + "notes": "", + "pages": []*xmlmodels.PageRender{}, + "html": "", + } if state := letterData.HTML.Data(); state != nil { - html = state.String() + text["html"] = state.String() + text["count"] = state.CountHTML() + text["notes"] = state.NotesHTML() + text["pages"] = state.Pages } tradition := lib.Traditions.Item(letter) - return c.Render("/brief/", fiber.Map{"meta": meta, "text": html, "tradition": tradition, "next": np.Next, "prev": np.Prev}) + return c.Render("/brief/", fiber.Map{"meta": meta, "text": text, "tradition": tradition, "next": np.Next, "prev": np.Prev}) } diff --git a/controllers/uebersicht.go b/controllers/uebersicht.go index 5fc44c9..8a7e6b4 100644 --- a/controllers/uebersicht.go +++ b/controllers/uebersicht.go @@ -1,14 +1,136 @@ package controllers import ( + "encoding/json" + "sort" + "strconv" + "strings" + "github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels" "github.com/gofiber/fiber/v2" ) +type filterItem struct { + ID int `json:"id"` + Name string `json:"name"` +} + func GetLetters(c *fiber.Ctx) error { lib := xmlmodels.Get() _, yearmap := lib.Years() rangeParam := c.Query("range", "") + personParam := strings.TrimSpace(c.Query("person", "")) + placeParam := strings.TrimSpace(c.Query("ort", "")) + + personID := 0 + if personParam != "" { + if id, err := strconv.Atoi(personParam); err == nil && id > 0 { + personID = id + } else { + personParam = "" + } + } + + placeID := 0 + if placeParam != "" { + if id, err := strconv.Atoi(placeParam); err == nil && id > 0 { + placeID = id + } else { + placeParam = "" + } + } + + rawQuery := string(c.Context().URI().QueryString()) + lastPerson := strings.LastIndex(rawQuery, "person=") + lastPlace := strings.LastIndex(rawQuery, "ort=") + if lastPerson >= 0 && lastPlace >= 0 { + if lastPlace > lastPerson { + personID = 0 + personParam = "" + } else { + placeID = 0 + placeParam = "" + } + } + + if personID > 0 && placeID > 0 { + // Fallback if we couldn't determine query order. + placeID = 0 + placeParam = "" + } + + personOptions := make(map[int]filterItem) + placeOptions := make(map[int]filterItem) + + addPerson := func(id int) { + if id <= 0 { + return + } + if _, ok := personOptions[id]; ok { + return + } + if def := lib.Person(id); def != nil { + name := strings.TrimSpace(def.Name) + if name == "" { + name = strings.TrimSpace(def.FirstName + " " + def.LastName) + } + if name == "" { + name = strings.TrimSpace(def.Ref) + } + if name == "" { + name = "Person " + strconv.Itoa(id) + } + personOptions[id] = filterItem{ID: id, Name: name} + } + } + + addPlace := func(id int) { + if id <= 0 { + return + } + if _, ok := placeOptions[id]; ok { + return + } + if def := lib.Place(id); def != nil { + name := strings.TrimSpace(def.Name) + if name == "" { + name = strings.TrimSpace(def.Ref) + } + if name == "" { + name = "Ort " + strconv.Itoa(id) + } + placeOptions[id] = filterItem{ID: id, Name: name} + } + } + + collectRefs := func(meta xmlmodels.Meta) { + for _, action := range meta.Sent { + for _, ref := range action.Persons { + addPerson(ref.Reference) + } + for _, ref := range action.Places { + addPlace(ref.Reference) + } + } + for _, action := range meta.Recieved { + for _, ref := range action.Persons { + addPerson(ref.Reference) + } + for _, ref := range action.Places { + addPlace(ref.Reference) + } + } + } + + filterLetter := func(meta xmlmodels.Meta) bool { + if personID > 0 && !meta.HasPerson(personID) { + return false + } + if placeID > 0 && !meta.HasPlace(placeID) { + return false + } + return true + } // TODO: does not work ATM c.Locals("path", c.Path()) @@ -25,12 +147,21 @@ func GetLetters(c *fiber.Ctx) error { for year, letters := range yearmap { for i := range ranges { if year >= ranges[i].Start && year <= ranges[i].End { - ranges[i].Letters = append(ranges[i].Letters, letters...) + for _, letter := range letters { + collectRefs(letter) + if filterLetter(letter) { + ranges[i].Letters = append(ranges[i].Letters, letter) + } + } break } } } + if personID > 0 || placeID > 0 { + rangeParam = "all" + } + // Handle specific range selection selectedRange := -1 for i, r := range ranges { @@ -40,9 +171,41 @@ func GetLetters(c *fiber.Ctx) error { } } + querySuffix := "" + if personParam != "" { + querySuffix += "&person=" + personParam + } + if placeParam != "" { + querySuffix += "&ort=" + placeParam + } + + personList := make([]filterItem, 0, len(personOptions)) + for _, item := range personOptions { + personList = append(personList, item) + } + sort.Slice(personList, func(i, j int) bool { + return strings.ToLower(personList[i].Name) < strings.ToLower(personList[j].Name) + }) + + placeList := make([]filterItem, 0, len(placeOptions)) + for _, item := range placeOptions { + placeList = append(placeList, item) + } + sort.Slice(placeList, func(i, j int) bool { + return strings.ToLower(placeList[i].Name) < strings.ToLower(placeList[j].Name) + }) + + personJSON, _ := json.Marshal(personList) + placeJSON, _ := json.Marshal(placeList) + return c.Render(LETTERS_URL+"/", fiber.Map{ - "ranges": ranges, + "ranges": ranges, "selectedRange": selectedRange, - "all": rangeParam == "all", + "all": rangeParam == "all", + "person": personParam, + "ort": placeParam, + "query": querySuffix, + "personJSON": string(personJSON), + "placeJSON": string(placeJSON), }) } diff --git a/out.pdf b/out.pdf deleted file mode 100644 index c45d17adea93669a6b807146e058a3b30bedd715..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13641 zcma)?V{k9dm*!*R#P`2l(;GXNsZ__{{h}A zX&IEl=G*?!vF7xZ*IVvrCY#^u)AiW-vlC~2&#^gnwI(CeN3IKN`sw7u-p2RvdGd5L zHZ_+1B2dx-wIG%H3xpz6b#XF)z7K;C`~&o(xg5xp#NL#7;&cSB;1E70?1cFwjY+E` z!6#7HM4aDtPvB@rF=;cksGm5n84j2Ah>YDQy_JQ2H zkATm|fyMzop0gjhI)al4ys$(LQJ z_jRPZj@EDf`YIx*%9e$~+NzJGk8>=H5uVCoC<9r39}qvYVQhVxC=!4i9p$RkmL@6z z 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 :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse))); + } + } + .space-y-1\.5 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse))); + } + } + .space-y-2 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); + } + } + .space-y-4 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse))); + } + } + .space-y-5 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse))); + } + } .space-y-6 { :where(& > :not(:last-child)) { --tw-space-y-reverse: 0; @@ -504,6 +577,20 @@ .gap-x-1\.5 { column-gap: calc(var(--spacing) * 1.5); } + .divide-y { + :where(& > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(1px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + } + } + .divide-slate-100 { + :where(& > :not(:last-child)) { + border-color: var(--color-slate-100); + } + } .self-end { align-self: flex-end; } @@ -513,6 +600,9 @@ .justify-self-end { justify-self: flex-end; } + .overflow-y-auto { + overflow-y: auto; + } .rounded { border-radius: 0.25rem; } @@ -542,6 +632,15 @@ .border-gray-300 { border-color: var(--color-gray-300); } + .border-slate-100 { + border-color: var(--color-slate-100); + } + .border-slate-200 { + border-color: var(--color-slate-200); + } + .border-slate-900 { + border-color: var(--color-slate-900); + } .border-t-gray-200 { border-top-color: var(--color-gray-200); } @@ -551,18 +650,30 @@ .bg-gray-900 { background-color: var(--color-gray-900); } + .bg-slate-900 { + background-color: var(--color-slate-900); + } + .bg-white { + background-color: var(--color-white); + } .mask-repeat { mask-repeat: repeat; } .p-2 { padding: calc(var(--spacing) * 2); } + .p-3 { + padding: calc(var(--spacing) * 3); + } .px-0\.5 { padding-inline: calc(var(--spacing) * 0.5); } .px-2 { padding-inline: calc(var(--spacing) * 2); } + .px-3 { + padding-inline: calc(var(--spacing) * 3); + } .px-8 { padding-inline: calc(var(--spacing) * 8); } @@ -581,6 +692,9 @@ .pb-0 { padding-bottom: calc(var(--spacing) * 0); } + .pb-1 { + padding-bottom: calc(var(--spacing) * 1); + } .pb-1\.5 { padding-bottom: calc(var(--spacing) * 1.5); } @@ -593,6 +707,9 @@ .text-justify { text-align: justify; } + .text-left { + text-align: left; + } .align-bottom { vertical-align: bottom; } @@ -632,6 +749,19 @@ font-size: var(--text-xl); line-height: var(--tw-leading, var(--text-xl--line-height)); } + .text-xs { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .text-\[0\.7rem\] { + font-size: 0.7rem; + } + .text-\[0\.65rem\] { + font-size: 0.65rem; + } + .text-\[0\.75rem\] { + font-size: 0.75rem; + } .leading-none { --tw-leading: 1; line-height: 1; @@ -640,10 +770,18 @@ --tw-font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold); } + .font-medium { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } .font-semibold { --tw-font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold); } + .tracking-wide { + --tw-tracking: var(--tracking-wide); + letter-spacing: var(--tracking-wide); + } .text-wrap { text-wrap: wrap; } @@ -669,6 +807,15 @@ .text-gray-800 { color: var(--color-gray-800); } + .text-slate-500 { + color: var(--color-slate-500); + } + .text-slate-600 { + color: var(--color-slate-600); + } + .text-slate-700 { + color: var(--color-slate-700); + } .text-white { color: var(--color-white); } @@ -733,9 +880,17 @@ -webkit-font-smoothing: auto; -moz-osx-font-smoothing: auto; } + .placeholder-slate-400 { + &::placeholder { + color: var(--color-slate-400); + } + } .opacity-0 { opacity: 0%; } + .opacity-70 { + opacity: 70%; + } .opacity-100 { opacity: 100%; } @@ -797,6 +952,20 @@ -webkit-user-select: all; user-select: all; } + .hover\:border-slate-400 { + &:hover { + @media (hover: hover) { + border-color: var(--color-slate-400); + } + } + } + .hover\:bg-slate-100 { + &:hover { + @media (hover: hover) { + background-color: var(--color-slate-100); + } + } + } .hover\:text-slate-900 { &:hover { @media (hover: hover) { @@ -811,6 +980,38 @@ } } } + .focus\:border-slate-400 { + &:focus { + border-color: var(--color-slate-400); + } + } + .focus\:ring-1 { + &:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + } + .focus\:ring-slate-400 { + &:focus { + --tw-ring-color: var(--color-slate-400); + } + } + .focus\:outline-none { + &:focus { + --tw-outline-style: none; + outline-style: none; + } + } + .lg\:w-72 { + @media (width >= 64rem) { + width: calc(var(--spacing) * 72); + } + } + .lg\:flex-row { + @media (width >= 64rem) { + flex-direction: row; + } + } .print\:hidden { @media print { display: none; @@ -1293,7 +1494,8 @@ line-height: var(--tw-leading, var(--text-sm--line-height)); color: var(--color-slate-800); } - .text .page { + .text .count .page, + .text .notes .page { visibility: hidden; font-family: var(--font-sans); font-size: var(--text-sm); @@ -1392,6 +1594,11 @@ inherits: false; initial-value: 0; } +@property --tw-divide-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} @property --tw-border-style { syntax: "*"; inherits: false; @@ -1405,6 +1612,10 @@ syntax: "*"; inherits: false; } +@property --tw-tracking { + syntax: "*"; + inherits: false; +} @property --tw-ordinal { syntax: "*"; inherits: false; @@ -1604,9 +1815,11 @@ --tw-skew-x: initial; --tw-skew-y: initial; --tw-space-y-reverse: 0; + --tw-divide-y-reverse: 0; --tw-border-style: solid; --tw-leading: initial; --tw-font-weight: initial; + --tw-tracking: initial; --tw-ordinal: initial; --tw-slashed-zero: initial; --tw-numeric-figure: initial; diff --git a/views/routes/brief/body.gohtml b/views/routes/brief/body.gohtml index 2d93a02..452fc3d 100644 --- a/views/routes/brief/body.gohtml +++ b/views/routes/brief/body.gohtml @@ -30,7 +30,26 @@
- {{- Safe $model.text -}} +
+ {{- Safe $model.text.count -}} +
+
+ {{- if $model.text.pages }} + {{- range $page := $model.text.pages }} +
+ {{- $page.HTML | Safe -}} +
+ {{- end -}} + {{- else }} + {{- Safe $model.text.html -}} + {{- end -}} +
+
+ {{- Safe $model.text.notes -}} +
diff --git a/views/routes/briefe/body.gohtml b/views/routes/briefe/body.gohtml index 325a55e..646b052 100644 --- a/views/routes/briefe/body.gohtml +++ b/views/routes/briefe/body.gohtml @@ -1,45 +1,194 @@ {{ $model := . }} +{{ $query := $model.query }} +{{ $personJSON := Safe .personJSON }} +{{ $placeJSON := Safe .placeJSON }} - - -{{- if .ranges -}} - {{- if ne .selectedRange -1 -}} - {{- $selectedRangeData := index .ranges .selectedRange -}} -
-
Briefe für {{ $selectedRangeData.Label }}
-
({{ len $selectedRangeData.Letters }})
+
+ - {{- if .all -}} - {{- range $range := .ranges -}} - {{- if $range.Letters -}} -
-
-
{{ $range.Label }}
-
({{ len $range.Letters }})
-
- {{ template "_letterlist" $range.Letters -}} +
+ {{- if .ranges -}} + {{- if ne .selectedRange -1 -}} + {{- $selectedRangeData := index .ranges .selectedRange -}} +
+
Briefe für {{ $selectedRangeData.Label }}
+
({{ len $selectedRangeData.Letters }})
+ {{ template "_letterlist" $selectedRangeData.Letters -}} + {{- end -}} + + {{- if .all -}} + {{- range $range := .ranges -}} + {{- if $range.Letters -}} +
+
+
{{ $range.Label }}
+
({{ len $range.Letters }})
+
+ {{ template "_letterlist" $range.Letters -}} +
+ {{- end -}} + {{- end -}} {{- end -}} {{- end -}} - {{- end -}} -{{- end -}} +
+
+ + diff --git a/views/transform/site.css b/views/transform/site.css index d926c13..74459ab 100644 --- a/views/transform/site.css +++ b/views/transform/site.css @@ -405,7 +405,8 @@ @apply text-sm text-slate-800; } - .text .page { + .text .count .page, + .text .notes .page { @apply font-sans text-sm text-slate-600 invisible; } diff --git a/xmlmodels/letter_text.go b/xmlmodels/letter_text.go index f9bacff..ac4724b 100644 --- a/xmlmodels/letter_text.go +++ b/xmlmodels/letter_text.go @@ -23,10 +23,31 @@ type Note struct { Tokens Tokens } +type PageRender struct { + Index string + StartsInline bool + Tokens Tokens + rendered string +} + +func (p *PageRender) HTML() string { + if p == nil { + return "" + } + if p.rendered != "" { + return p.rendered + } + p.rendered = p.Tokens.String() + return p.rendered +} + type LenzParseState struct { Tokens Tokens Notes []Note Count []Note + Pages []*PageRender + currentPage *PageRender + paging bool LC int PC string CloseElement bool @@ -46,9 +67,7 @@ func (s *LenzParseState) String() string { } builder := strings.Builder{} builder.WriteString(outToken{Name: "div", Classes: []string{"count"}, Type: Element}.String()) - for _, c := range s.Count { - builder.WriteString(c.Tokens.String()) - } + builder.WriteString(s.CountHTML()) builder.WriteString(outToken{Name: "div", Classes: []string{"count"}, Type: EndElement}.String()) tokens := s.Tokens @@ -57,18 +76,118 @@ func (s *LenzParseState) String() string { builder.WriteString(tokens.String()) builder.WriteString(outToken{Name: "div", Classes: []string{"notes"}, Type: Element}.String()) - for _, note := range s.Notes { - builder.WriteString(note.Tokens.String()) - } + builder.WriteString(s.NotesHTML()) builder.WriteString(outToken{Name: "div", Classes: []string{"notes"}, Type: EndElement}.String()) s.rendered = builder.String() return s.rendered } +func (s *LenzParseState) CountHTML() string { + if s == nil { + return "" + } + builder := strings.Builder{} + for _, c := range s.Count { + builder.WriteString(c.Tokens.String()) + } + return builder.String() +} + +func (s *LenzParseState) NotesHTML() string { + if s == nil { + return "" + } + builder := strings.Builder{} + for _, note := range s.Notes { + builder.WriteString(note.Tokens.String()) + } + return builder.String() +} + func (s *LenzParseState) AppendNote(note Note) { s.Notes = append(s.Notes, note) } +func (s *LenzParseState) ensureCurrentPage() *PageRender { + if s.currentPage == nil { + s.startPage(s.PC) + } + return s.currentPage +} + +func (s *LenzParseState) startPage(index string) *PageRender { + if index == "" { + index = strconv.Itoa(len(s.Pages) + 1) + } + page := &PageRender{Index: index} + s.Pages = append(s.Pages, page) + s.currentPage = page + s.paging = true + return page +} + +func (s *LenzParseState) currentPageTokens() *Tokens { + if !s.paging { + return nil + } + return &s.ensureCurrentPage().Tokens +} + +func (s *LenzParseState) appendDefaultElement(token *xmlparsing.Token, ids ...string) { + s.Tokens.AppendDefaultElement(token, ids...) + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendDefaultElement(token, ids...) + } +} + +func (s *LenzParseState) appendDivElement(id string, classes ...string) { + s.Tokens.AppendDivElement(id, classes...) + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendDivElement(id, classes...) + } +} + +func (s *LenzParseState) appendEndElement() { + s.Tokens.AppendEndElement() + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendEndElement() + } +} + +func (s *LenzParseState) appendCustomAttribute(name, value string) { + s.Tokens.AppendCustomAttribute(name, value) + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendCustomAttribute(name, value) + } +} + +func (s *LenzParseState) appendLink(href string, classes ...string) { + s.Tokens.AppendLink(href, classes...) + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendLink(href, classes...) + } +} + +func (s *LenzParseState) appendEmptyElement(name string, id string, classes ...string) { + s.Tokens.AppendEmptyElement(name, id, classes...) + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendEmptyElement(name, id, classes...) + } +} + +func (s *LenzParseState) appendText(text string) { + s.Tokens.AppendText(text) + if pageTokens := s.currentPageTokens(); pageTokens != nil { + pageTokens.AppendText(text) + } +} + +func (s *LenzParseState) markCurrentPageInline(inline bool) { + if page := s.ensureCurrentPage(); page != nil { + page.StartsInline = inline + } +} + type LenzTextHandler struct { Lib *Library } @@ -86,54 +205,54 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt switch elem.Name { case "insertion": - ps.Tokens.AppendDefaultElement(elem) - ps.Tokens.AppendDivElement("", "insertion-marker") - ps.Tokens.AppendEndElement() + ps.appendDefaultElement(elem) + ps.appendDivElement("", "insertion-marker") + ps.appendEndElement() case "sidenote": id := randString(8) - ps.Tokens.AppendDefaultElement(elem) + ps.appendDefaultElement(elem) ps.Break = false - ps.Tokens.AppendCustomAttribute("aria-describedby", id) + ps.appendCustomAttribute("aria-describedby", id) if elem.Attributes["annotation"] != "" || elem.Attributes["page"] != "" || elem.Attributes["pos"] != "" { note := Note{Id: id} note.Tokens.AppendDivElement(id, "note-sidenote-meta") - ps.Tokens.AppendDivElement(id, "inline-sidenote-meta") + ps.appendDivElement(id, "inline-sidenote-meta") if elem.Attributes["page"] != "" { note.Tokens.AppendDivElement("", "sidenote-page") note.Tokens.AppendText(elem.Attributes["page"]) note.Tokens.AppendEndElement() - ps.Tokens.AppendDivElement("", "sidenote-page") - ps.Tokens.AppendText(elem.Attributes["page"]) - ps.Tokens.AppendEndElement() + ps.appendDivElement("", "sidenote-page") + ps.appendText(elem.Attributes["page"]) + ps.appendEndElement() } if elem.Attributes["annotation"] != "" { note.Tokens.AppendDivElement("", "sidenote-note") note.Tokens.AppendText(elem.Attributes["annotation"]) note.Tokens.AppendEndElement() - ps.Tokens.AppendDivElement("", "sidenote-note") - ps.Tokens.AppendText(elem.Attributes["annotation"]) - ps.Tokens.AppendEndElement() + ps.appendDivElement("", "sidenote-note") + ps.appendText(elem.Attributes["annotation"]) + ps.appendEndElement() } if elem.Attributes["pos"] != "" { note.Tokens.AppendDivElement("", "sidenote-pos") note.Tokens.AppendText(elem.Attributes["pos"]) note.Tokens.AppendEndElement() - ps.Tokens.AppendDivElement("", "sidenote-pos") - ps.Tokens.AppendText(elem.Attributes["pos"]) - ps.Tokens.AppendEndElement() + ps.appendDivElement("", "sidenote-pos") + ps.appendText(elem.Attributes["pos"]) + ps.appendEndElement() } note.Tokens.AppendEndElement() - ps.Tokens.AppendEndElement() + ps.appendEndElement() ps.AppendNote(note) } case "note": id := randString(8) - ps.Tokens.AppendLink("#"+id, "nanchor-note") - ps.Tokens.AppendEndElement() - ps.Tokens.AppendDivElement(id, "note", "note-note") + ps.appendLink("#"+id, "nanchor-note") + ps.appendEndElement() + ps.appendDivElement(id, "note", "note-note") case "nr": ext := elem.Attributes["extent"] @@ -145,9 +264,9 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt extno = 1 } - ps.Tokens.AppendDefaultElement(elem) + ps.appendDefaultElement(elem) for i := 0; i < extno; i++ { - ps.Tokens.AppendText(" ") + ps.appendText(" ") } case "hand": @@ -167,21 +286,21 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt note.Tokens.AppendText(hand) note.Tokens.AppendEndElement() ps.AppendNote(note) - ps.Tokens.AppendDivElement(id, "inline-hand") - ps.Tokens.AppendText(hand) - ps.Tokens.AppendEndElement() - ps.Tokens.AppendDivElement("", "hand") - ps.Tokens.AppendCustomAttribute("aria-describedby", id) + ps.appendDivElement(id, "inline-hand") + ps.appendText(hand) + ps.appendEndElement() + ps.appendDivElement("", "hand") + ps.appendCustomAttribute("aria-describedby", id) case "line": if val := elem.Attributes["type"]; val != "empty" { ps.LC += 1 if ps.Break { - ps.Tokens.AppendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC)) + ps.appendEmptyElement("br", ps.PC+"-"+strconv.Itoa(ps.LC)) } - ps.Tokens.AppendDefaultElement(elem) + ps.appendDefaultElement(elem) } else { - ps.Tokens.AppendEmptyElement("br", "", "empty") + ps.appendEmptyElement("br", "", "empty") ps.CloseElement = false } ps.LineBreak = true @@ -190,9 +309,10 @@ func (h LenzTextHandler) OnOpenElement(state *xmlparsing.ParseState[*LenzParseSt ps.PC = elem.Attributes["index"] ps.PageBreak = true ps.CloseElement = false + ps.startPage(ps.PC) default: - ps.Tokens.AppendDefaultElement(elem) + ps.appendDefaultElement(elem) } return nil @@ -204,7 +324,7 @@ func (h LenzTextHandler) OnCloseElement(state *xmlparsing.ParseState[*LenzParseS ps.LineBreak = true } if ps.CloseElement { - ps.Tokens.AppendEndElement() + ps.appendEndElement() } else { ps.CloseElement = true } @@ -223,17 +343,19 @@ func (h LenzTextHandler) OnText(state *xmlparsing.ParseState[*LenzParseState], e } if ps.PageBreak && ps.PC != "1" { ps.PageBreak = false + inline := !ps.LineBreak + ps.markCurrentPageInline(inline) note := Note{Id: ps.PC} quality := "outside" - if !ps.LineBreak { + if inline { quality = "inside" } - ps.Tokens.AppendDivElement("", "eanchor-page", "eanchor-page-"+quality) - ps.Tokens.AppendCustomAttribute("aria-describedby", ps.PC) - ps.Tokens.AppendEndElement() - ps.Tokens.AppendDivElement("", "page-counter", "page-"+quality) - ps.Tokens.AppendText(ps.PC) - ps.Tokens.AppendEndElement() + ps.appendDivElement("", "eanchor-page", "eanchor-page-"+quality) + ps.appendCustomAttribute("aria-describedby", ps.PC) + ps.appendEndElement() + ps.appendDivElement("", "page-counter", "page-"+quality) + ps.appendText(ps.PC) + ps.appendEndElement() note.Tokens.AppendDivElement(ps.PC, "page", "page-"+quality) note.Tokens.AppendText(ps.PC) note.Tokens.AppendEndElement() @@ -243,7 +365,7 @@ func (h LenzTextHandler) OnText(state *xmlparsing.ParseState[*LenzParseState], e ps.LineBreak = false } - ps.Tokens.AppendDefaultElement(elem) + ps.appendDefaultElement(elem) return nil } diff --git a/xmlmodels/letter_text_test.go b/xmlmodels/letter_text_test.go new file mode 100644 index 0000000..3ec6402 --- /dev/null +++ b/xmlmodels/letter_text_test.go @@ -0,0 +1,23 @@ +package xmlmodels + +import "testing" + +func TestPageRendering(t *testing.T) { + content := `FirstSecond` + parsed, err := parseText(nil, content) + if err != nil { + t.Fatalf("parse error: %v", err) + } + state := parsed.Data() + for i, page := range state.Pages { + t.Logf("page %d idx=%s html=%q", i, page.Index, page.HTML()) + } + if len(state.Pages) != 2 { + t.Fatalf("expected 2 pages, got %d", len(state.Pages)) + } + for i, page := range state.Pages { + if page.HTML() == "" { + t.Fatalf("page %d empty", i) + } + } +} diff --git a/xmlmodels/meta.go b/xmlmodels/meta.go index 26b7a06..6bbd96d 100644 --- a/xmlmodels/meta.go +++ b/xmlmodels/meta.go @@ -86,6 +86,40 @@ type Action struct { Persons []RefElement `xml:"person"` } +func (m Meta) HasPerson(id int) bool { + if id <= 0 { + return false + } + for _, action := range m.Sent { + if action.HasPerson(id) { + return true + } + } + for _, action := range m.Recieved { + if action.HasPerson(id) { + return true + } + } + return false +} + +func (m Meta) HasPlace(id int) bool { + if id <= 0 { + return false + } + for _, action := range m.Sent { + if action.HasPlace(id) { + return true + } + } + for _, action := range m.Recieved { + if action.HasPlace(id) { + return true + } + } + return false +} + func (a *Action) Earliest() *Date { if len(a.Dates) == 0 { return nil @@ -93,3 +127,21 @@ func (a *Action) Earliest() *Date { return &a.Dates[0] } + +func (a Action) HasPerson(id int) bool { + for _, ref := range a.Persons { + if ref.Reference == id { + return true + } + } + return false +} + +func (a Action) HasPlace(id int) bool { + for _, ref := range a.Places { + if ref.Reference == id { + return true + } + } + return false +}