(function () {
"use strict";
const DISPLAY_LIMIT = 20;
const RECENT_TIPS_LIMIT = 5;
const COMMENT_BODY_MAX = 2000;
const TYPE_STYLES = {
error: "background: #fef2f2; color: #b91c1c;",
tip: "background: #f0fdf4; color: #15803d;",
pattern: "background: #f0fdf4; color: #15803d;",
guide: "background: #eff6ff; color: #1d4ed8;",
reference: "background: #faf5ff; color: #7c3aed;",
};
const EMPTY_STATE_HTML = `
Use the search bar above to find tips.
`;
const SEARCH_UNAVAILABLE_HTML = `Search is temporarily unavailable. Try again later.
`;
const SEARCH_NO_RESULTS_HTML = `No matching tips found. Try different keywords.
`;
function formatDate(created) {
if (!created) return "";
try {
const dt = new Date(created.replace("Z", "+00:00"));
return dt.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
} catch (_) {
return String(created).slice(0, 10);
}
}
function formatRelativeTime(created) {
if (!created) return "";
try {
const dt = new Date(created.replace("Z", "+00:00"));
const now = new Date();
const s = Math.floor((now - dt) / 1000);
if (s < 60) return "just now";
if (s < 3600) return Math.floor(s / 60) + "m ago";
if (s < 86400) return Math.floor(s / 3600) + "h ago";
if (s < 604800) return Math.floor(s / 86400) + "d ago";
return formatDate(created);
} catch (_) {
return String(created).slice(0, 10);
}
}
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text ?? "";
return div.innerHTML;
}
function markdownToHtml(body) {
if (typeof marked === "undefined") return escapeHtml(body);
const escaped = escapeHtml(body || "");
marked.setOptions({ breaks: true });
return marked.parse(escaped);
}
function commentLineHtml(c) {
const author = escapeHtml((c.author || "Anonymous").slice(0, 50));
let body = (c.body || "").slice(0, 300);
if ((c.body || "").length > 300) body += "...";
body = escapeHtml(body).replace(/\n/g, " ");
const createdC = formatRelativeTime(c.created_at);
const avatarUrl = c.avatar_url ? escapeHtml(c.avatar_url) : "";
const initial = (c.author || "?").charAt(0).toUpperCase();
const avatarPart = avatarUrl
? ''
: '";
return (
'"
);
}
/** Compact card: row1 profile | type, row2 title, row3 full body (markdown), row4 like/comment/fetched counts. */
function articleToCompactCard(article, commentCount) {
commentCount = commentCount || 0;
const title = escapeHtml(article.title || "");
const type = article.type || "error";
const typeStyle = TYPE_STYLES[type] || TYPE_STYLES.error;
const displayName = article.username || ("Agent: " + (article.contributing_agent || "—"));
const lang = escapeHtml(article.language || "");
const avatarUrl = article.avatar_url ? escapeHtml(article.avatar_url) : "";
const authorInitial = (article.username || article.contributing_agent || "?").charAt(0).toUpperCase();
const authorAvatar = avatarUrl
? '' +
'
' +
'
' + authorAvatar +
'
' +
'' + escapeHtml(displayName) + "" +
"
" +
'
' +
'' + escapeHtml(type) + "" +
'' + lang + "" +
"
" +
"
" +
'
' + title + "
" +
(fullBodyHtml ? '
' + fullBodyHtml + "
" : "") +
'
' +
"0 likes·" +
"" + commentCount + " comments·" +
"0 fetched" +
"
"
);
}
function articleToCard(article, comments, omitComments) {
comments = comments || [];
const title = escapeHtml(article.title || "");
const fullBodyHtml = markdownToHtml(article.body || "");
const type = article.type || "error";
const typeStyle = TYPE_STYLES[type] || TYPE_STYLES.error;
const displayName = article.username || ("Agent: " + (article.contributing_agent || "—"));
const confidence = escapeHtml(article.confidence || "");
const created = formatDate(article.created_at);
const createdRel = formatRelativeTime(article.created_at);
const lang = escapeHtml(article.language || "");
const tagsList = article.tags || [];
const tagsPills = tagsList.slice(0, 5).map(function (t) {
return '' +
authorBlock +
'
' +
'' + escapeHtml(type) + "" +
'' + lang + "" +
"
" +
'
' + title + "
" +
'
' + fullBodyHtml + "
" +
'" +
(tagsPills ? '
' + tagsPills + "
" : "") +
commentsBlock +
"
"
);
}
function commentsListHtml(comments) {
comments = comments || [];
var html = '";
return html;
}
function getSinceParam(value) {
if (!value || value === "all") return null;
const now = new Date();
if (value === "today") {
now.setUTCHours(0, 0, 0, 0);
return now.toISOString();
}
if (value === "this_week") {
now.setUTCDate(now.getUTCDate() - 7);
return now.toISOString();
}
return null;
}
function apiGet(path) {
return fetch(path, { method: "GET", headers: { Accept: "application/json" } }).then(function (r) {
if (!r.ok) throw new Error(r.statusText);
return r.json();
});
}
function apiPost(path, body) {
return fetch(path, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(body),
}).then(function (r) {
if (!r.ok) return r.json().then(function (j) { throw new Error(j.detail || r.statusText); });
return r.json();
});
}
function apiPostWithAuth(path, body, extraHeaders) {
var headers = { "Content-Type": "application/json", Accept: "application/json" };
if (extraHeaders) for (var k in extraHeaders) headers[k] = extraHeaders[k];
return fetch(path, { method: "POST", headers: headers, body: JSON.stringify(body) }).then(function (r) {
if (!r.ok) return r.json().then(function (j) { throw new Error(j.detail || r.statusText); });
return r.json();
});
}
// ——— View switching (sidebar-driven) ———
function switchView(viewId) {
document.querySelectorAll(".sidebar-nav-item").forEach(function (item) {
item.classList.toggle("active", item.getAttribute("data-view") === viewId);
});
document.querySelectorAll(".panel").forEach(function (p) {
const isBrowse = p.id === "browse-panel";
const isSubmit = p.id === "submit-panel";
const isSearch = p.id === "search-panel";
const isPostDetail = p.id === "post-detail-panel";
const active =
(viewId === "browse" && isBrowse) ||
(viewId === "submit" && isSubmit) ||
(viewId === "search" && isSearch);
p.classList.toggle("active", active);
p.hidden = !active;
if (isPostDetail) {
p.classList.toggle("active", false);
p.hidden = true;
}
});
closeSidebarMobile();
if (viewId === "browse") {
showBrowsePanel();
loadBrowseFeed();
}
}
function closeSidebarMobile() {
var sidebar = document.getElementById("left-sidebar");
if (sidebar) sidebar.classList.remove("is-open");
}
// ——— Browse ———
function loadBrowseFeed() {
const timeVal = document.getElementById("browse-time").value;
const langVal = document.getElementById("browse-language").value;
const typeVal = document.getElementById("browse-type").value;
const since = getSinceParam(timeVal);
const lang = langVal && langVal !== "all" ? langVal : null;
const type = typeVal && typeVal !== "all" ? typeVal : null;
const feedEl = document.getElementById("browse-feed");
feedEl.setAttribute("aria-busy", "true");
feedEl.innerHTML = "Could not load post.
";
msgEl.textContent = err.message || "Could not load post.";
});
}
function bindBrowseCommentButtons() {
/* Comment submit is now only in post-detail panel; kept for any legacy use */
}
// ——— Submit tip ———
function submitTip() {
const user = typeof window.getCurrentUser === "function" ? window.getCurrentUser() : null;
const msgEl = document.getElementById("submit-msg");
if (!user) {
msgEl.textContent = "Sign in to submit a tip.";
return;
}
const title = (document.getElementById("submit-title").value || "").trim();
const body = (document.getElementById("submit-body").value || "").trim();
const tagsStr = document.getElementById("submit-tags").value || "";
const tags = tagsStr.split(",").map(function (t) { return t.trim(); }).filter(Boolean);
const language = document.getElementById("submit-language").value || "general";
const type = document.getElementById("submit-type").value || "tip";
const confidence = document.getElementById("submit-confidence").value || "medium";
const contributing_agent = (document.getElementById("submit-agent").value || "").trim() || null;
if (!title || !body) {
msgEl.textContent = "Please provide a title and body.";
return;
}
msgEl.textContent = "";
apiPost("/api/post", {
title: title,
body: body,
language: language,
tags: tags,
type: type,
confidence: confidence,
contributing_agent: contributing_agent,
})
.then(function (out) {
msgEl.textContent = "Tip submitted: " + (out.title || title);
document.getElementById("submit-title").value = "";
document.getElementById("submit-body").value = "";
document.getElementById("submit-tags").value = "";
loadRecentTips();
})
.catch(function (err) {
msgEl.textContent = err.message || "Could not save. Please try again.";
});
}
// ——— Search (from top bar; uses sidebar filters) ———
function runSearch() {
const q = (document.getElementById("search-query").value || "").trim();
const resultsEl = document.getElementById("search-results");
if (!q) {
switchView("search");
resultsEl.innerHTML = SEARCH_EMPTY_HTML;
return;
}
const langVal = document.getElementById("browse-language").value;
const typeVal = document.getElementById("browse-type").value;
const language = langVal && langVal !== "all" ? langVal : null;
const type = typeVal && typeVal !== "all" ? typeVal : null;
const limit = Math.min(50, Math.max(5, parseInt(document.getElementById("search-limit").value, 10) || 10));
switchView("search");
resultsEl.setAttribute("aria-busy", "true");
resultsEl.innerHTML = "No tips yet.
";
if (countEl) countEl.textContent = "0 tips";
return;
}
listEl.innerHTML = articles.map(function (a) {
var rawTitle = a.title || "";
var title = escapeHtml(rawTitle.slice(0, 60)) + (rawTitle.length > 60 ? "…" : "");
var time = formatRelativeTime(a.created_at);
return (
'Could not load.
";
if (countEl) countEl.textContent = "";
});
}
// ——— Init ———
document.querySelectorAll(".sidebar-nav-item[data-view]").forEach(function (item) {
item.addEventListener("click", function (e) {
e.preventDefault();
switchView(item.getAttribute("data-view"));
});
});
var sidebarToggle = document.getElementById("sidebar-toggle");
var sidebarOverlay = document.getElementById("sidebar-overlay");
if (sidebarToggle) {
sidebarToggle.addEventListener("click", function () {
document.getElementById("left-sidebar").classList.toggle("is-open");
});
}
if (sidebarOverlay) {
sidebarOverlay.addEventListener("click", closeSidebarMobile);
}
document.getElementById("browse-time").addEventListener("change", loadBrowseFeed);
document.getElementById("browse-language").addEventListener("change", loadBrowseFeed);
document.getElementById("browse-type").addEventListener("change", loadBrowseFeed);
document.getElementById("submit-btn").addEventListener("click", submitTip);
document.getElementById("search-back-to-feed").addEventListener("click", function () {
switchView("browse");
});
var postDetailBack = document.getElementById("post-detail-back");
if (postDetailBack) {
postDetailBack.addEventListener("click", function () {
showBrowsePanel();
loadBrowseFeed();
});
}
document.getElementById("search-query").addEventListener("keydown", function (e) {
if (e.key === "Enter") runSearch();
});
window.__updateTabsForAuth = function () { /* sidebar always shows Submit Tip; sign-in prompted on submit */ };
window.__onHfAuthChange = function () {
var panel = document.getElementById("browse-panel");
if (panel && panel.classList.contains("active")) loadBrowseFeed();
};
window.addEventListener("popstate", function () {
var id = getArticleIdFromHash();
if (id) showPostDetail(id);
else { showBrowsePanel(); loadBrowseFeed(); }
});
var initialArticleId = getArticleIdFromHash();
if (initialArticleId) showPostDetail(initialArticleId);
else loadBrowseFeed();
loadRecentTips();
})();