diff --git a/frontend/app.js b/frontend/app.js
index b5cee4a..57e29d0 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -2183,20 +2183,10 @@
window.open(popoutUrl, `popout_${data.vault}_${data.path.replace(/[^a-zA-Z0-9]/g, '_')}`, 'width=1000,height=700,menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=no');
});
- // Frontmatter
+ // Frontmatter — Accent Card
let fmSection = null;
if (data.frontmatter && Object.keys(data.frontmatter).length > 0) {
- const fmToggle = el("div", { class: "frontmatter-toggle" }, [
- document.createTextNode("▶ Frontmatter"),
- ]);
- const fmContent = el("div", { class: "frontmatter-content" }, [
- document.createTextNode(JSON.stringify(data.frontmatter, null, 2)),
- ]);
- fmToggle.addEventListener("click", () => {
- fmContent.classList.toggle("open");
- fmToggle.textContent = fmContent.classList.contains("open") ? "▼ Frontmatter" : "▶ Frontmatter";
- });
- fmSection = el("div", {}, [fmToggle, fmContent]);
+ fmSection = buildFrontmatterCard(data.frontmatter);
}
// Content container (rendered HTML)
@@ -3419,6 +3409,186 @@
});
}
+ // ---------------------------------------------------------------------------
+ // Frontmatter Accent Card Builder
+ // ---------------------------------------------------------------------------
+
+ function buildFrontmatterCard(frontmatter) {
+ // Helper: format date
+ function formatDate(iso) {
+ if (!iso) return '—';
+ const d = new Date(iso);
+ const date = d.toISOString().slice(0, 10);
+ const time = d.toTimeString().slice(0, 5);
+ return `${date} · ${time}`;
+ }
+
+ // Extract boolean flags
+ const booleanFlags = ['publish', 'favoris', 'template', 'task', 'archive', 'draft', 'private']
+ .map(key => ({ key, value: !!frontmatter[key] }));
+
+ // Toggle state
+ let isOpen = true;
+
+ // Build header with chevron
+ const chevron = el("span", { class: "fm-chevron open" });
+ chevron.innerHTML = '';
+
+ const fmHeader = el("div", { class: "fm-header" }, [
+ chevron,
+ document.createTextNode("Frontmatter")
+ ]);
+
+ // ZONE 1: Top strip
+ const topBadges = [];
+
+ // Title badge
+ const title = frontmatter.titre || frontmatter.title || '';
+ if (title) {
+ topBadges.push(el("span", { class: "ac-title" }, [document.createTextNode(`"${title}"`)]));
+ }
+
+ // Status badge
+ if (frontmatter.statut) {
+ const statusBadge = el("span", { class: "ac-badge green" }, [
+ el("span", { class: "ac-dot" }),
+ document.createTextNode(frontmatter.statut)
+ ]);
+ topBadges.push(statusBadge);
+ }
+
+ // Category badge
+ if (frontmatter.catégorie || frontmatter.categorie) {
+ const cat = frontmatter.catégorie || frontmatter.categorie;
+ const catBadge = el("span", { class: "ac-badge blue" }, [
+ document.createTextNode(cat)
+ ]);
+ topBadges.push(catBadge);
+ }
+
+ // Publish badge
+ if (frontmatter.publish) {
+ topBadges.push(el("span", { class: "ac-badge purple" }, [
+ document.createTextNode("publié")
+ ]));
+ }
+
+ // Favoris badge
+ if (frontmatter.favoris) {
+ topBadges.push(el("span", { class: "ac-badge purple" }, [
+ document.createTextNode("favori")
+ ]));
+ }
+
+ const acTop = el("div", { class: "ac-top" }, topBadges);
+
+ // ZONE 2: Body 2 columns
+ const leftCol = el("div", { class: "ac-col" }, [
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("auteur")]),
+ el("span", { class: "ac-v" }, [document.createTextNode(frontmatter.auteur || '—')])
+ ]),
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("catégorie")]),
+ el("span", { class: "ac-v" }, [document.createTextNode(frontmatter.catégorie || frontmatter.categorie || '—')])
+ ]),
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("statut")]),
+ el("span", { class: "ac-v" }, [document.createTextNode(frontmatter.statut || '—')])
+ ]),
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("aliases")]),
+ el("span", { class: "ac-v muted" }, [
+ document.createTextNode(
+ frontmatter.aliases && frontmatter.aliases.length > 0
+ ? frontmatter.aliases.join(', ')
+ : '[]'
+ )
+ ])
+ ])
+ ]);
+
+ const rightCol = el("div", { class: "ac-col" }, [
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("creation_date")]),
+ el("span", { class: "ac-v mono" }, [document.createTextNode(formatDate(frontmatter.creation_date))])
+ ]),
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("modification_date")]),
+ el("span", { class: "ac-v mono" }, [document.createTextNode(formatDate(frontmatter.modification_date))])
+ ]),
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("publish")]),
+ el("span", { class: "ac-v" }, [document.createTextNode(String(frontmatter.publish || false))])
+ ]),
+ el("div", { class: "ac-row" }, [
+ el("span", { class: "ac-k" }, [document.createTextNode("favoris")]),
+ el("span", { class: "ac-v" }, [document.createTextNode(String(frontmatter.favoris || false))])
+ ])
+ ]);
+
+ const acBody = el("div", { class: "ac-body" }, [leftCol, rightCol]);
+
+ // ZONE 3: Tags row
+ const tagPills = [];
+ if (frontmatter.tags && frontmatter.tags.length > 0) {
+ frontmatter.tags.forEach(tag => {
+ tagPills.push(el("span", { class: "ac-tag" }, [document.createTextNode(tag)]));
+ });
+ }
+
+ const acTagsRow = el("div", { class: "ac-tags-row" }, [
+ el("span", { class: "ac-tags-k" }, [document.createTextNode("tags")]),
+ el("div", { class: "ac-tags-wrap" }, tagPills)
+ ]);
+
+ // ZONE 4: Flags row
+ const flagChips = [];
+ booleanFlags.forEach(flag => {
+ const chipClass = flag.value ? "flag-chip on" : "flag-chip off";
+ flagChips.push(el("span", { class: chipClass }, [
+ el("span", { class: "flag-dot" }),
+ document.createTextNode(flag.key)
+ ]));
+ });
+
+ const acFlagsRow = el("div", { class: "ac-flags-row" }, [
+ el("span", { class: "ac-flags-k" }, [document.createTextNode("flags")]),
+ ...flagChips
+ ]);
+
+ // Assemble the card
+ const acCard = el("div", { class: "ac-card" }, [
+ acTop,
+ acBody,
+ acTagsRow,
+ acFlagsRow
+ ]);
+
+ // Toggle functionality
+ fmHeader.addEventListener("click", () => {
+ isOpen = !isOpen;
+ if (isOpen) {
+ acCard.style.display = "block";
+ chevron.classList.remove("closed");
+ chevron.classList.add("open");
+ } else {
+ acCard.style.display = "none";
+ chevron.classList.remove("open");
+ chevron.classList.add("closed");
+ }
+ safeCreateIcons();
+ });
+
+ // Wrap in section
+ const fmSection = el("div", { class: "fm-section" }, [
+ fmHeader,
+ acCard
+ ]);
+
+ return fmSection;
+ }
+
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
diff --git a/frontend/popout.html b/frontend/popout.html
index 9a6e907..2c9571c 100644
--- a/frontend/popout.html
+++ b/frontend/popout.html
@@ -135,23 +135,9 @@
header.appendChild(actionsDiv);
area.appendChild(header);
- // Frontmatter
+ // Frontmatter — Accent Card
if (data.frontmatter && Object.keys(data.frontmatter).length > 0) {
- const fmSection = document.createElement("div");
- const fmToggle = document.createElement("div");
- fmToggle.className = "frontmatter-toggle";
- fmToggle.textContent = "▶ Frontmatter";
- const fmContent = document.createElement("div");
- fmContent.className = "frontmatter-content";
- fmContent.textContent = JSON.stringify(data.frontmatter, null, 2);
-
- fmToggle.addEventListener("click", () => {
- fmContent.classList.toggle("open");
- fmToggle.textContent = fmContent.classList.contains("open") ? "▼ Frontmatter" : "▶ Frontmatter";
- });
-
- fmSection.appendChild(fmToggle);
- fmSection.appendChild(fmContent);
+ const fmSection = buildFrontmatterCard(data.frontmatter);
area.appendChild(fmSection);
}
@@ -197,6 +183,186 @@
return item;
}
+ function buildFrontmatterCard(frontmatter) {
+ // Helper: format date
+ function formatDate(iso) {
+ if (!iso) return '—';
+ const d = new Date(iso);
+ const date = d.toISOString().slice(0, 10);
+ const time = d.toTimeString().slice(0, 5);
+ return `${date} · ${time}`;
+ }
+
+ // Helper: create element
+ function el(tag, className, children) {
+ const e = document.createElement(tag);
+ if (className) e.className = className;
+ if (children) {
+ children.forEach(c => { if (c) e.appendChild(c); });
+ }
+ return e;
+ }
+
+ // Extract boolean flags
+ const booleanFlags = ['publish', 'favoris', 'template', 'task', 'archive', 'draft', 'private']
+ .map(key => ({ key, value: !!frontmatter[key] }));
+
+ // Toggle state
+ let isOpen = true;
+
+ // Build header with chevron
+ const chevron = el("span", "fm-chevron open");
+ chevron.innerHTML = '';
+
+ const fmHeader = el("div", "fm-header");
+ fmHeader.appendChild(chevron);
+ fmHeader.appendChild(document.createTextNode("Frontmatter"));
+
+ // ZONE 1: Top strip
+ const acTop = el("div", "ac-top");
+
+ // Title badge
+ const title = frontmatter.titre || frontmatter.title || '';
+ if (title) {
+ const titleSpan = el("span", "ac-title");
+ titleSpan.textContent = `"${title}"`;
+ acTop.appendChild(titleSpan);
+ }
+
+ // Status badge
+ if (frontmatter.statut) {
+ const statusBadge = el("span", "ac-badge green");
+ const dot = el("span", "ac-dot");
+ statusBadge.appendChild(dot);
+ statusBadge.appendChild(document.createTextNode(frontmatter.statut));
+ acTop.appendChild(statusBadge);
+ }
+
+ // Category badge
+ if (frontmatter.catégorie || frontmatter.categorie) {
+ const cat = frontmatter.catégorie || frontmatter.categorie;
+ const catBadge = el("span", "ac-badge blue");
+ catBadge.textContent = cat;
+ acTop.appendChild(catBadge);
+ }
+
+ // Publish badge
+ if (frontmatter.publish) {
+ const pubBadge = el("span", "ac-badge purple");
+ pubBadge.textContent = "publié";
+ acTop.appendChild(pubBadge);
+ }
+
+ // Favoris badge
+ if (frontmatter.favoris) {
+ const favBadge = el("span", "ac-badge purple");
+ favBadge.textContent = "favori";
+ acTop.appendChild(favBadge);
+ }
+
+ // ZONE 2: Body 2 columns
+ const leftCol = el("div", "ac-col");
+
+ const row1 = el("div", "ac-row");
+ row1.innerHTML = 'auteur' + (frontmatter.auteur || '—') + '';
+ leftCol.appendChild(row1);
+
+ const row2 = el("div", "ac-row");
+ row2.innerHTML = 'catégorie' + (frontmatter.catégorie || frontmatter.categorie || '—') + '';
+ leftCol.appendChild(row2);
+
+ const row3 = el("div", "ac-row");
+ row3.innerHTML = 'statut' + (frontmatter.statut || '—') + '';
+ leftCol.appendChild(row3);
+
+ const row4 = el("div", "ac-row");
+ const aliases = frontmatter.aliases && frontmatter.aliases.length > 0 ? frontmatter.aliases.join(', ') : '[]';
+ row4.innerHTML = 'aliases' + aliases + '';
+ leftCol.appendChild(row4);
+
+ const rightCol = el("div", "ac-col");
+
+ const row5 = el("div", "ac-row");
+ row5.innerHTML = 'creation_date' + formatDate(frontmatter.creation_date) + '';
+ rightCol.appendChild(row5);
+
+ const row6 = el("div", "ac-row");
+ row6.innerHTML = 'modification_date' + formatDate(frontmatter.modification_date) + '';
+ rightCol.appendChild(row6);
+
+ const row7 = el("div", "ac-row");
+ row7.innerHTML = 'publish' + String(frontmatter.publish || false) + '';
+ rightCol.appendChild(row7);
+
+ const row8 = el("div", "ac-row");
+ row8.innerHTML = 'favoris' + String(frontmatter.favoris || false) + '';
+ rightCol.appendChild(row8);
+
+ const acBody = el("div", "ac-body");
+ acBody.appendChild(leftCol);
+ acBody.appendChild(rightCol);
+
+ // ZONE 3: Tags row
+ const acTagsRow = el("div", "ac-tags-row");
+ const tagsLabel = el("span", "ac-tags-k");
+ tagsLabel.textContent = "tags";
+ acTagsRow.appendChild(tagsLabel);
+
+ const tagsWrap = el("div", "ac-tags-wrap");
+ if (frontmatter.tags && frontmatter.tags.length > 0) {
+ frontmatter.tags.forEach(tag => {
+ const tagSpan = el("span", "ac-tag");
+ tagSpan.textContent = tag;
+ tagsWrap.appendChild(tagSpan);
+ });
+ }
+ acTagsRow.appendChild(tagsWrap);
+
+ // ZONE 4: Flags row
+ const acFlagsRow = el("div", "ac-flags-row");
+ const flagsLabel = el("span", "ac-flags-k");
+ flagsLabel.textContent = "flags";
+ acFlagsRow.appendChild(flagsLabel);
+
+ booleanFlags.forEach(flag => {
+ const chipClass = flag.value ? "flag-chip on" : "flag-chip off";
+ const chip = el("span", chipClass);
+ const dot = el("span", "flag-dot");
+ chip.appendChild(dot);
+ chip.appendChild(document.createTextNode(flag.key));
+ acFlagsRow.appendChild(chip);
+ });
+
+ // Assemble the card
+ const acCard = el("div", "ac-card");
+ acCard.appendChild(acTop);
+ acCard.appendChild(acBody);
+ acCard.appendChild(acTagsRow);
+ acCard.appendChild(acFlagsRow);
+
+ // Toggle functionality
+ fmHeader.addEventListener("click", () => {
+ isOpen = !isOpen;
+ if (isOpen) {
+ acCard.style.display = "block";
+ chevron.classList.remove("closed");
+ chevron.classList.add("open");
+ } else {
+ acCard.style.display = "none";
+ chevron.classList.remove("open");
+ chevron.classList.add("closed");
+ }
+ if (window.lucide) window.lucide.createIcons();
+ });
+
+ // Wrap in section
+ const fmSection = el("div", "fm-section");
+ fmSection.appendChild(fmHeader);
+ fmSection.appendChild(acCard);
+
+ return fmSection;
+ }
+
window.onload = initPopout;