Added marginal search. Some refactoring

This commit is contained in:
Simon Martens
2022-11-11 02:15:45 +01:00
parent 3327155eb5
commit 8c26cb44d0
24 changed files with 361 additions and 186 deletions

View File

@@ -11,13 +11,13 @@ namespace HaDocument.Models {
string letter, string letter,
string page, string page,
string line, string line,
string elemnt string element
) { ) {
Index = index; Index = index;
Letter = letter; Letter = letter;
Page = page; Page = page;
Line = line; Line = line;
Element = elemnt; Element = element;
} }
} }
} }

View File

@@ -232,7 +232,7 @@ public class APIController : Controller {
continue; continue;
} }
var filename = ""; var filename = string.Empty;
if (hasContentDispositionHeader && contentDisposition != null) { if (hasContentDispositionHeader && contentDisposition != null) {
if (!MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition)) { if (!MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition)) {
ModelState.AddModelError("Error", $"Wrong Content-Dispostion Headers in Multipart Document"); ModelState.AddModelError("Error", $"Wrong Content-Dispostion Headers in Multipart Document");

View File

@@ -96,7 +96,7 @@ public class IndexController : Controller {
.ToList(); .ToList();
} }
private BriefeMetaViewModel _generateMetaViewModel(ILibrary lib, Meta meta) { internal static BriefeMetaViewModel GenerateMetaViewModel(ILibrary lib, Meta meta) {
var hasMarginals = lib.MarginalsByLetter.Contains(meta.Index) ? true : false; var hasMarginals = lib.MarginalsByLetter.Contains(meta.Index) ? true : false;
var senders = meta.Senders.Select(x => lib.Persons[x].Name) ?? new List<string>(); var senders = meta.Senders.Select(x => lib.Persons[x].Name) ?? new List<string>();
var recivers = meta.Receivers.Select(x => lib.Persons[x].Name) ?? new List<string>(); var recivers = meta.Receivers.Select(x => lib.Persons[x].Name) ?? new List<string>();
@@ -146,7 +146,7 @@ public class IndexController : Controller {
letters = metasbyyear letters = metasbyyear
.Where(x => x.Key >= pages[page].StartYear && x.Key <= pages[page].EndYear) .Where(x => x.Key >= pages[page].StartYear && x.Key <= pages[page].EndYear)
.Select(x => (x.Key, x .Select(x => (x.Key, x
.Select(y => _generateMetaViewModel(lib, y)) .Select(y => GenerateMetaViewModel(lib, y))
.OrderBy(x => x.Meta.Sort) .OrderBy(x => x.Meta.Sort)
.ThenBy(x => x.Meta.Order) .ThenBy(x => x.Meta.Order)
.ToList())) .ToList()))

View File

@@ -7,6 +7,11 @@ using HaDocument.Models;
using HaXMLReader.Interfaces; using HaXMLReader.Interfaces;
using System.Collections.Specialized; using System.Collections.Specialized;
using HaWeb.XMLParser; using HaWeb.XMLParser;
using HaWeb.Settings.ParsingState;
using System.Text;
using HaWeb.Settings.ParsingRules;
using System.Linq;//AsParallel, ToList
using System.Collections.Generic;//Dictionary
namespace HaWeb.Controllers; namespace HaWeb.Controllers;
@@ -23,65 +28,188 @@ public class SucheController : Controller {
_lettersForPage = config.GetValue<int>("LettersOnPage"); _lettersForPage = config.GetValue<int>("LettersOnPage");
} }
[Route("/HKB/Suche")] // Letter Search
public IActionResult Index(string search, string category = "letters", int page = 0) { [Route("/HKB/Suche/Briefe/")]
if (search == null) return _error404(); public IActionResult Briefe(string search, int page = 0, bool? comments = false) {
var lib = _lib.GetLibrary(); var lib = _lib.GetLibrary();
if (category == "letters") { // Error checking
if (String.IsNullOrWhiteSpace(search)) if (search == null) return _error404();
return _paginateSendLetters(lib, page, search, SearchResultType.InvalidSearchTerm, null, null); if (String.IsNullOrWhiteSpace(search))
search = search.Trim(); return View("~/Views/HKB/Dynamic/Suche.cshtml", new SucheViewModel(SearchType.Letters, SearchResultType.InvalidSearchTerm, comments, page, null, search, null, null, null, null));
var res = _xmlService.SearchCollection("letters", search, _readerService, null); search = search.Trim();
if (res == null || !res.Any())
return _paginateSendLetters(lib, page, search, SearchResultType.NotFound, null, null);
var ret = res.ToDictionary(
x => x.Index,
x => x.Results
.Select(y => new SearchResult(search, x.Index) { Page = y.Page, Line = y.Line, Preview = y.Preview })
.ToList()
);
var keys = res.Select(x => x.Index).Where(x => lib.Metas.ContainsKey(x)).Select(x => lib.Metas[x]);
var letters = keys.ToLookup(x => x.Sort.Year).OrderBy(x => x.Key).ToList();
return _paginateSendLetters(lib, page, search, SearchResultType.Success, ret, letters); // Letter & comment search and search result creation
} else if (category == "register") { var resletter = _xmlService.SearchCollection("letters", search, _readerService, null);
if (String.IsNullOrWhiteSpace(search)) List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? rescomments = null;
return _paginateSendRegister(lib, page, search, SearchResultType.InvalidSearchTerm, null); if (comments == true)
search = search.Trim(); rescomments = _xmlService.SearchCollection("marginals", search, _readerService, lib);
List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? res = null; // Error checking
if (page == 0) if ((resletter == null || !resletter.Any()) && (rescomments == null || !rescomments.Any()))
res = _xmlService.SearchCollection("register-comments", search, _readerService, lib); return View("~/Views/HKB/Dynamic/Suche.cshtml", new SucheViewModel(SearchType.Letters, SearchResultType.NotFound, comments, page, null, search, null, null, null, null));
if (page == 1)
res = _xmlService.SearchCollection("forschung-comments", search, _readerService, lib);
if (res == null || !res.Any())
return _paginateSendRegister(lib, page, search, SearchResultType.NotFound, null);
return _paginateSendRegister(lib, page, search, SearchResultType.Success, _createComments("neuzeit", res.Select((x) => (x.Index, x.Results.Select((y) => y.Identifier).ToList())).OrderBy(x => x.Index).ToList())); // Metadata aquisition & sorting
List<Meta>? metas = new List<Meta>();
if (resletter != null)
metas.AddRange(
resletter
.Select(x => x.Index)
.Where(x => lib.Metas.ContainsKey(x))
.Select(x => lib.Metas[x])
);
if (rescomments != null)
metas.AddRange(
rescomments
.Where(x => lib.Marginals.ContainsKey(x.Index))
.Select(x => lib.Marginals[x.Index])
.Where(x => lib.Metas.ContainsKey(x.Letter))
.Select(x => lib.Metas[x.Letter])
);
// Return
return _paginateSendLettersComments(lib, page, search, comments, SearchResultType.Success, metas.Distinct().ToList(), resletter, rescomments);
}
// Register & Bibliography Search
[Route("/HKB/Suche/Register/")]
public IActionResult Register(string search) {
var lib = _lib.GetLibrary();
// Error checking
if (search == null) return _error404();
if (String.IsNullOrWhiteSpace(search))
return _paginateSendRegister(lib, search, SearchType.Register, SearchResultType.InvalidSearchTerm, null);
search = search.Trim();
// Search
List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? res = null;
res = _xmlService.SearchCollection("register-comments", search, _readerService, lib);
if (res == null || !res.Any())
return _paginateSendRegister(lib, search, SearchType.Register, SearchResultType.NotFound, null);
// Return
return _paginateSendRegister(lib, search, SearchType.Register, SearchResultType.Success, _createComments("neuzeit", res.Select((x) => (x.Index, x.Results.Select((y) => y.Identifier).ToList())).OrderBy(x => x.Index).ToList()));
}
[Route("/HKB/Suche/Forschung/")]
public IActionResult Science(string search) {
var lib = _lib.GetLibrary();
// Error checking
if (search == null) return _error404();
if (String.IsNullOrWhiteSpace(search))
return _paginateSendRegister(lib, search, SearchType.Science, SearchResultType.InvalidSearchTerm, null);
search = search.Trim();
// Search
List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? res = null;
res = _xmlService.SearchCollection("forschung-comments", search, _readerService, lib);
if (res == null || !res.Any())
return _paginateSendRegister(lib, search, SearchType.Science, SearchResultType.NotFound, null);
// Return
return _paginateSendRegister(lib, search, SearchType.Science, SearchResultType.Success, _createComments("neuzeit", res.Select((x) => (x.Index, x.Results.Select((y) => y.Identifier).ToList())).OrderBy(x => x.Index).ToList()));
}
private IActionResult _paginateSendLettersComments(
ILibrary lib,
int page,
string search,
bool? comments,
SearchResultType SRT,
List<Meta>? metas,
List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? resletters,
List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? rescomments
) {
// Sorting, get Pages & Error Checking
var metasbyyear = metas!.Distinct().ToLookup(x => x.Sort.Year).OrderBy(x => x.Key).ToList();
var pages = IndexController.Paginate(metasbyyear, _lettersForPage);
if (pages != null && page >= pages.Count) return _error404();
if (pages == null && page > 0) return _error404();
List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters = null;
// Select & Parse Metadata for Letters to be shown on the selected Page
if (pages != null && metasbyyear != null)
letters = metasbyyear
.Where(x => x.Key >= pages[page].StartYear && x.Key <= pages[page].EndYear)
.Select(x => (x.Key, x
.Select(y => IndexController.GenerateMetaViewModel(lib, y))
.OrderBy(x => x.Meta.Sort)
.ThenBy(x => x.Meta.Order)
.ToList()))
.ToList();
// Generate Search results & previews
Dictionary<string, List<SearchResult>>? searchResults = new Dictionary<string, List<SearchResult>>();
Dictionary<string, List<(Marginal, string)>>? parsedMarginals = null;
if (resletters != null)
foreach (var res in resletters) {
if (!searchResults.ContainsKey(res.Index))
searchResults.Add(res.Index, new List<SearchResult>());
foreach (var r in res.Results) {
if(!searchResults[res.Index].Where(x => x.Page == r.Page && x.Line == r.Line).Any())
searchResults[res.Index].Add(new SearchResult(search, res.Index) { Page = r.Page, Line = r.Line, Preview = r.Preview });
}
if (searchResults[res.Index].Any()) {
searchResults[res.Index] = searchResults[res.Index].OrderBy(x => HaWeb.HTMLHelpers.ConversionHelpers.RomanOrNumberToInt(x.Page)).ThenBy(x => HaWeb.HTMLHelpers.ConversionHelpers.RomanOrNumberToInt(x.Line)).ToList();
}
}
if (rescomments != null) {
var marginals = rescomments.Where(x => lib.Marginals.ContainsKey(x.Index)).Select(x => lib.Marginals[x.Index]).ToLookup(x => x.Letter);
var shownletters = letters!.SelectMany(x => x.LetterList.Select(y => y.Meta.Index)).ToHashSet();
var shownmarginals = marginals!.Where(x => shownletters.Contains(x.Key)).Select(x => (x.Key, x.ToList())).ToList();
var previews = _xmlService != null ? _xmlService.GetPreviews(shownmarginals, _readerService ,lib) : null;
if (previews != null)
foreach (var p in previews) {
if (!searchResults.ContainsKey(p.Index))
searchResults.Add(p.Index, new List<SearchResult>());
foreach (var res in p.Results) {
if (!searchResults[p.Index].Where(x => x.Page == res.Page && x.Line == res.Line).Any())
searchResults[p.Index].Add(new SearchResult(search, p.Index) { Page = res.Page, Line = res.Line, Preview = res.Preview });
}
if (searchResults[p.Index].Any()) {
searchResults[p.Index] = searchResults[p.Index].OrderBy(x => HaWeb.HTMLHelpers.ConversionHelpers.RomanOrNumberToInt(x.Page)).ThenBy(x => HaWeb.HTMLHelpers.ConversionHelpers.RomanOrNumberToInt(x.Line)).ToList();
}
}
// Parse Marginals
foreach (var l in marginals) {
if (parsedMarginals == null && l.Any())
parsedMarginals = new Dictionary<string, List<(Marginal, string)>>();
if (l.Any()) {
var list = new List<(Marginal, string)>();
foreach (var c in l) {
var sb = new StringBuilder();
var rd = _readerService.RequestStringReader(c.Element);
var st = new LetterState(lib, _readerService, lib.Metas[c.Letter], null, null, null);
new HaWeb.HTMLParser.XMLHelper<HaWeb.Settings.ParsingState.LetterState>(st, rd, sb, LetterRules.OTagRules, null, LetterRules.CTagRules, LetterRules.TextRules, LetterRules.WhitespaceRules);
new HaWeb.HTMLHelpers.LinkHelper(st.Lib, rd, sb, false);
rd.Read();
list.Add((c, sb.ToString()));
}
parsedMarginals!.Add(l.Key, list);
}
}
} }
return _error404(); // Model Init & Return
var model = new SucheViewModel(SearchType.Letters, SearchResultType.Success, comments, page, _paginate(pages), search, searchResults, letters, null, parsedMarginals);
return View("~/Views/HKB/Dynamic/Suche.cshtml", model);
} }
private List<(string Key, string Person)> _getAvailablePersons(ILibrary lib) { private IActionResult _paginateSendRegister(
return lib.Persons ILibrary lib,
.OrderBy(x => x.Value.Surname) string activeSearch,
.ThenBy(x => x.Value.Prename) SearchType ST,
.Select(x => (x.Key, x.Value.Name)) SearchResultType SRT,
.ToList(); List<CommentModel> comments) {
} // Model init & return
var model = new SucheViewModel(ST, SRT, null, 0, null, activeSearch, null, null, comments, null);
private BriefeMetaViewModel _generateMetaViewModel(ILibrary lib, Meta meta) { return View("~/Views/HKB/Dynamic/Suche.cshtml", model);
var hasMarginals = lib.MarginalsByLetter.Contains(meta.Index) ? true : false;
var senders = meta.Senders.Select(x => lib.Persons[x].Name) ?? new List<string>();
var recivers = meta.Receivers.Select(x => lib.Persons[x].Name) ?? new List<string>();
var zhstring = meta.ZH != null ? HaWeb.HTMLHelpers.LetterHelpers.CreateZHString(meta) : null;
return new BriefeMetaViewModel(meta, hasMarginals) {
ParsedZHString = zhstring,
ParsedSenders = HTMLHelpers.StringHelpers.GetEnumerationString(senders),
ParsedReceivers = HTMLHelpers.StringHelpers.GetEnumerationString(recivers)
};
} }
private List<string>? _paginate(List<(int StartYear, int EndYear)>? pages) { private List<string>? _paginate(List<(int StartYear, int EndYear)>? pages) {
@@ -93,46 +221,7 @@ public class SucheController : Controller {
}).ToList() : null; }).ToList() : null;
} }
private List<string>? _paginate(List<string> comments) { // Select and parse comments to be shown on a page
return null;
}
private IActionResult _paginateSendLetters(
ILibrary lib,
int page,
string activeSearch,
SearchResultType SRT,
Dictionary<string, List<SearchResult>>? searchResults,
List<IGrouping<int, Meta>>? metasbyyear
) {
var pages = IndexController.Paginate(metasbyyear, _lettersForPage);
if (pages != null && page >= pages.Count) return _error404();
if (pages == null && page > 0) return _error404();
List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters = null;
if (pages != null && metasbyyear != null)
letters = metasbyyear
.Where(x => x.Key >= pages[page].StartYear && x.Key <= pages[page].EndYear)
.Select(x => (x.Key, x
.Select(y => _generateMetaViewModel(lib, y))
.OrderBy(x => x.Meta.Sort)
.ThenBy(x => x.Meta.Order)
.ToList()))
.ToList();
var model = new SucheViewModel("letters", SRT, page, _paginate(pages), activeSearch, searchResults, letters, null);
return View("~/Views/HKB/Dynamic/Suche.cshtml", model);
}
private IActionResult _paginateSendRegister(
ILibrary lib,
int page,
string activeSearch,
SearchResultType SRT,
List<CommentModel> comments) {
var model = new SucheViewModel("register", SRT, page, new List<string>() { "Allgemeines Register", "Forschungsbibliographie" }, activeSearch, null, null, comments);
return View("~/Views/HKB/Dynamic/Suche.cshtml", model);
}
private List<CommentModel> _createComments(string category, List<(string, List<string>)>? comments) { private List<CommentModel> _createComments(string category, List<(string, List<string>)>? comments) {
var lib = _lib.GetLibrary(); var lib = _lib.GetLibrary();
var res = new List<CommentModel>(); var res = new List<CommentModel>();

View File

@@ -39,8 +39,8 @@ public static class CommentHelpers {
arrow = true; arrow = true;
} }
sb.Append(HTMLHelpers.TagHelpers.CreateElement("a", LETLINKCLASS, "/HKB/Briefe/" + let.Autopsic + "#" + blk.Page + "-" + blk.Line)); sb.Append(HTMLHelpers.TagHelpers.CreateElement("a", LETLINKCLASS, "/HKB/Briefe/" + let.Autopsic + "#" + blk.Page + "-" + blk.Line));
var linkstring = ""; var linkstring = string.Empty;
var pglnstring = ""; var pglnstring = string.Empty;
linkstring += let.Autopsic; linkstring += let.Autopsic;
pglnstring += "&nbsp;(&#8239;" + blk.Page + "/" + blk.Line + "&#8239;)"; pglnstring += "&nbsp;(&#8239;" + blk.Page + "/" + blk.Line + "&#8239;)";
linkstring += pglnstring; linkstring += pglnstring;

View File

@@ -36,8 +36,8 @@ public static class LetterHelpers {
var editsState = new EditState(); var editsState = new EditState();
foreach (var edit in editreasons) { foreach (var edit in editreasons) {
var currstring = edit.StartPage + "/" + edit.StartLine; var currstring = edit.StartPage + "/" + edit.StartLine;
var endstring = ""; var endstring = string.Empty;
var refstring = ""; var refstring = string.Empty;
if (edit.StartPage != edit.EndPage) if (edit.StartPage != edit.EndPage)
endstring += edit.EndPage + "/" + edit.EndLine; endstring += edit.EndPage + "/" + edit.EndLine;
else if (edit.StartLine != edit.EndLine) else if (edit.StartLine != edit.EndLine)
@@ -81,8 +81,8 @@ public static class LetterHelpers {
var handstrings = new List<(string, string, string, string, string)>(); var handstrings = new List<(string, string, string, string, string)>();
foreach (var hand in hands.OrderBy(x => x.StartPage.Length).ThenBy(x => x.StartPage).ThenBy(x => x.StartLine.Length).ThenBy(x => x.StartLine)) { foreach (var hand in hands.OrderBy(x => x.StartPage.Length).ThenBy(x => x.StartPage).ThenBy(x => x.StartLine.Length).ThenBy(x => x.StartLine)) {
var currstring = hand.StartPage + "/" + hand.StartLine; var currstring = hand.StartPage + "/" + hand.StartLine;
var endstring = ""; var endstring = string.Empty;
var personstring = ""; var personstring = string.Empty;
if (hand.StartPage != hand.EndPage) if (hand.StartPage != hand.EndPage)
endstring += hand.EndPage + "/" + hand.EndLine; endstring += hand.EndPage + "/" + hand.EndLine;
else else

View File

@@ -48,9 +48,9 @@ public class LinkHelper {
var letter = _lib.Metas[tag["letter"]]; var letter = _lib.Metas[tag["letter"]];
_sb.Append(HTMLHelpers.TagHelpers.CreateElement("a", LETLINKCLASS, "/HKB/Briefe/" + letter.Autopsic + "#" + tag["page"] + "-" + tag["line"])); _sb.Append(HTMLHelpers.TagHelpers.CreateElement("a", LETLINKCLASS, "/HKB/Briefe/" + letter.Autopsic + "#" + tag["page"] + "-" + tag["line"]));
if (!tag.Values.ContainsKey("linktext") || tag.Values["linktext"] == "true") { if (!tag.Values.ContainsKey("linktext") || tag.Values["linktext"] == "true") {
var linkstring = ""; var linkstring = string.Empty;
var ZHstring = ""; var ZHstring = string.Empty;
var pglnstring = ""; var pglnstring = string.Empty;
linkstring += "HKB&nbsp;" + letter.Autopsic; linkstring += "HKB&nbsp;" + letter.Autopsic;
if (tag.Values.ContainsKey("page")) { if (tag.Values.ContainsKey("page")) {
pglnstring += tag["page"]; pglnstring += tag["page"];
@@ -103,7 +103,7 @@ public class LinkHelper {
subreader.Read(); subreader.Read();
return sb.ToString(); return sb.ToString();
} }
return ""; return string.Empty;
} }
public void Dispose() { public void Dispose() {

View File

@@ -3,7 +3,7 @@ using System.Web;
namespace HaWeb.HTMLHelpers; namespace HaWeb.HTMLHelpers;
public static class StringHelpers { public static class StringHelpers {
public static string GetEnumerationString(IEnumerable<string> strlist) { public static string GetEnumerationString(IEnumerable<string> strlist) {
var res = ""; var res = string.Empty;
foreach (var str in strlist) { foreach (var str in strlist) {
if (str != strlist.First()) if (str != strlist.First())
if (str == strlist.Last()) if (str == strlist.Last())

View File

@@ -68,16 +68,11 @@ public class LineXMLHelper<T> {
_newpage = false; _newpage = false;
_firstline = true; _firstline = true;
if (_OTag_Funcs != null) _in.OpenTag += OnOTag;
_in.OpenTag += OnOTag; _in.SingleTag += OnSTag;
if (_STag_Funcs != null) _in.CloseTag += OnCTag;
_in.SingleTag += OnSTag; _in.Text += OnText;
if (_CTag_Funcs != null) _in.Whitespace += OnWS;
_in.CloseTag += OnCTag;
if (_Text_Funcs != null)
_in.Text += OnText;
if (_WS_Funcs != null)
_in.Whitespace += OnWS;
} }
private void _pushLine(object? _, EventArgs _empty) { private void _pushLine(object? _, EventArgs _empty) {
@@ -104,22 +99,18 @@ public class LineXMLHelper<T> {
protected virtual void OnText(object? _, Text text) { protected virtual void OnText(object? _, Text text) {
LastText.Append(text.Value); LastText.Append(text.Value);
_currentText.Append(text.Value);
if (_Text_Funcs != null) if (_Text_Funcs != null)
if (CatchPageLine == null || (CurrentPage == CatchPageLine.Value.Page && CurrentLine == CatchPageLine.Value.Line)) { foreach (var entry in _Text_Funcs)
_currentText.Append(text.Value); if (entry.Item1(text, this)) entry.Item2(_target, text, this);
foreach (var entry in _Text_Funcs)
if (entry.Item1(text, this)) entry.Item2(_target, text, this);
}
} }
protected virtual void OnWS(object? _, Whitespace ws) { protected virtual void OnWS(object? _, Whitespace ws) {
LastText.Append(ws.Value); LastText.Append(ws.Value);
_currentText.Append(ws.Value);
if (_WS_Funcs != null) if (_WS_Funcs != null)
if (CatchPageLine == null || (CurrentPage == CatchPageLine.Value.Page && CurrentLine == CatchPageLine.Value.Line)) { foreach (var entry in _WS_Funcs)
_currentText.Append(ws.Value); if (entry.Item1(ws, this)) entry.Item2(_target, ws, this);
foreach (var entry in _WS_Funcs)
if (entry.Item1(ws, this)) entry.Item2(_target, ws, this);
}
} }
protected virtual void OnOTag(object? _, Tag tag) { protected virtual void OnOTag(object? _, Tag tag) {

View File

@@ -8,27 +8,37 @@ public enum SearchResultType {
InvalidSearchTerm InvalidSearchTerm
} }
public enum SearchType {
Letters,
Register,
Science
}
public class SucheViewModel { public class SucheViewModel {
public List<(int Year, List<BriefeMetaViewModel> LetterList)>? Letters { get; private set; } public List<(int Year, List<BriefeMetaViewModel> LetterList)>? Letters { get; private set; }
public List<CommentModel>? Comments { get; private set; } public List<CommentModel>? Comments { get; private set; }
public Dictionary<string, List<(Marginal, string)>>? Marginals { get; private set; }
public int Count { get; private set; } public int Count { get; private set; }
public int ActivePage { get; private set; } public int ActivePage { get; private set; }
public List<string>? AvailablePages { get; private set; } public bool? IncludeComments { get; private set; }
public string ActiveSearch { get; private set; } public string ActiveSearch { get; private set; }
public List<string>? AvailablePages { get; private set; }
public Dictionary<string, List<SearchResult>>? SearchResults { get; private set; } public Dictionary<string, List<SearchResult>>? SearchResults { get; private set; }
public SearchResultType SearchResultType { get; private set; } public SearchResultType SearchResultType { get; private set; }
public string SearchType { get; private set; } public SearchType SearchType { get; private set; }
public SucheViewModel( public SucheViewModel(
string searchType, SearchType searchType,
SearchResultType searchResultType, SearchResultType searchResultType,
bool? includeComments,
int activePage, int activePage,
List<string>? availablePages, List<string>? availablePages,
string activeSearch, string activeSearch,
Dictionary<string, List<SearchResult>>? searchResults, Dictionary<string, List<SearchResult>>? searchResults,
List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters, List<(int Year, List<BriefeMetaViewModel> LetterList)>? letters,
List<CommentModel>? comments List<CommentModel>? comments,
Dictionary<string, List<(Marginal, string)>>? marginals
) { ) {
Letters = letters; Letters = letters;
if (letters != null) if (letters != null)
@@ -43,5 +53,7 @@ public class SucheViewModel {
ActiveSearch = activeSearch; ActiveSearch = activeSearch;
SearchResults = searchResults; SearchResults = searchResults;
Comments = comments; Comments = comments;
Marginals = marginals;
IncludeComments = includeComments;
} }
} }

View File

@@ -70,7 +70,7 @@ public class XMLRootDocument {
} }
private string _removeInvalidChars(string? s) { private string _removeInvalidChars(string? s) {
if (String.IsNullOrWhiteSpace(s)) return ""; if (String.IsNullOrWhiteSpace(s)) return string.Empty;
foreach (var c in Path.GetInvalidFileNameChars()) { foreach (var c in Path.GetInvalidFileNameChars()) {
s = s.Replace(c, '-'); s = s.Replace(c, '-');
} }

View File

@@ -83,11 +83,10 @@ TODO Navigation auf die Startseite / von Kontakt
Vor dem internen release: Vor dem internen release:
TODO Jahreszahlen auf der Startseite TODO Jahreszahlen auf der Startseite
Vor dem Release:
TODO Suchergebnisse beschränken TODO Suchergebnisse beschränken
TODO Mobile Menüs bei der Seitennavigation (Jahrszahlen, Buchstabenindex usw) TODO Mobile Menüs bei der Seitennavigation (Jahrszahlen, Buchstabenindex usw)
TODO Fehlerseiten bei nicht gefundenen Seiten TODO Fehlerseiten bei nicht gefundenen Seiten
TODO Traditions durchsuchen
Liste für Janina/Luca: Liste für Janina/Luca:
KEIN brief für Bassa KEIN brief für Bassa

View File

@@ -36,7 +36,7 @@ public static class CommentRules {
if (reader.State.Category == "bibel" && reader.State.Type == HaWeb.Settings.ParsingState.CommentType.Subcomment && if (reader.State.Category == "bibel" && reader.State.Type == HaWeb.Settings.ParsingState.CommentType.Subcomment &&
reader.OpenTags.Any() && reader.OpenTags.Last().Name == "lemma" && reader.OpenTags.Any() && reader.OpenTags.Last().Name == "lemma" &&
!txt.Value.Contains("Stücke zu") && !txt.Value.Contains("ZusDan")) { !txt.Value.Contains("Stücke zu") && !txt.Value.Contains("ZusDan")) {
var lnkstring = Regex.Replace(txt.Value, @"\s+", ""); var lnkstring = Regex.Replace(txt.Value, @"\s+", string.Empty);
sb.Append(HTMLHelpers.TagHelpers.CreateCustomElement("a", sb.Append(HTMLHelpers.TagHelpers.CreateCustomElement("a",
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "href", Value = "https://www.bibleserver.com/LUT/" + lnkstring}, new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "href", Value = "https://www.bibleserver.com/LUT/" + lnkstring},
new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "target", Value = "_blank"}, new HaWeb.HTMLHelpers.TagHelpers.Attribute() { Name = "target", Value = "_blank"},

View File

@@ -50,7 +50,7 @@ public class LetterState : HaWeb.HTMLParser.IState {
sb_lettertext = new StringBuilder(); sb_lettertext = new StringBuilder();
active_skipwhitespace = true; active_skipwhitespace = true;
currline = "-1"; currline = "-1";
currpage = ""; currpage = string.Empty;
mustwrap = (false, false); mustwrap = (false, false);
minwidth = false; minwidth = false;

View File

@@ -50,6 +50,6 @@ public class TraditionState : HaWeb.HTMLParser.IState {
active_trad = false; active_trad = false;
active_skipwhitespace = true; active_skipwhitespace = true;
currline = "-1"; currline = "-1";
currpage = ""; currpage = string.Empty;
} }
} }

View File

@@ -38,12 +38,10 @@
</div> </div>
</div> </div>
<div class="ha-uploadheader"> <div class="ha-uploadheader">
<h1 class="ha-uploadtitle">@Model.ActiveTitle</h1> <h1 class="ha-uploadtitle">@Model.ActiveTitle</h1>
</div> </div>
<div class="ha-uploadcontainer"> <div class="ha-uploadcontainer">
@* File Category Page File List *@ @* File Category Page File List *@
@if (Model.AvailableFiles != null && Model.AvailableFiles.Any()) { @if (Model.AvailableFiles != null && Model.AvailableFiles.Any()) {
@@ -125,7 +123,5 @@
if (filesbutton !== null) if (filesbutton !== null)
filesbutton.addEventListener("click", () => hideshowfiles()); filesbutton.addEventListener("click", () => hideshowfiles());
}); });
</script> </script>

View File

@@ -147,7 +147,7 @@
<div class="ha-filtertitle"> <div class="ha-filtertitle">
Volltextsuche Volltextsuche
</div> </div>
<form class="ha-searchform" id="ha-searchform" asp-controller="Suche" asp-action="Index" method="get"> <form class="ha-searchform" id="ha-searchform" asp-controller="Suche" asp-action="Briefe" method="get">
<input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff"/> <input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff"/>
<button id="ha-searchformsubmit" type="submit">Suchen</button> <button id="ha-searchformsubmit" type="submit">Suchen</button>
</form> </form>

View File

@@ -37,12 +37,19 @@
</div> </div>
} }
@if (Model.AllowSearch) { @if (Model.AllowSearch) {
<form class="ha-searchform" id="ha-searchform" asp-controller="Suche" asp-action="Index" method="get"> @if (Model.Category == "neuzeit") {
<input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff"/> <form class="ha-searchform" id="ha-searchform" asp-controller="Suche" asp-action="Register" method="get">
<input type="hidden" name="category" type="text" value="register"/> <input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff"/>
<input type="hidden" name="page" type="text" value="@(Model.Category == "forschung" ? "1" : "0")"/> <input type="hidden" name="page" type="text" value="@(Model.Category == "forschung" ? "1" : "0")"/>
<button id="ha-searchformsubmit" type="submit">Durchsuchen</button> <button id="ha-searchformsubmit" type="submit">Durchsuchen</button>
</form> </form>
} else {
<form class="ha-searchform" id="ha-searchform" asp-controller="Suche" asp-action="Science" method="get">
<input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff"/>
<input type="hidden" name="page" type="text" value="@(Model.Category == "forschung" ? "1" : "0")"/>
<button id="ha-searchformsubmit" type="submit">Durchsuchen</button>
</form>
}
<script> <script>
const ACTIVATESEARCHFILTER = function(filter, button) { const ACTIVATESEARCHFILTER = function(filter, button) {

View File

@@ -14,45 +14,48 @@
<div class="ha-searchfilter"> <div class="ha-searchfilter">
@if (Model.ActiveSearch != null) { @if (Model.ActiveSearch != null) {
<div class="ha-activefilterinfo"> <div class="ha-activefilterinfo">
@if (Model.SearchType == "letters") { @if (Model.SearchType == SearchType.Letters) {
<span><span class="">Briefe</span>, die »@Model.ActiveSearch« enthalten.&emsp;</span><br> <span><span class="">Briefe</span> @if(Model.IncludeComments == true) { <span> und Stellenkommentare</span> }, die »@Model.ActiveSearch« enthalten.&emsp;</span><br>
} }
@if (Model.SearchType == "register") { @if (Model.SearchType == SearchType.Register) {
@if (Model.ActivePage == 0) { <span><span class="">Registereinträge</span>, die »@Model.ActiveSearch« enthalten.&emsp;</span><br>
<span><span class="">Registereinträge</span>, die »@Model.ActiveSearch« enthalten.&emsp;</span><br> <a class="ha-reversefilter" asp-controller="Register" asp-action="Allgemein">← Registerübersicht</a><span> / </span>
<a class="ha-reversefilter" asp-controller="Register" asp-action="Allgemein">← Registerübersicht</a><span> / </span>
} else {
<span><span class="">Bibliografische Einträge</span>, die »@Model.ActiveSearch« enthalten.&emsp;</span><br>
<a class="ha-reversefilter" asp-controller="Register" asp-action="Forschung">← Forschungsbibliographie</a><span> / </span>
}
} }
@if (Model.SearchType == "marginals") { @if (Model.SearchType == SearchType.Science) {
<span><span class="">Stellenkommentare</span>, die »@Model.ActiveSearch« enthalten.&emsp;</span><br> <span><span class="">Bibliografische Einträge</span>, die »@Model.ActiveSearch« enthalten.&emsp;</span><br>
<a class="ha-reversefilter" asp-controller="Register" asp-action="Forschung">← Forschungsbibliographie</a><span> / </span>
} }
<a class="ha-reversefilter" asp-controller="Index" asp-action="Index">← Briefübersicht</a> <a class="ha-reversefilter" asp-controller="Index" asp-action="Index">← Briefübersicht</a>
</div> </div>
} }
<form class="ha-searchform" id="ha-searchform" asp-controller="Suche" asp-action="Index" method="get"> <form id="ha-searchform" method="get">
<div class="ha-searchform">
<input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff" value="@Model.ActiveSearch"/> <input id="ha-searchformtext" name="search" type="text" placeholder="Suchbegriff" value="@Model.ActiveSearch"/>
<input type="hidden" name="category" type="text" value="@Model.SearchType"/>
<button id="ha-searchformsubmit" type="submit">Suchen</button> <button id="ha-searchformsubmit" type="submit">Suchen</button>
</div>
@if (Model.SearchType == SearchType.Letters) {
<div class="ha-includecomments">
<input type="checkbox" id="comments" name="comments" value="true" @(Model.IncludeComments == true ? "checked" : "")>
<label for="comments">Stellenkommentare einbeziehen</label>
</div>
}
</form> </form>
<div class="ha-alternativesearches"> <div class="ha-alternativesearches">
@if (Model.SearchType != "letters") { @if (Model.SearchType != SearchType.Letters) {
<a asp-controller="Suche" asp-action="index" asp-route-search="@Model.ActiveSearch" asp-route-category="letters"> <a asp-controller="Suche" asp-action="Briefe" asp-route-search="@Model.ActiveSearch" asp-route-comments="true" >
Stattdessen Briefe nach »@Model.ActiveSearch« durchsuchen&nbsp;→ Stattdessen Briefe / Stellenkommentare nach »@Model.ActiveSearch« durchsuchen&nbsp;→
</a> </a>
} }
@if (Model.SearchType != "register") { @if (Model.SearchType != SearchType.Register) {
<a asp-controller="Suche" asp-action="index" asp-route-search="@Model.ActiveSearch" asp-route-category="register"> <a asp-controller="Suche" asp-action="Register" asp-route-search="@Model.ActiveSearch" >
Stattdessen Register & Bibliographie nach »@Model.ActiveSearch« durchsuchen&nbsp;→ Stattdessen Register nach »@Model.ActiveSearch« durchsuchen&nbsp;→
</a> </a>
} }
@if (Model.SearchType != "marginals") { @if (Model.SearchType != SearchType.Science) {
<a asp-controller="Suche" asp-action="index" asp-route-search="@Model.ActiveSearch" asp-route-category="marginals"> <a asp-controller="Suche" asp-action="Science" asp-route-search="@Model.ActiveSearch" >
Stattdessen Stellenkommentare nach »@Model.ActiveSearch« durchsuchen&nbsp;→ Stattdessen Forschungsbibliographie nach »@Model.ActiveSearch« durchsuchen&nbsp;→
</a> </a>
} }
</div> </div>
@@ -79,7 +82,7 @@
<div class="ha-searchnav"> <div class="ha-searchnav">
@if (Model.AvailablePages != null && Model.AvailablePages.Any() && Model.AvailablePages.Count > 1) { @if (Model.AvailablePages != null && Model.AvailablePages.Any() && Model.AvailablePages.Count > 1) {
@for(var i = 0; i < Model.AvailablePages.Count; i++) { @for(var i = 0; i < Model.AvailablePages.Count; i++) {
<a class="@(Model.ActivePage == i ? "active" : "")" asp-route-search="@Model.ActiveSearch" asp-controller="Suche" asp-route-page="@i" asp-route-category="@Model.SearchType"> <a class="@(Model.ActivePage == i ? "active" : "")" asp-route-search="@Model.ActiveSearch" asp-controller="Suche" asp-route-page="@i" asp-route-comments="@(Model.IncludeComments == true ? "true" : "")">
<span> <span>
@Model.AvailablePages[i] @Model.AvailablePages[i]
</span> </span>
@@ -91,7 +94,7 @@
<div class="ha-searchbody"> <div class="ha-searchbody">
@* Letter Search *@ @* Letter & Marginal Search *@
@if (Model.Letters != null) { @if (Model.Letters != null) {
<div class="ha-letterlist"> <div class="ha-letterlist">
@foreach (var year in Model.Letters) { @foreach (var year in Model.Letters) {
@@ -105,11 +108,30 @@
<div class="ha-letterlistsearchresults"> <div class="ha-letterlistsearchresults">
@foreach (var sr in Model.SearchResults[letter.Meta.Index]) @foreach (var sr in Model.SearchResults[letter.Meta.Index])
{ {
<a class="ha-letterlistsearchresult" asp-controller="Briefe" asp-action="Index" asp-route-id="@letter.Meta.Autopsic" asp-fragment="@sr.Page-@sr.Line"> <div class="ha-letterlistsearchresult">
<div class="ha-searchresultlocation caps-allpetite"> <div class="ha-searchresultlocation">
HKB @sr.Page/@sr.Line</div><div class="ha-searchresultpreview">@sr.Preview <a asp-controller="Briefe" asp-action="Index" asp-route-id="@letter.Meta.Autopsic" asp-fragment="@sr.Page-@sr.Line">
HKB @sr.Page/@sr.Line
</a>
</div> </div>
</a> <div class="ha-searchresultpreview">
<a asp-controller="Briefe" asp-action="Index" asp-route-id="@letter.Meta.Autopsic" asp-fragment="@sr.Page-@sr.Line">
@sr.Preview
</a>
@if (Model.Marginals != null && Model.Marginals.Any()) {
@if (Model.Marginals.ContainsKey(letter.Meta.Index)) {
@foreach (var c in Model.Marginals[letter.Meta.Index]) {
@if (c.Item1.Page == sr.Page && c.Item1.Line == sr.Line) {
<div class="ha-seachresultmarginal">
<div class="ha-searchresultcommentpill">Kommentar</div>
@Html.Raw(c.Item2)
</div>
}
}
}
}
</div>
</div>
} }
</div> </div>
} }
@@ -120,8 +142,8 @@
} }
@* Register Search *@ @* Register Search *@
@if (Model.SearchType == "register" && Model.Comments != null && Model.Comments.Any()) { @if (Model.Comments != null && Model.Comments.Any()) {
<div class="ha-commentlist @(Model.ActivePage == 1 ? "ha-forschung" : "")"> <div class="ha-commentlist @(Model.SearchType == SearchType.Science ? "ha-forschung" : "")">
@foreach (var k in Model.Comments) { @foreach (var k in Model.Comments) {
<div class="ha-comment"> <div class="ha-comment">
<div class="ha-headcomment">@Html.Raw(k.ParsedComment)</div> <div class="ha-headcomment">@Html.Raw(k.ParsedComment)</div>

View File

@@ -3,6 +3,7 @@ using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using HaWeb.Models; using HaWeb.Models;
using HaDocument.Interfaces; using HaDocument.Interfaces;
using HaDocument.Models;
using HaXMLReader.Interfaces; using HaXMLReader.Interfaces;
public interface IXMLService { public interface IXMLService {
@@ -21,4 +22,5 @@ public interface IXMLService {
public void SetInProduction(); public void SetInProduction();
public void SetInProduction(XDocument document); public void SetInProduction(XDocument document);
public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? SearchCollection(string collection, string searchword, IReaderService reader, ILibrary? lib); public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? SearchCollection(string collection, string searchword, IReaderService reader, ILibrary? lib);
public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? GetPreviews(List<(string, List<Marginal>)> places, IReaderService reader, ILibrary lib);
} }

View File

@@ -25,7 +25,7 @@ public class IdentificationStringJSONConverter : JsonConverter<(string?, string?
JsonSerializerOptions options) JsonSerializerOptions options)
{ {
if (value.Item1 == null && value.Item2 == null) return; if (value.Item1 == null && value.Item2 == null) return;
var res = ""; var res = string.Empty;
if (value.Item1 != null) res += value.Item1; if (value.Item1 != null) res += value.Item1;
if (value.Item2 != null) res += "-" + value.Item2; if (value.Item2 != null) res += "-" + value.Item2;
writer.WriteStringValue(res); writer.WriteStringValue(res);

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Text; using System.Text;
using HaXMLReader.Interfaces; using HaXMLReader.Interfaces;
using HaDocument.Interfaces; using HaDocument.Interfaces;
using HaDocument.Models;
public class XMLService : IXMLService { public class XMLService : IXMLService {
private Dictionary<string, FileList?>? _Used; private Dictionary<string, FileList?>? _Used;
@@ -113,7 +114,38 @@ public class XMLService : IXMLService {
_collectedProduction = ret.ToDictionary(x => x.Key, y => y.Value); _collectedProduction = ret.ToDictionary(x => x.Key, y => y.Value);
} }
public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? SearchCollection(string collection, string searchword, IReaderService reader, ILibrary? lib = null) { public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? GetPreviews(List<(string, List<Marginal>)> places, IReaderService reader, ILibrary lib) {
var searchableObjects = _collectedProduction["letters"].Items;
var res = new ConcurrentBag<(string Index, List<(string Page, string Line, string preview, string identifier)> Results)>();
Parallel.ForEach(places, (obj) => {
var text = searchableObjects[obj.Item1];
if (text == null || text.SearchText == null || obj.Item2 == null || !obj.Item2.Any()) return;
var state = new SearchState(String.Empty, false, lib);
var rd = reader.RequestStringReader(text.SearchText);
var parser = new HaWeb.HTMLParser.LineXMLHelper<SearchState>(state, rd, new StringBuilder(), null, null, null, null, null);
rd.Read();
res.Add((
obj.Item1,
obj.Item2.Select(x => (
x.Page,
x.Line,
parser.Lines != null ?
parser.Lines
.Where(y => y.Page == x.Page && y.Line == x.Line)
.Select(y => y.Text)
.FirstOrDefault(string.Empty)
: string.Empty,
String.Empty
) ).ToList()
));
});
return res.ToList();
}
public List<(string Index, List<(string Page, string Line, string Preview, string Identifier)> Results)>? SearchCollection(string collection, string searchword, IReaderService reader, ILibrary lib) {
if (!_collectedProduction.ContainsKey(collection)) return null; if (!_collectedProduction.ContainsKey(collection)) return null;
var searchableObjects = _collectedProduction[collection].Items; var searchableObjects = _collectedProduction[collection].Items;
var res = new ConcurrentBag<(string Index, List<(string Page, string Line, string preview, string identifier)> Results)>(); var res = new ConcurrentBag<(string Index, List<(string Page, string Line, string preview, string identifier)> Results)>();

File diff suppressed because one or more lines are too long

View File

@@ -97,15 +97,22 @@
.ha-search .ha-searchhead .ha-searchfilterinfo { .ha-search .ha-searchhead .ha-searchfilterinfo {
@apply border p-2 mb-4 hyphenate text-base max-w-[46rem] @apply border p-2 mb-4 hyphenate text-base max-w-[46rem]
} }
.ha-search .ha-searchhead .ha-searchfilter form {
@apply mb-2 max-w-[34rem]
}
.ha-search .ha-searchhead .ha-searchfilter .ha-searchform { .ha-search .ha-searchhead .ha-searchfilter .ha-searchform {
@apply py-1 flex flex-row gap-x-2 max-w-[34rem] @apply py-1 flex flex-row gap-x-2
} }
.ha-search .ha-searchhead .ha-searchfilter .ha-searchform input { .ha-search .ha-searchhead .ha-searchfilter .ha-searchform input {
@apply px-1 border grow min-w-0 @apply px-1 border grow min-w-0
} }
.ha-search .ha-searchhead .ha-searchfilter .ha-includecomments {
@apply w-full !text-base
}
.ha-search .ha-searchhead .ha-searchfilter .ha-searchform button { .ha-search .ha-searchhead .ha-searchfilter .ha-searchform button {
@apply float-right px-2 border border-slate-200 hover:border-black disabled:bg-gray-200 disabled:hover:border-slate-200 disabled:text-gray-600 @apply float-right px-2 border border-slate-200 hover:border-black disabled:bg-gray-200 disabled:hover:border-slate-200 disabled:text-gray-600
} }
@@ -159,17 +166,35 @@
} }
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult { .ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult {
@apply px-6 py-1 bg-slate-50 block @apply px-6 py-1 bg-slate-50 flex flex-row gap-x-4 items-baseline
} }
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-searchresultlocation { .ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-searchresultlocation {
@apply numeric-mediaeval font-semibold text-sm inline-block @apply font-semibold text-sm inline-block shrink-0 flex-nowrap
} }
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-searchresultpreview { .ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-searchresultpreview {
@apply inline-block pl-4
} }
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-seachresultmarginal {
@apply text-sm max-w-[32rem] my-1
}
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-seachresultmarginal .ha-searchresultcommentpill {
@apply text-xs px-1.5 rounded-xl text-white bg-hamannSlate-700 inline-block mr-2
}
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-seachresultmarginal .ha-marginal a {
@apply underline decoration-dotted hover:decoration-solid
}
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-seachresultmarginal .ha-marginal,
.ha-search .ha-searchbody .ha-letterlist .ha-letterlistentry .ha-letterlistsearchresults .ha-letterlistsearchresult .ha-seachresultmarginal .ha-marginal * {
@apply !inline hyphenate
}
.ha-search .ha-searchbody .ha-commentlist { .ha-search .ha-searchbody .ha-commentlist {
@apply pt-2 md:pt-4 px-9 md:px-16 numeric-mediaeval font-serif @apply pt-2 md:pt-4 px-9 md:px-16 numeric-mediaeval font-serif
} }