"
if pic, ok := authorPics[ev.PubKey]; ok && pic != "" {
headerHTML += "

"
}
headerHTML += "
" + escapeHTML(name) + " "
truncated := len(ev.Content) > 300
text := ev.Content
if truncated {
text = text[:300] + "..."
}
embedEvID := ev.ID
embedRootID := getRootID(ev)
if embedRootID == "" {
embedRootID = embedEvID
}
for _, elemID := range elemIDs {
el := dom.GetElementById(elemID)
if el == 0 {
continue
}
dom.SetInnerHTML(el, "")
dom.SetStyle(el, "opacity", "1")
dom.SetStyle(el, "cursor", "pointer")
hdr := dom.CreateElement("div")
dom.SetInnerHTML(hdr, headerHTML)
dom.AppendChild(el, hdr)
body := dom.CreateElement("div")
dom.SetStyle(body, "fontSize", "13px")
dom.SetStyle(body, "lineHeight", "1.4")
dom.SetInnerHTML(body, renderEmbedText(text))
dom.AppendChild(el, body)
if truncated {
more := dom.CreateElement("span")
dom.SetTextContent(more, t("show_more"))
dom.SetStyle(more, "color", "var(--accent)")
dom.SetStyle(more, "cursor", "pointer")
dom.SetStyle(more, "fontSize", "12px")
dom.SetStyle(more, "display", "inline-block")
dom.SetStyle(more, "marginTop", "2px")
fullContent := ev.Content
thisBody := body
expanded := false
dom.AddEventListener(more, "click", dom.RegisterCallback(func() {
expanded = !expanded
if expanded {
dom.SetInnerHTML(thisBody, renderEmbedText(fullContent))
dom.SetTextContent(more, t("show_less"))
} else {
dom.SetInnerHTML(thisBody, renderEmbedText(fullContent[:300]+"..."))
dom.SetTextContent(more, t("show_more"))
}
}))
dom.AppendChild(el, more)
}
// Click anywhere on embed (except "show more") opens thread view.
thisEl := el
dom.AddEventListener(thisEl, "click", dom.RegisterCallback(func() {
showNoteThread(embedRootID, embedEvID)
}))
}
if _, ok := authorNames[ev.PubKey]; !ok {
if !fetchedK0[ev.PubKey] {
queueProfileFetch(ev.PubKey)
}
}
}
// jsonGetNum extracts a numeric value for a given key from a JSON object.
func jsonGetNum(s, key string) int64 {
needle := "\"" + key + "\":"
idx := strIndex(s, needle)
if idx < 0 {
return 0
}
idx += len(needle)
// Skip whitespace.
for idx < len(s) && (s[idx] == ' ' || s[idx] == '\t') {
idx++
}
if idx >= len(s) {
return 0
}
var n int64
for idx < len(s) && s[idx] >= '0' && s[idx] <= '9' {
n = n*10 + int64(s[idx]-'0')
idx++
}
return n
}
// jsonEsc escapes a string for embedding in a JSON value.
func jsonEsc(s string) string {
s = strReplace(s, "\\", "\\\\")
s = strReplace(s, "\"", "\\\"")
s = strReplace(s, "\n", "\\n")
s = strReplace(s, "\r", "\\r")
s = strReplace(s, "\t", "\\t")
return s
}
// strIndex finds substring in string. Returns -1 if not found.
func strIndex(s, sub string) int {
sl := len(sub)
for i := 0; i <= len(s)-sl; i++ {
if s[i:i+sl] == sub {
return i
}
}
return -1
}
// --- Helpers ---
// normalizeURL strips trailing slashes and lowercases the scheme+host.
func normalizeURL(u string) string {
for len(u) > 0 && u[len(u)-1] == '/' {
u = u[:len(u)-1]
}
// Lowercase scheme and host (before first / after ://).
if len(u) > 6 && u[:6] == "wss://" {
rest := u[6:]
slash := strIndex(rest, "/")
if slash < 0 {
return u[:6] + toLower(rest)
}
return u[:6] + toLower(rest[:slash]) + rest[slash:]
}
if len(u) > 5 && u[:5] == "ws://" {
rest := u[5:]
slash := strIndex(rest, "/")
if slash < 0 {
return u[:5] + toLower(rest)
}
return u[:5] + toLower(rest[:slash]) + rest[slash:]
}
return u
}
func toLower(s string) string {
b := []byte{:len(s)}
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'A' && c <= 'Z' {
c += 32
}
b[i] = c
}
return string(b)
}
func showQRModal(npubStr string) {
svg := qrSVG(npubStr, 5)
if svg == "" {
return
}
scrim := dom.CreateElement("div")
dom.SetStyle(scrim, "position", "fixed")
dom.SetStyle(scrim, "inset", "0")
dom.SetStyle(scrim, "background", "rgba(0,0,0,0.6)")
dom.SetStyle(scrim, "display", "flex")
dom.SetStyle(scrim, "alignItems", "center")
dom.SetStyle(scrim, "justifyContent", "center")
dom.SetStyle(scrim, "zIndex", "9999")
dom.SetStyle(scrim, "cursor", "pointer")
dom.AddEventListener(scrim, "click", dom.RegisterCallback(func() {
dom.RemoveChild(dom.Body(), scrim)
}))
card := dom.CreateElement("div")
dom.SetStyle(card, "background", "white")
dom.SetStyle(card, "borderRadius", "16px")
dom.SetStyle(card, "padding", "24px")
dom.SetStyle(card, "display", "flex")
dom.SetStyle(card, "flexDirection", "column")
dom.SetStyle(card, "alignItems", "center")
dom.SetStyle(card, "gap", "12px")
dom.SetStyle(card, "cursor", "default")
dom.SetAttribute(card, "onclick", "event.stopPropagation()")
dom.SetInnerHTML(card, svg)
label := dom.CreateElement("div")
dom.SetStyle(label, "fontSize", "11px")
dom.SetStyle(label, "color", "#666")
dom.SetStyle(label, "wordBreak", "break-all")
dom.SetStyle(label, "textAlign", "center")
dom.SetStyle(label, "maxWidth", "205px")
dom.SetStyle(label, "fontFamily", "'Fira Code', monospace")
dom.SetTextContent(label, npubStr)
dom.AppendChild(card, label)
dom.AppendChild(scrim, card)
dom.AppendChild(dom.Body(), scrim)
}
func clearChildren(el dom.Element) {
dom.SetInnerHTML(el, "")
}
func itoa(n int) string {
if n == 0 {
return "0"
}
neg := false
if n < 0 {
neg = true
n = -n
}
var b [20]byte
i := len(b)
for n > 0 {
i--
b[i] = byte('0' + n%10)
n /= 10
}
if neg {
i--
b[i] = '-'
}
return string(b[i:])
}