Tragen Sie pro Seite die passenden URL-Slugs je Sprache ein. Links ist nur eine Bezeichnung (frei wählbar).
Der Sprachwechsel nutzt diese Zuordnungen automatisch.
Domain: —
Projekt: —
Sprachen erlaubt: —
Status wird geladen…
Tipp: Nutzen Sie in jeder Sprachspalte den vollständigen Pfad (z. B. /de/start).
Für die Startseite wird die Zeile Start verwendet.
`;
}
toast("Gespeichert", "Ihre Änderungen wurden gespeichert.");
updateStatus();
}catch(e){
toast("Fehler", "Speichern war nicht möglich. Bitte später erneut versuchen.");
}finally{
$("#btnSave").disabled = false;
$("#btnSave").textContent = "Änderungen speichern";
}
}
// === Init ===
async function init(){
state.public = getParam("public").trim();
state.domain = getParam("domain").trim().toLowerCase();
state.edit = getParam("edit").trim();
// Labels
$("#publicLabel").textContent = state.public || "—";
$("#domainLabel").textContent = state.domain || "—";
// Default links
if(state.public){
const configUrl = `${CDN_BASE}/config/${encodeURIComponent(state.public)}.json`;
$("#configLink").href = configUrl;
$("#configLink").textContent = configUrl;
const healthUrl = `${state.domain ? ("https://" + state.domain) : ""}/?tf_check=1`;
$("#healthLink").href = state.domain ? healthUrl : "#";
$("#healthLink").textContent = state.domain ? healthUrl : "—";
$("#snippetText").textContent = ``;
}
// Try load existing config
let cfg = null;
if(state.public){
cfg = await loadConfigIfAvailable();
}
// Determine entitlement
state.maxLanguages =
(cfg && cfg.entitlement && Number(cfg.entitlement.maxLanguages)) ||
(cfg && cfg.maxLanguages && Number(cfg.maxLanguages)) ||
DEFAULT_MAX_LANGUAGES;
$("#maxLangLabel").textContent = String(state.maxLanguages);
// Determine initial languages
if(cfg && Array.isArray(cfg.languages) && cfg.languages.length >= 2){
state.langCodes = cfg.languages.map(x=>String(x||"").toLowerCase());
}else{
state.langCodes = ["de","en"];
}
// Determine initial rows from pagesMapping if present (best effort)
// MVP: if mapping exists, we attempt to reconstruct rows by collecting unique slugs per language and grouping by shared targets.
// If not, show 4 example rows.
let rows = [];
if(cfg && cfg.pagesMapping && typeof cfg.pagesMapping === "object"){
// Collect slugs per lang
const langs = uniqLower(state.langCodes).slice(0, state.maxLanguages);
const slugSets = {};
langs.forEach(l=> slugSets[l] = new Set());
for(const [srcLang, mp] of Object.entries(cfg.pagesMapping)){
if(!slugSets[srcLang]) continue;
for(const srcSlug of Object.keys(mp||{})){
slugSets[srcLang].add(String(srcSlug));
}
}
// Build a simple row list from union by index (not perfect but usable)
const maxLen = Math.max(...langs.map(l => slugSets[l].size));
const arrays = langs.map(l => Array.from(slugSets[l]));
for(let i=0;i{
r.slugs[li] = arrays[li][i] ? normalizePath(arrays[li][i]) : "";
});
// give keyword from first non-empty slug tail
const any = r.slugs.find(Boolean) || "";
r.keyword = any ? any.split("/").filter(Boolean).slice(-1)[0] : "";
rows.push(r);
}
}
if(rows.length === 0){
rows = DEFAULT_EXAMPLE_ROWS.map(r => ({
keyword: r.keyword,
slugs: Array(state.langCodes.length).fill(""),
isExample: true
}));
}
// Ensure at least 4 rows visible (as you want), but unlimited via +
while(rows.length < 4){
rows.push({ keyword:"", slugs: Array(state.langCodes.length).fill(""), isExample:false });
}
state.rows = rows;
// Wire buttons
$("#btnSave").addEventListener("click", saveConfig);
$("#btnReload").addEventListener("click", async ()=>{
toast("Neu laden", "Konfiguration wird neu geladen…");
await init();
});
// Render
buildHeader();
buildRows();
updateStatus();
// If link incomplete, show helpful message
if(!state.public || !state.domain || !state.edit){
$("#statusMsg").textContent =
"Dieser Link ist unvollständig. Bitte verwenden Sie den vollständigen Edit-Link, den Sie von Tintenfrei erhalten haben.";
$("#statusDot").style.background = "var(--bad)";
}
}
init();