188 lines
6.2 KiB
JavaScript
188 lines
6.2 KiB
JavaScript
/* Section « Liens » des notes — Fusion #215 (ACC-1.2 + ACC-1.3).
|
|
*
|
|
* Module autonome (vanilla, zéro dépendance, zéro build) : il écoute
|
|
* l'événement « obsigate:file-rendered » émis par renderFile
|
|
* (frontend/app.js) et rend sa section à la fin de #content-area :
|
|
*
|
|
* - ACC-1.2 : si la note est une projection TikTak (frontmatter
|
|
* « tiktak_id » écrit par la synchro TikTak ↔ ObsiGate), un bandeau
|
|
* « Tâche TikTak projetée » avec le bouton « Ouvrir dans TikTak »
|
|
* (lien profond https://tiktak.dracodev.net/?task=<id>) ;
|
|
* - ACC-1.3 : les références croisées de la note (GET /api/refs),
|
|
* ajout par collage d'une URL profonde, retrait en un clic.
|
|
*
|
|
* Textes via window.t (i18n FR/EN — clés « draco.links.* » des locales).
|
|
*/
|
|
(function () {
|
|
"use strict";
|
|
|
|
var COLORS = {
|
|
obsigate: "#7a5af5",
|
|
flowdeck: "#2f6fed",
|
|
tiktak: "#e8762c",
|
|
kanbante: "#0d9488",
|
|
};
|
|
|
|
function t(key, fallback) {
|
|
try {
|
|
var v = window.t && window.t(key);
|
|
return v && v !== key ? v : fallback;
|
|
} catch (e) {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function authHeaders() {
|
|
var tok = null;
|
|
try {
|
|
tok = sessionStorage.getItem("obsigate_access_token");
|
|
} catch (e) { /* stockage indisponible */ }
|
|
return tok ? { Authorization: "Bearer " + tok } : {};
|
|
}
|
|
|
|
async function api(path, opts) {
|
|
var options = opts || {};
|
|
options.headers = Object.assign(
|
|
{ "Content-Type": "application/json" },
|
|
authHeaders(),
|
|
options.headers || {}
|
|
);
|
|
options.credentials = "same-origin";
|
|
var resp = await fetch(path, options);
|
|
if (!resp.ok) {
|
|
var detail = "";
|
|
try {
|
|
detail = (await resp.json()).detail || "";
|
|
} catch (e) { /* corps non JSON */ }
|
|
throw new Error(detail || "HTTP " + resp.status);
|
|
}
|
|
return resp.json();
|
|
}
|
|
|
|
function el(tag, cls, text) {
|
|
var node = document.createElement(tag);
|
|
if (cls) node.className = cls;
|
|
if (text !== undefined) node.textContent = text;
|
|
return node;
|
|
}
|
|
|
|
async function render(vault, path) {
|
|
var area = document.getElementById("content-area");
|
|
if (!area || !vault || !path) return;
|
|
var old = document.getElementById("draco-liens");
|
|
if (old) old.remove();
|
|
|
|
var section = el("section", "draco-liens");
|
|
section.id = "draco-liens";
|
|
var title = el("h3", "draco-liens-title", t("draco.links.title", "Liens"));
|
|
section.appendChild(title);
|
|
area.appendChild(section);
|
|
|
|
// ACC-1.2 — note projetée depuis TikTak ?
|
|
if (/\.md$/i.test(path)) {
|
|
try {
|
|
var rawUrl = "/api/file/" + encodeURIComponent(vault) + "/raw?path=" +
|
|
encodeURIComponent(path);
|
|
var rawData = await api(rawUrl);
|
|
var m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(rawData.raw || "");
|
|
var tid = m && /^tiktak_id:\s*(\d+)\s*$/m.exec(m[1]);
|
|
if (tid) {
|
|
var bandeau = el("div", "draco-liens-projete");
|
|
bandeau.appendChild(el("span", "draco-liens-badge",
|
|
t("draco.links.projected", "Tâche TikTak projetée")));
|
|
var ouvrir = el("a", "btn-action draco-liens-ouvrir",
|
|
t("draco.links.openInTikTak", "Ouvrir dans TikTak"));
|
|
ouvrir.href = "https://tiktak.dracodev.net/?task=" + tid[1];
|
|
ouvrir.target = "_blank";
|
|
ouvrir.rel = "noopener";
|
|
bandeau.appendChild(ouvrir);
|
|
section.appendChild(bandeau);
|
|
}
|
|
} catch (e) { /* la détection est opportuniste : silence */ }
|
|
}
|
|
|
|
// ACC-1.3 — références croisées de la note
|
|
var list = el("ul", "draco-liens-list");
|
|
section.appendChild(list);
|
|
var erreur = el("p", "draco-liens-erreur");
|
|
erreur.hidden = true;
|
|
section.appendChild(erreur);
|
|
|
|
async function refresh() {
|
|
list.innerHTML = "";
|
|
try {
|
|
var data = await api("/api/refs?vault=" + encodeURIComponent(vault) +
|
|
"&path=" + encodeURIComponent(path));
|
|
(data.refs || []).forEach(function (r) {
|
|
var li = el("li", "draco-liens-item");
|
|
var dot = el("span", "draco-ref-dot");
|
|
dot.style.background = COLORS[r.target_app] || "#888";
|
|
li.appendChild(dot);
|
|
var a = el("a", "", r.label);
|
|
a.href = r.target_url;
|
|
a.target = "_blank";
|
|
a.rel = "noopener";
|
|
li.appendChild(a);
|
|
var del = el("button", "draco-liens-del", "✕");
|
|
del.type = "button";
|
|
del.title = t("draco.links.remove", "Retirer le lien");
|
|
del.addEventListener("click", async function () {
|
|
try {
|
|
await api("/api/refs/" + encodeURIComponent(r.id), { method: "DELETE" });
|
|
refresh();
|
|
} catch (e) {
|
|
erreur.textContent = e.message;
|
|
erreur.hidden = false;
|
|
}
|
|
});
|
|
li.appendChild(del);
|
|
list.appendChild(li);
|
|
});
|
|
} catch (e) { /* section facultative : ne jamais casser la note */ }
|
|
}
|
|
|
|
var form = el("form", "draco-liens-form");
|
|
var urlInput = el("input");
|
|
urlInput.type = "url";
|
|
urlInput.required = true;
|
|
urlInput.placeholder = t("draco.links.urlPlaceholder",
|
|
"Coller une URL profonde (Flowdeck, TikTak, Gitea, ObsiGate)…");
|
|
var labelInput = el("input");
|
|
labelInput.type = "text";
|
|
labelInput.placeholder = t("draco.links.labelPlaceholder", "Libellé (optionnel)");
|
|
var submit = el("button", "btn-action", t("draco.links.add", "Lier"));
|
|
submit.type = "submit";
|
|
form.appendChild(urlInput);
|
|
form.appendChild(labelInput);
|
|
form.appendChild(submit);
|
|
form.addEventListener("submit", async function (ev) {
|
|
ev.preventDefault();
|
|
erreur.hidden = true;
|
|
try {
|
|
await api("/api/refs", {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
vault: vault,
|
|
path: path,
|
|
url: urlInput.value.trim(),
|
|
label: labelInput.value.trim() || null,
|
|
}),
|
|
});
|
|
urlInput.value = "";
|
|
labelInput.value = "";
|
|
refresh();
|
|
} catch (e) {
|
|
erreur.textContent = e.message;
|
|
erreur.hidden = false;
|
|
}
|
|
});
|
|
section.appendChild(form);
|
|
|
|
refresh();
|
|
}
|
|
|
|
document.addEventListener("obsigate:file-rendered", function (ev) {
|
|
if (ev.detail) render(ev.detail.vault, ev.detail.path);
|
|
});
|
|
})();
|