diff --git a/templates/annotations_banner.templ b/templates/annotations_banner.templ
index 647da894..a0436ad5 100644
--- a/templates/annotations_banner.templ
+++ b/templates/annotations_banner.templ
@@ -61,11 +61,11 @@ templ AnnotationsBanner(book handlers.MediaDetail) {
-
-
+
+
diff --git a/templates/annotations_banner_templ.go b/templates/annotations_banner_templ.go
index 13e4d50c..5b039980 100644
--- a/templates/annotations_banner_templ.go
+++ b/templates/annotations_banner_templ.go
@@ -50,7 +50,7 @@ func AnnotationsBanner(book handlers.MediaDetail) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Loading…
No notes or highlights on this book yet.
Markdown supported
Export annotations
Notes and highlights, as markdown files.
")
+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "Loading…
No notes or highlights on this book yet.
Markdown supported
Export annotations
Notes and highlights, as markdown files.
")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
diff --git a/web/src/annotations-banner.ts b/web/src/annotations-banner.ts
index f6880781..cb211fac 100644
--- a/web/src/annotations-banner.ts
+++ b/web/src/annotations-banner.ts
@@ -19,8 +19,11 @@ interface AnnotationRow {
label: string;
/** compact preview line: stripped markdown (note) or capped selection */
preview: string;
- /** rich body for the expanded view (pre-sanitized) */
- rich: string;
+ /** expanded view, split so the highlight→note boundary is explicit:
+ * the escaped selection text, then the note's markdown-rendered HTML
+ * (empty when the row has no note body). */
+ richSelection: string;
+ richNote: string;
/** the ?loc= handoff value for the jump */
loc: string;
/** row accent color (swatch) */
@@ -96,7 +99,8 @@ function buildAnnotationRows(
kind: "note",
label,
preview: stripNote(content) || "(empty note)",
- rich: renderNoteHtml(content),
+ richSelection: "",
+ richNote: renderNoteHtml(content),
loc,
color: "var(--accent)",
hasNote: true,
@@ -135,17 +139,19 @@ function buildAnnotationRows(
}
const capped = selection.length > 80 ? selection.slice(0, 80) + "…" : selection;
- // Expanded body: the full highlight text followed by its rich note.
- const rich = note
- ? `${escapeHtmlForRich(selection)}
` + renderNoteHtml(note)
- : `${escapeHtmlForRich(selection) || "…"}
`;
+ // Expanded body, split at the highlight→note boundary: the escaped
+ // selection, then the note's rendered markdown under a labeled divider
+ // (rendered by the banner template).
+ const richSelection = escapeHtmlForRich(selection);
+ const richNote = note ? renderNoteHtml(note) : "";
const row: AnnotationRow = {
key: `hl-${h.id}`,
kind: "highlight",
label,
preview: capped || "(empty highlight)",
- rich,
+ richSelection,
+ richNote,
loc,
color,
hasNote: !!note,
diff --git a/web/src/book-detail.ts b/web/src/book-detail.ts
index 3dce2aeb..bb35c84f 100644
--- a/web/src/book-detail.ts
+++ b/web/src/book-detail.ts
@@ -225,13 +225,14 @@ Alpine.data("bookDetail", () => {
},
// ----- note edit/delete at the banner (objective 8a) -----
startBannerEdit(row: AnnotationRow): void {
- // Highlight rows edit the note ATTACHED to the highlight (never the
- // highlight itself): rich = "\n\n".
+ // Both kinds carry the raw note body on their source: highlights
+ // hold the ATTACHED note (never the highlight itself), location
+ // notes hold their content. The old code parsed row.rich (rendered
+ // HTML — no \n\n separators in it) and came up empty every time.
this.bannerEditRow = row;
- this.bannerEditText =
- row.kind === "highlight"
- ? String((row.rich.split("\n\n").slice(1).join("\n\n") || "")).trim()
- : String(this.bannerSources.find(s => s.row.key === row.key)?.source ?? "");
+ this.bannerEditText = String(
+ this.bannerSources.find(s => s.row.key === row.key)?.source ?? "",
+ );
this.bannerEditOpen = true;
},
async saveBannerNote() {