mirror of
https://github.com/infinition/Bjorn.git
synced 2026-03-08 05:51:59 +00:00
Compare commits
2 Commits
9b04b1eb50
...
f8c26288ab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8c26288ab | ||
|
|
8fd63ce5b1 |
560
index.html
560
index.html
@@ -33,7 +33,7 @@
|
|||||||
<meta name="msapplication-TileColor" id="ms-tile-color" content="#0B0C0E">
|
<meta name="msapplication-TileColor" id="ms-tile-color" content="#0B0C0E">
|
||||||
<meta name="theme-color" id="theme-color-meta" content="#0B0C0E">
|
<meta name="theme-color" id="theme-color-meta" content="#0B0C0E">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="manifest" id="manifest-link" href="wiki/manifest.json">
|
<link rel="manifest" id="manifest-link" href="wiki/manifest.pwa.json">
|
||||||
|
|
||||||
<script src="wiki/config.js"></script>
|
<script src="wiki/config.js"></script>
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<script src="https://unpkg.com/lucide@latest"></script>
|
<script src="https://unpkg.com/lucide@latest"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked@4.3.0/marked.min.js"></script>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"></script>
|
||||||
|
|
||||||
@@ -51,6 +51,10 @@
|
|||||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js"></script>
|
||||||
|
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Inter:wght@400;500;600&display=swap"
|
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Inter:wght@400;500;600&display=swap"
|
||||||
rel="stylesheet">
|
rel="stylesheet">
|
||||||
@@ -174,9 +178,12 @@
|
|||||||
#theme-toggle-mobile,
|
#theme-toggle-mobile,
|
||||||
.badge-sm,
|
.badge-sm,
|
||||||
.pagination-card,
|
.pagination-card,
|
||||||
#toc-btn-mobile,
|
|
||||||
#toc-btn-desktop,
|
#toc-btn-desktop,
|
||||||
#clear-highlight-btn {
|
#search-btn-mobile,
|
||||||
|
#clear-highlight-btn,
|
||||||
|
.kb-mobile-toggle,
|
||||||
|
.kb-mobile-toc-toggle,
|
||||||
|
.kb-scroll-top-btn {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -190,9 +197,12 @@
|
|||||||
#theme-toggle-mobile::after,
|
#theme-toggle-mobile::after,
|
||||||
.badge-sm::after,
|
.badge-sm::after,
|
||||||
.pagination-card::after,
|
.pagination-card::after,
|
||||||
#toc-btn-mobile::after,
|
|
||||||
#toc-btn-desktop::after,
|
#toc-btn-desktop::after,
|
||||||
#clear-highlight-btn::after {
|
#search-btn-mobile::after,
|
||||||
|
#clear-highlight-btn::after,
|
||||||
|
.kb-mobile-toggle::after,
|
||||||
|
.kb-mobile-toc-toggle::after,
|
||||||
|
.kb-scroll-top-btn::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -211,9 +221,12 @@
|
|||||||
#theme-toggle-mobile:hover::after,
|
#theme-toggle-mobile:hover::after,
|
||||||
.badge-sm:hover::after,
|
.badge-sm:hover::after,
|
||||||
.pagination-card:hover::after,
|
.pagination-card:hover::after,
|
||||||
#toc-btn-mobile:hover::after,
|
|
||||||
#toc-btn-desktop:hover::after,
|
#toc-btn-desktop:hover::after,
|
||||||
#clear-highlight-btn:hover::after {
|
#search-btn-mobile:hover::after,
|
||||||
|
#clear-highlight-btn:hover::after,
|
||||||
|
.kb-mobile-toggle:hover::after,
|
||||||
|
.kb-mobile-toc-toggle:hover::after,
|
||||||
|
.kb-scroll-top-btn:hover::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,6 +387,29 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.markdown-body table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table th,
|
||||||
|
.markdown-body table td {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table th {
|
||||||
|
font-weight: 600;
|
||||||
|
background: var(--accent-dim);
|
||||||
|
color: var(--text-heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table tr:hover td {
|
||||||
|
background: var(--accent-dim);
|
||||||
|
}
|
||||||
|
|
||||||
.markdown-body img {
|
.markdown-body img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -385,6 +421,14 @@
|
|||||||
border-color: var(--accent-green);
|
border-color: var(--accent-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- KATEX STYLES --- */
|
||||||
|
.katex-display {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
padding: 1rem 0;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
#lightbox {
|
#lightbox {
|
||||||
transition: opacity 0.3s ease, visibility 0.3s;
|
transition: opacity 0.3s ease, visibility 0.3s;
|
||||||
}
|
}
|
||||||
@@ -496,14 +540,122 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#scroll-top-btn {
|
/* Bottom bar: Menu | Scroll to top (center) | TOC — aligne les 3 boutons à la même hauteur */
|
||||||
|
.kb-bottom-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
|
||||||
|
padding-left: calc(1.5rem + env(safe-area-inset-left, 0px));
|
||||||
|
padding-right: calc(1.5rem + env(safe-area-inset-right, 0px));
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
z-index: 80;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-left {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-center {
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-btn {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: var(--bg-sidebar);
|
||||||
|
color: var(--text-main);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-btn:hover {
|
||||||
|
border-color: var(--accent-green);
|
||||||
|
color: var(--accent-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-btn.kb-bottom-bar-btn-hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-scroll-top-btn {
|
||||||
transition: opacity 0.3s, transform 0.3s;
|
transition: opacity 0.3s, transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#scroll-top-btn.hidden {
|
#scroll-top-btn:not(.visible) {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transform: translateY(20px);
|
transform: translateY(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll-top-btn.visible {
|
||||||
|
opacity: 0.9;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Menu (gauche) : visible uniquement en mobile (sidebar masquée) */
|
||||||
|
.kb-mobile-toggle {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 769px) {
|
||||||
|
.kb-bottom-bar-left .kb-mobile-toggle {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-left {
|
||||||
|
min-width: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TOC (droite) : visible en mobile et quand la TOC sidebar n'est pas affichée (< xl) */
|
||||||
|
.kb-mobile-toc-toggle {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.kb-bottom-bar-right .kb-mobile-toc-toggle {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kb-bottom-bar-right {
|
||||||
|
min-width: 0;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- CLEAR HIGHLIGHT BTN (NEW) --- */
|
/* --- CLEAR HIGHLIGHT BTN (NEW) --- */
|
||||||
@@ -582,7 +734,7 @@
|
|||||||
#mobile-toc-sidebar,
|
#mobile-toc-sidebar,
|
||||||
header,
|
header,
|
||||||
#theme-toggle-desktop,
|
#theme-toggle-desktop,
|
||||||
#scroll-top-btn,
|
#kb-bottom-bar,
|
||||||
#reading-progress-bar,
|
#reading-progress-bar,
|
||||||
#overlay,
|
#overlay,
|
||||||
.copy-btn,
|
.copy-btn,
|
||||||
@@ -852,9 +1004,9 @@
|
|||||||
<span id="label-menu" class="text-xs font-mono font-bold uppercase tracking-wider">Menu</span>
|
<span id="label-menu" class="text-xs font-mono font-bold uppercase tracking-wider">Menu</span>
|
||||||
<i data-lucide="menu" class="w-5 h-5 text-hack-green"></i>
|
<i data-lucide="menu" class="w-5 h-5 text-hack-green"></i>
|
||||||
</button>
|
</button>
|
||||||
<button id="toc-btn-mobile" class="text-gray-400 hover:text-hack-green p-2 transition-colors"
|
<button id="search-btn-mobile" type="button" class="text-gray-400 hover:text-hack-green p-2 transition-colors"
|
||||||
aria-label="Toggle Table of Contents">
|
title="Search (Ctrl+K)" aria-label="Search" onclick="openSearch()">
|
||||||
<i data-lucide="hash" class="w-5 h-5"></i>
|
<i data-lucide="search" class="w-5 h-5"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -862,6 +1014,24 @@
|
|||||||
<div id="overlay" class="fixed inset-0 bg-black/80 backdrop-blur-[2px] z-[65] hidden transition-opacity opacity-0">
|
<div id="overlay" class="fixed inset-0 bg-black/80 backdrop-blur-[2px] z-[65] hidden transition-opacity opacity-0">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="kb-bottom-bar" class="kb-bottom-bar">
|
||||||
|
<div class="kb-bottom-bar-cell kb-bottom-bar-left">
|
||||||
|
<button id="kb-mobile-toggle" type="button" class="kb-bottom-bar-btn kb-mobile-toggle" title="Menu" aria-label="Open Menu">
|
||||||
|
<i data-lucide="menu" class="w-5 h-5"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="kb-bottom-bar-cell kb-bottom-bar-center">
|
||||||
|
<button id="scroll-top-btn" type="button" class="kb-bottom-bar-btn kb-scroll-top-btn p-3 rounded-full bg-hack-greenDim border border-hack-border text-hack-green hover:bg-hack-green hover:text-white shadow-lg" title="Back to Top" aria-label="Back to Top">
|
||||||
|
<i data-lucide="arrow-up" class="w-5 h-5"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="kb-bottom-bar-cell kb-bottom-bar-right">
|
||||||
|
<button id="kb-mobile-toc-toggle" type="button" class="kb-bottom-bar-btn kb-mobile-toc-toggle" title="Table of Contents" aria-label="Toggle Table of Contents">
|
||||||
|
<i data-lucide="list" class="w-5 h-5"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<aside id="sidebar"
|
<aside id="sidebar"
|
||||||
class="sidebar-mobile fixed top-0 bottom-0 left-0 z-[70] w-[280px] bg-hack-sidebar border-r border-hack-border transform -translate-x-full md:translate-x-0 md:static md:h-full flex flex-col shadow-2xl md:shadow-none transition-transform duration-300">
|
class="sidebar-mobile fixed top-0 bottom-0 left-0 z-[70] w-[280px] bg-hack-sidebar border-r border-hack-border transform -translate-x-full md:translate-x-0 md:static md:h-full flex flex-col shadow-2xl md:shadow-none transition-transform duration-300">
|
||||||
|
|
||||||
@@ -913,18 +1083,6 @@
|
|||||||
class="w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity"></i>
|
class="w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-group mb-1">
|
|
||||||
<button id="btn-root-readme"
|
|
||||||
class="w-full text-left px-2 py-2 flex items-center justify-between text-gray-500 hover:text-hack-heading transition-colors cursor-pointer group rounded hover:bg-hack-bg focus:bg-hack-bg outline-none"
|
|
||||||
onclick="loadContent('', CONFIG.ui.rootReadmeTitle, 'README.md', true, true)" tabindex="0">
|
|
||||||
<div
|
|
||||||
class="flex items-center gap-2 text-[11px] font-bold uppercase tracking-widest font-mono text-hack-green">
|
|
||||||
<i data-lucide="home" class="w-3 h-3"></i> <span id="label-root-readme">Project Home</span>
|
|
||||||
</div>
|
|
||||||
<i data-lucide="chevron-right"
|
|
||||||
class="w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="pb-4 px-3 space-y-2 flex-none" id="nav-container">
|
<nav class="pb-4 px-3 space-y-2 flex-none" id="nav-container">
|
||||||
@@ -1036,12 +1194,6 @@
|
|||||||
title="Clear Search Highlights" aria-label="Clear Search Highlights">
|
title="Clear Search Highlights" aria-label="Clear Search Highlights">
|
||||||
<i data-lucide="eraser" class="w-5 h-5"></i>
|
<i data-lucide="eraser" class="w-5 h-5"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button id="scroll-top-btn"
|
|
||||||
class="absolute bottom-6 right-6 p-3 rounded-full bg-hack-greenDim border border-hack-border text-hack-green hover:bg-hack-green hover:text-white shadow-lg hidden z-30"
|
|
||||||
title="Back to Top" aria-label="Back to Top">
|
|
||||||
<i data-lucide="arrow-up" class="w-5 h-5"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="search-modal-overlay"
|
<div id="search-modal-overlay"
|
||||||
@@ -1098,16 +1250,53 @@
|
|||||||
return `<img src="${href}" alt="${text || ''}" title="${title || ''}" loading="lazy" class="rounded-lg border border-hack-border bg-black/20">`;
|
return `<img src="${href}" alt="${text || ''}" title="${title || ''}" loading="lazy" class="rounded-lg border border-hack-border bg-black/20">`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- TABLE RENDERER (compatible marked 4.x ; en 5+ le token est un objet) ---
|
||||||
renderer.table = function (header, body) {
|
renderer.table = function (header, body) {
|
||||||
return `<div class="overflow-x-auto my-4 border border-hack-border rounded-lg"><table class="w-full text-left text-sm border-collapse"><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
|
const wrap = (html) => `<div class="overflow-x-auto my-4 border border-hack-border rounded-lg">${html}</div>`;
|
||||||
|
if (typeof header === 'object' && header !== null) {
|
||||||
|
const token = header;
|
||||||
|
const thead = (typeof token.header === 'string') ? token.header : (Array.isArray(token.rows) ? token.rows[0] || '' : '');
|
||||||
|
const rows = Array.isArray(token.rows) ? token.rows.slice(1).join('') : (typeof body === 'string' ? body : '');
|
||||||
|
return wrap(`<table class="w-full text-left text-sm border-collapse"><thead>${thead}</thead><tbody>${rows}</tbody></table>`);
|
||||||
|
}
|
||||||
|
const h = typeof header === 'string' ? header : '';
|
||||||
|
const b = typeof body === 'string' ? body : '';
|
||||||
|
return wrap(`<table class="w-full text-left text-sm border-collapse"><thead>${h}</thead><tbody>${b}</tbody></table>`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- EXTENSION POUR PROTÉGER LE LATEX DANS MARKED ---
|
||||||
|
const mathExtension = {
|
||||||
|
name: 'math',
|
||||||
|
level: 'inline',
|
||||||
|
start(src) {
|
||||||
|
const match = src.match(/\$/);
|
||||||
|
return match ? match.index : -1;
|
||||||
|
},
|
||||||
|
tokenizer(src, tokens) {
|
||||||
|
const rule = /^(\$\$[\s\S]+?\$\$|\$[\s\S]+?\$)/;
|
||||||
|
const match = rule.exec(src);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
type: 'math',
|
||||||
|
raw: match[0],
|
||||||
|
text: match[0]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
return token.text;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
marked.use({ extensions: [mathExtension] });
|
||||||
marked.use({ renderer });
|
marked.use({ renderer });
|
||||||
marked.setOptions({ breaks: false, gfm: true });
|
marked.setOptions({ breaks: false, gfm: true });
|
||||||
|
|
||||||
// Safety check for CONFIG
|
// Safety check for CONFIG
|
||||||
let STATE = {
|
let STATE = {
|
||||||
wikiData: {},
|
wikiData: {},
|
||||||
|
rootMdFiles: [],
|
||||||
|
wikiId: '',
|
||||||
contentCache: {},
|
contentCache: {},
|
||||||
searchIndex: [],
|
searchIndex: [],
|
||||||
expandedSections: new Set(),
|
expandedSections: new Set(),
|
||||||
@@ -1121,6 +1310,13 @@
|
|||||||
if (typeof CONFIG !== 'undefined') {
|
if (typeof CONFIG !== 'undefined') {
|
||||||
STATE.repo = CONFIG.repo;
|
STATE.repo = CONFIG.repo;
|
||||||
STATE.branch = CONFIG.branch;
|
STATE.branch = CONFIG.branch;
|
||||||
|
// Identifiant unique par repo pour éviter conflits de cache localStorage entre différentes GitHub Pages
|
||||||
|
const shortHash = (str) => {
|
||||||
|
let h = 0;
|
||||||
|
for (let i = 0; i < str.length; i++) { h = ((h << 5) - h) + str.charCodeAt(i); h |= 0; }
|
||||||
|
return Math.abs(h).toString(36).slice(0, 12);
|
||||||
|
};
|
||||||
|
STATE.wikiId = (CONFIG.repo && shortHash(CONFIG.repo)) || '';
|
||||||
} else {
|
} else {
|
||||||
console.error("CRITICAL: CONFIG not loaded from wiki/config.js");
|
console.error("CRITICAL: CONFIG not loaded from wiki/config.js");
|
||||||
}
|
}
|
||||||
@@ -1148,7 +1344,8 @@
|
|||||||
if (modalSearchInput) modalSearchInput.placeholder = CONFIG.ui.searchPlaceholder;
|
if (modalSearchInput) modalSearchInput.placeholder = CONFIG.ui.searchPlaceholder;
|
||||||
|
|
||||||
document.getElementById('label-changelog').innerText = CONFIG.ui.changelogTitle;
|
document.getElementById('label-changelog').innerText = CONFIG.ui.changelogTitle;
|
||||||
document.getElementById('label-root-readme').innerText = CONFIG.ui.rootReadmeTitle;
|
const labelRootReadme = document.getElementById('label-root-readme');
|
||||||
|
if (labelRootReadme) labelRootReadme.innerText = CONFIG.ui.rootReadmeTitle;
|
||||||
document.getElementById('label-initializing').innerText = CONFIG.ui.initializingText;
|
document.getElementById('label-initializing').innerText = CONFIG.ui.initializingText;
|
||||||
document.getElementById('label-join-us').innerText = CONFIG.ui.joinUsTitle;
|
document.getElementById('label-join-us').innerText = CONFIG.ui.joinUsTitle;
|
||||||
document.getElementById('label-on-this-page').innerText = CONFIG.ui.onThisPageTitle;
|
document.getElementById('label-on-this-page').innerText = CONFIG.ui.onThisPageTitle;
|
||||||
@@ -1164,15 +1361,14 @@
|
|||||||
changelogBtn.style.display = CONFIG.features.showChangelog ? 'flex' : 'none';
|
changelogBtn.style.display = CONFIG.features.showChangelog ? 'flex' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootReadmeBtn = document.getElementById('btn-root-readme');
|
|
||||||
if (rootReadmeBtn) {
|
|
||||||
rootReadmeBtn.style.display = CONFIG.features.showRootReadme ? 'flex' : 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchTrigger = document.querySelector('[onclick="openSearch()"]');
|
const searchTrigger = document.querySelector('[onclick="openSearch()"]');
|
||||||
if (searchTrigger) {
|
if (searchTrigger) {
|
||||||
searchTrigger.style.display = CONFIG.features.showSearch ? 'block' : 'none';
|
searchTrigger.style.display = CONFIG.features.showSearch ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
|
const searchBtnMobile = document.getElementById('search-btn-mobile');
|
||||||
|
if (searchBtnMobile) {
|
||||||
|
searchBtnMobile.style.display = CONFIG.features.showSearch ? '' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
const socialSection = document.getElementById('social-section');
|
const socialSection = document.getElementById('social-section');
|
||||||
if (socialSection) {
|
if (socialSection) {
|
||||||
@@ -1219,7 +1415,7 @@
|
|||||||
document.getElementById('app-name').content = CONFIG.projectName;
|
document.getElementById('app-name').content = CONFIG.projectName;
|
||||||
document.getElementById('ms-tile-color').content = CONFIG.themeColor || '#0B0C0E';
|
document.getElementById('ms-tile-color').content = CONFIG.themeColor || '#0B0C0E';
|
||||||
document.getElementById('theme-color-meta').content = CONFIG.themeColor || '#0B0C0E';
|
document.getElementById('theme-color-meta').content = CONFIG.themeColor || '#0B0C0E';
|
||||||
document.getElementById('manifest-link').href = CONFIG.manifestPath || 'manifest.json';
|
document.getElementById('manifest-link').href = CONFIG.manifestPath || 'manifest.pwa.json';
|
||||||
|
|
||||||
renderCustomLinks();
|
renderCustomLinks();
|
||||||
renderFooter();
|
renderFooter();
|
||||||
@@ -1410,29 +1606,43 @@
|
|||||||
debugLog('[AcidWiki] 🚀 Initializing wiki...');
|
debugLog('[AcidWiki] 🚀 Initializing wiki...');
|
||||||
|
|
||||||
// Try GitHub API first for production
|
// Try GitHub API first for production
|
||||||
let structure = await fetchWikiStructureFromAPI();
|
let apiResult = await fetchWikiStructureFromAPI();
|
||||||
|
let structure = apiResult && (apiResult.structure ?? apiResult);
|
||||||
|
const rootMdFiles = (apiResult && apiResult.rootMdFiles) || [];
|
||||||
|
|
||||||
// If API fails, try local filesystem scanning (for local HTTP servers)
|
// If API fails, try local filesystem scanning (for local HTTP servers)
|
||||||
if (!structure) {
|
if (!structure && typeof structure !== 'object') {
|
||||||
debugLog('[AcidWiki] 🔄 Trying local filesystem scan...');
|
debugLog('[AcidWiki] 🔄 Trying local filesystem scan...');
|
||||||
structure = await scanLocalFilesystem();
|
structure = await scanLocalFilesystem();
|
||||||
}
|
}
|
||||||
|
if (structure && typeof structure === 'object' && !Array.isArray(structure)) {
|
||||||
|
// When from scanLocalFilesystem we don't have rootMdFiles; keep []
|
||||||
|
} else {
|
||||||
|
structure = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!structure || Object.keys(structure).length === 0) {
|
if (!structure || Object.keys(structure).length === 0) {
|
||||||
if (CONFIG.features.showRootReadme) {
|
if (CONFIG.features.showRootReadme) {
|
||||||
debugLog('[AcidWiki] ℹ️ No docs found, but root README is enabled. Proceeding...');
|
debugLog('[AcidWiki] ℹ️ No docs found, but root README is enabled. Proceeding...');
|
||||||
structure = {}; // Empty but valid
|
structure = {};
|
||||||
} else {
|
} else {
|
||||||
console.error('[AcidWiki] ❌ No wiki content found!');
|
console.error('[AcidWiki] ❌ No wiki content found!');
|
||||||
throw new Error("No wiki content found. Please add .md files to wiki/docs/");
|
throw new Error("No wiki content found. Please add .md files to wiki/docs/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog('[AcidWiki] ✅ Wiki structure loaded successfully');
|
STATE.rootMdFiles = rootMdFiles.length > 0 ? rootMdFiles : (CONFIG.features.showRootReadme ? [{ title: 'README.md'.replace(/\.md$/, '').replace(/_/g, ' '), filename: 'README.md' }] : []);
|
||||||
|
if (STATE.rootMdFiles.length > 0) {
|
||||||
|
const rootObj = Object.fromEntries(STATE.rootMdFiles.map(f => [f.title, f.filename]));
|
||||||
|
STATE.wikiData = { __root__: rootObj, ...structure };
|
||||||
|
STATE.expandedSections.add('__root__');
|
||||||
|
} else {
|
||||||
STATE.wikiData = structure;
|
STATE.wikiData = structure;
|
||||||
|
}
|
||||||
|
|
||||||
const firstFolder = Object.keys(STATE.wikiData)[0];
|
const firstFolder = Object.keys(STATE.wikiData)[0];
|
||||||
if (firstFolder) STATE.expandedSections.add(firstFolder);
|
if (firstFolder) STATE.expandedSections.add(firstFolder);
|
||||||
|
debugLog('[AcidWiki] ✅ Wiki structure loaded successfully');
|
||||||
|
|
||||||
renderSidebar();
|
renderSidebar();
|
||||||
buildSearchIndex();
|
buildSearchIndex();
|
||||||
@@ -1440,7 +1650,7 @@
|
|||||||
window.onpopstate = (event) => {
|
window.onpopstate = (event) => {
|
||||||
if (event.state) {
|
if (event.state) {
|
||||||
if (event.state.page === 'versions') toggleVersionsPage(null, false);
|
if (event.state.page === 'versions') toggleVersionsPage(null, false);
|
||||||
else loadContent(event.state.folder, event.state.title, event.state.filename, false);
|
else loadContent(event.state.folder, event.state.title, event.state.filename, false, event.state.folder === '');
|
||||||
} else {
|
} else {
|
||||||
handleInitialRoute();
|
handleInitialRoute();
|
||||||
}
|
}
|
||||||
@@ -1476,9 +1686,11 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const structure = {};
|
const structure = {};
|
||||||
let fileCount = 0;
|
let fileCount = 0;
|
||||||
|
const rootMdFiles = [];
|
||||||
|
|
||||||
data.tree.forEach(item => {
|
data.tree.forEach(item => {
|
||||||
if (item.path.startsWith('wiki/docs/') && item.type === 'blob' && item.path.endsWith('.md')) {
|
if (item.type !== 'blob' || !item.path.endsWith('.md')) return;
|
||||||
|
if (item.path.startsWith('wiki/docs/')) {
|
||||||
const relativePath = item.path.replace('wiki/docs/', '');
|
const relativePath = item.path.replace('wiki/docs/', '');
|
||||||
const parts = relativePath.split('/');
|
const parts = relativePath.split('/');
|
||||||
|
|
||||||
@@ -1493,9 +1705,20 @@
|
|||||||
const title = decodeURIComponent(filename.replace(/\.md$/, '').replace(/_/g, ' '));
|
const title = decodeURIComponent(filename.replace(/\.md$/, '').replace(/_/g, ' '));
|
||||||
currentLevel[title] = filename;
|
currentLevel[title] = filename;
|
||||||
fileCount++;
|
fileCount++;
|
||||||
|
} else if (!item.path.includes('/')) {
|
||||||
|
const filename = item.path;
|
||||||
|
const title = decodeURIComponent(filename.replace(/\.md$/, '').replace(/_/g, ' '));
|
||||||
|
rootMdFiles.push({ title, filename });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
rootMdFiles.sort((a, b) => {
|
||||||
|
if (a.filename.toLowerCase() === 'readme.md') return -1;
|
||||||
|
if (b.filename.toLowerCase() === 'readme.md') return 1;
|
||||||
|
return a.filename.localeCompare(b.filename);
|
||||||
|
});
|
||||||
|
if (rootMdFiles.length) debugLog(`[AcidWiki] 📂 Root .md files: ${rootMdFiles.map(f => f.filename).join(', ')}`);
|
||||||
|
|
||||||
// Sort folders and files recursively
|
// Sort folders and files recursively
|
||||||
function sortStructure(obj) {
|
function sortStructure(obj) {
|
||||||
const sorted = {};
|
const sorted = {};
|
||||||
@@ -1511,7 +1734,10 @@
|
|||||||
|
|
||||||
const sortedStructure = sortStructure(structure);
|
const sortedStructure = sortStructure(structure);
|
||||||
debugLog(`[AcidWiki] ✅ GitHub discovery complete: Found ${fileCount} files`);
|
debugLog(`[AcidWiki] ✅ GitHub discovery complete: Found ${fileCount} files`);
|
||||||
return Object.keys(sortedStructure).length > 0 ? sortedStructure : null;
|
const hasStructure = Object.keys(sortedStructure).length > 0;
|
||||||
|
const hasRoot = rootMdFiles.length > 0;
|
||||||
|
if (!hasStructure && !hasRoot) return null;
|
||||||
|
return { structure: hasStructure ? sortedStructure : {}, rootMdFiles };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugLog(`[AcidWiki] ❌ Error fetching branch "${branch}":`, e);
|
debugLog(`[AcidWiki] ❌ Error fetching branch "${branch}":`, e);
|
||||||
}
|
}
|
||||||
@@ -1563,8 +1789,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build expected URL prefix for files in the current folder
|
// Build expected URL prefix for files in the current folder
|
||||||
// When scanning wiki/docs/, we expect links like /...path.../wiki/docs/file.md or /...path.../wiki/docs/folder/
|
|
||||||
// When scanning wiki/docs/01_General/, we expect /...path.../wiki/docs/01_General/file.md
|
|
||||||
const baseDocsPath = '/wiki/docs/';
|
const baseDocsPath = '/wiki/docs/';
|
||||||
|
|
||||||
// Only process links that contain wiki/docs in their path
|
// Only process links that contain wiki/docs in their path
|
||||||
@@ -1581,8 +1805,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a direct child of our current folder
|
// Check if this is a direct child of our current folder
|
||||||
// If we're scanning root (path=''), afterDocs should be like 'file.md' or 'folder/'
|
|
||||||
// If we're scanning '01_General', afterDocs should start with '01_General/' and next segment is the child
|
|
||||||
const expectedPrefix = path ? `${path}/` : '';
|
const expectedPrefix = path ? `${path}/` : '';
|
||||||
if (path && !afterDocs.startsWith(expectedPrefix)) {
|
if (path && !afterDocs.startsWith(expectedPrefix)) {
|
||||||
debugLog(`[AcidWiki] ⏭️ Skipped (not in current path "${path}"): afterDocs="${afterDocs}"`);
|
debugLog(`[AcidWiki] ⏭️ Skipped (not in current path "${path}"): afterDocs="${afterDocs}"`);
|
||||||
@@ -1678,6 +1900,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Convertit une URL virtuelle ?page=README_ik5745.md en nom réel README.md pour la recherche. */
|
||||||
|
function pageParamToRealForRoot(pageParam) {
|
||||||
|
if (!pageParam || !pageParam.endsWith('.md')) return pageParam;
|
||||||
|
const m = pageParam.match(/^(.+)_([a-z0-9]+)\.md$/);
|
||||||
|
if (m) return m[1] + '.md';
|
||||||
|
return pageParam;
|
||||||
|
}
|
||||||
|
|
||||||
function handleInitialRoute() {
|
function handleInitialRoute() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const pageParam = urlParams.get('page');
|
const pageParam = urlParams.get('page');
|
||||||
@@ -1686,18 +1916,20 @@
|
|||||||
toggleVersionsPage(null, false);
|
toggleVersionsPage(null, false);
|
||||||
} else if (pageParam) {
|
} else if (pageParam) {
|
||||||
const flatList = getFlatPageList();
|
const flatList = getFlatPageList();
|
||||||
const found = flatList.find(item => `${item.folder}/${item.file}` === pageParam || item.file === pageParam);
|
const paramForRoot = pageParamToRealForRoot(pageParam);
|
||||||
|
const found = flatList.find(item => {
|
||||||
|
if (item.folder === '') return item.file === paramForRoot || item.file === pageParam;
|
||||||
|
return `${item.folder}/${item.file}` === pageParam || item.file === pageParam;
|
||||||
|
});
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
loadContent(found.folder, found.title, found.file, false);
|
loadContent(found.folder, found.title, found.file, false, found.folder === '');
|
||||||
// Expand all parent folders
|
if (found.folder === '') STATE.expandedSections.add('__root__');
|
||||||
const parts = found.folder.split('/');
|
const parts = found.folder.split('/').filter(Boolean);
|
||||||
let current = "";
|
let current = "";
|
||||||
parts.forEach(p => {
|
parts.forEach(p => {
|
||||||
if (p) {
|
|
||||||
current = current ? `${current}/${p}` : p;
|
current = current ? `${current}/${p}` : p;
|
||||||
STATE.expandedSections.add(current);
|
STATE.expandedSections.add(current);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
renderSidebar();
|
renderSidebar();
|
||||||
} else {
|
} else {
|
||||||
@@ -1708,11 +1940,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AMÉLIORATION : Indexation via l'API GitHub Raw
|
||||||
async function buildSearchIndex() {
|
async function buildSearchIndex() {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
function indexRecursive(data, currentPath = '') {
|
function indexRecursive(data, currentPath = '') {
|
||||||
for (const [key, value] of Object.entries(data)) {
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
if (key === '__root__') continue;
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === 'object' && value !== null) {
|
||||||
const folderPath = currentPath ? `${currentPath}/${key}` : key;
|
const folderPath = currentPath ? `${currentPath}/${key}` : key;
|
||||||
indexRecursive(value, folderPath);
|
indexRecursive(value, folderPath);
|
||||||
@@ -1721,8 +1955,9 @@
|
|||||||
const title = key;
|
const title = key;
|
||||||
const folder = currentPath;
|
const folder = currentPath;
|
||||||
|
|
||||||
|
// ICI : Utilisation de l'URL raw de GitHub
|
||||||
promises.push(
|
promises.push(
|
||||||
fetch(`./wiki/docs/${folder}/${filename}`)
|
fetch(`https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/wiki/docs/${folder}/${filename}`)
|
||||||
.then(res => { if (!res.ok) return ''; return res.text(); })
|
.then(res => { if (!res.ok) return ''; return res.text(); })
|
||||||
.then(text => {
|
.then(text => {
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
@@ -1740,26 +1975,26 @@
|
|||||||
|
|
||||||
indexRecursive(STATE.wikiData);
|
indexRecursive(STATE.wikiData);
|
||||||
|
|
||||||
// 2. --- MANUAL INDEXING OF ROOT README ---
|
// 2. --- INDEXING ROOT .MD FILES (Project Home) ---
|
||||||
if (CONFIG.features.showRootReadme) {
|
(STATE.rootMdFiles || []).forEach(({ title, filename }) => {
|
||||||
promises.push(
|
promises.push(
|
||||||
fetch('README.md')
|
// ICI : Utilisation de l'URL raw de GitHub
|
||||||
|
fetch(`https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/${filename}`)
|
||||||
.then(res => { if (!res.ok) return ''; return res.text(); })
|
.then(res => { if (!res.ok) return ''; return res.text(); })
|
||||||
.then(text => {
|
.then(text => {
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
// On l'ajoute manuellement à l'index
|
|
||||||
STATE.searchIndex.push({
|
STATE.searchIndex.push({
|
||||||
folder: '', // Racine
|
folder: '',
|
||||||
title: CONFIG.ui.rootReadmeTitle || "Project Home",
|
title,
|
||||||
filename: 'README.md',
|
filename,
|
||||||
content: text.toLowerCase(),
|
content: text.toLowerCase(),
|
||||||
titleLower: (CONFIG.ui.rootReadmeTitle || "Project Home").toLowerCase()
|
titleLower: title.toLowerCase()
|
||||||
});
|
});
|
||||||
debugLog("[AcidWiki] 🔍 Root README indexed.");
|
|
||||||
})
|
})
|
||||||
.catch(e => console.warn("[AcidWiki] Failed to index README.md", e))
|
.catch(() => {})
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
if (STATE.rootMdFiles && STATE.rootMdFiles.length) debugLog("[AcidWiki] 🔍 Root .md files indexed.");
|
||||||
|
|
||||||
// 3. --- CHANGELOG INDEXING (API) ---
|
// 3. --- CHANGELOG INDEXING (API) ---
|
||||||
if (CONFIG.features.showChangelog) {
|
if (CONFIG.features.showChangelog) {
|
||||||
@@ -1768,13 +2003,12 @@
|
|||||||
.then(res => { if (!res.ok) return []; return res.json(); })
|
.then(res => { if (!res.ok) return []; return res.json(); })
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!Array.isArray(data) || data.length === 0) return;
|
if (!Array.isArray(data) || data.length === 0) return;
|
||||||
// On concatène toutes les releases pour la recherche
|
|
||||||
const fullLog = data.map(r => `${r.tag_name} ${r.name || ''} ${r.body || ''}`).join(' ');
|
const fullLog = data.map(r => `${r.tag_name} ${r.name || ''} ${r.body || ''}`).join(' ');
|
||||||
|
|
||||||
STATE.searchIndex.push({
|
STATE.searchIndex.push({
|
||||||
folder: 'SYSTEM', // Petit tag visuel
|
folder: 'SYSTEM',
|
||||||
title: CONFIG.ui.changelogTitle,
|
title: CONFIG.ui.changelogTitle,
|
||||||
filename: 'CHANGELOG_SPECIAL_ID', // ID unique pour le clic
|
filename: 'CHANGELOG_SPECIAL_ID',
|
||||||
content: fullLog.toLowerCase(),
|
content: fullLog.toLowerCase(),
|
||||||
titleLower: CONFIG.ui.changelogTitle.toLowerCase(),
|
titleLower: CONFIG.ui.changelogTitle.toLowerCase(),
|
||||||
isVirtual: true
|
isVirtual: true
|
||||||
@@ -1792,6 +2026,12 @@
|
|||||||
const flatList = [];
|
const flatList = [];
|
||||||
function traverse(data, currentPath = '') {
|
function traverse(data, currentPath = '') {
|
||||||
for (const [key, value] of Object.entries(data)) {
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
if (key === '__root__' && typeof value === 'object' && value !== null) {
|
||||||
|
for (const [title, file] of Object.entries(value))
|
||||||
|
if (typeof file === 'string' && file.endsWith('.md'))
|
||||||
|
flatList.push({ folder: '', title, file });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (typeof value === 'object' && value !== null) {
|
if (typeof value === 'object' && value !== null) {
|
||||||
const folderPath = currentPath ? `${currentPath}/${key}` : key;
|
const folderPath = currentPath ? `${currentPath}/${key}` : key;
|
||||||
traverse(value, folderPath);
|
traverse(value, folderPath);
|
||||||
@@ -1805,14 +2045,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadDefault() {
|
function loadDefault() {
|
||||||
if (CONFIG.features.showRootReadme) {
|
|
||||||
loadContent('', CONFIG.ui.rootReadmeTitle, 'README.md', true, true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const flatList = getFlatPageList();
|
const flatList = getFlatPageList();
|
||||||
if (flatList.length === 0) return;
|
if (flatList.length === 0) return;
|
||||||
const first = flatList[0];
|
const first = flatList[0];
|
||||||
loadContent(first.folder, first.title, first.file, true);
|
loadContent(first.folder, first.title, first.file, true, first.folder === '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function showErrorState(msg) {
|
function showErrorState(msg) {
|
||||||
@@ -1866,6 +2102,9 @@
|
|||||||
let currentPath = "";
|
let currentPath = "";
|
||||||
const folderParts = folder.split('/').filter(s => s);
|
const folderParts = folder.split('/').filter(s => s);
|
||||||
|
|
||||||
|
if (folder === '' && (STATE.rootMdFiles || []).length > 0) {
|
||||||
|
breadcrumbParts.push(`<span class="hover:text-hack-heading cursor-pointer" onclick="loadDefault()">${CONFIG.ui.rootReadmeTitle || 'Project Home'}</span>`);
|
||||||
|
}
|
||||||
segments.forEach((seg, i) => {
|
segments.forEach((seg, i) => {
|
||||||
const partKey = folderParts[i];
|
const partKey = folderParts[i];
|
||||||
currentPath = currentPath ? `${currentPath}/${partKey}` : partKey;
|
currentPath = currentPath ? `${currentPath}/${partKey}` : partKey;
|
||||||
@@ -1920,8 +2159,8 @@
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let text;
|
let text;
|
||||||
const cacheKey = `${folder}/${filename}`;
|
const cacheKey = (isRoot && STATE.wikiId) ? `__root__${STATE.wikiId}/${filename}` : `${folder}/${filename}`;
|
||||||
const storageKey = `bjorn_content_${cacheKey}`;
|
const storageKey = `bjorn_content_${cacheKey.replace(/\//g, '_')}`;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const TTL = 3600 * 1000 * 24; // 24 Hours Cache
|
const TTL = 3600 * 1000 * 24; // 24 Hours Cache
|
||||||
|
|
||||||
@@ -1944,14 +2183,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Network Fetch (Fallback)
|
// AMÉLIORATION : Network Fetch depuis l'API GitHub Raw
|
||||||
if (!text) {
|
if (!text) {
|
||||||
try {
|
try {
|
||||||
let path;
|
let path;
|
||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
path = `./${filename}`;
|
path = `https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/${filename}`;
|
||||||
} else {
|
} else {
|
||||||
path = folder ? `./wiki/docs/${folder}/${filename}` : `./wiki/docs/${filename}`;
|
path = folder
|
||||||
|
? `https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/wiki/docs/${folder}/${filename}`
|
||||||
|
: `https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/wiki/docs/${filename}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(path);
|
const res = await fetch(path);
|
||||||
@@ -1961,18 +2202,7 @@
|
|||||||
throw new Error("404");
|
throw new Error("404");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Try fallback if not root
|
throw new Error(`Content not found. Make sure the branch ${STATE.branch} exists and contains the file.`);
|
||||||
if (!isRoot) {
|
|
||||||
const path2 = `./wiki/docs/${filename}`;
|
|
||||||
const res2 = await fetch(path2);
|
|
||||||
if (res2.ok) {
|
|
||||||
text = await res2.text();
|
|
||||||
} else {
|
|
||||||
throw new Error(`Content not found.`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save to caches
|
// Save to caches
|
||||||
@@ -1988,6 +2218,17 @@
|
|||||||
const cleanHTML = DOMPurify.sanitize(marked.parse(text));
|
const cleanHTML = DOMPurify.sanitize(marked.parse(text));
|
||||||
viewer.innerHTML = cleanHTML;
|
viewer.innerHTML = cleanHTML;
|
||||||
|
|
||||||
|
// --- AJOUT : RENDU DES ÉQUATIONS LATEX (KaTeX) ---
|
||||||
|
renderMathInElement(viewer, {
|
||||||
|
delimiters: [
|
||||||
|
{left: '$$', right: '$$', display: true},
|
||||||
|
{left: '$', right: '$', display: false},
|
||||||
|
{left: '\\(', right: '\\)', display: false},
|
||||||
|
{left: '\\[', right: '\\]', display: true}
|
||||||
|
],
|
||||||
|
throwOnError: false
|
||||||
|
});
|
||||||
|
|
||||||
// Update SEO Tags
|
// Update SEO Tags
|
||||||
const fullTitle = `${title} | ${CONFIG.projectName}`;
|
const fullTitle = `${title} | ${CONFIG.projectName}`;
|
||||||
document.title = fullTitle;
|
document.title = fullTitle;
|
||||||
@@ -2005,14 +2246,16 @@
|
|||||||
renderPagination(folder, title);
|
renderPagination(folder, title);
|
||||||
|
|
||||||
if (pushHistory) {
|
if (pushHistory) {
|
||||||
const newUrl = `?page=${folder}/${filename}`;
|
const newUrl = folder
|
||||||
|
? `?page=${folder}/${filename}`
|
||||||
|
: (STATE.wikiId ? `?page=${filename.replace(/\.md$/, `_${STATE.wikiId}.md`)}` : `?page=${filename}`);
|
||||||
window.history.pushState({ folder, title, filename }, "", newUrl);
|
window.history.pushState({ folder, title, filename }, "", newUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONFIG.features.autoCollapseSidebar) {
|
if (CONFIG.features.autoCollapseSidebar) {
|
||||||
STATE.expandedSections = new Set([folder]);
|
STATE.expandedSections = new Set([folder || '__root__']);
|
||||||
} else {
|
} else {
|
||||||
STATE.expandedSections.add(folder);
|
STATE.expandedSections.add(folder || '__root__');
|
||||||
}
|
}
|
||||||
renderSidebar();
|
renderSidebar();
|
||||||
|
|
||||||
@@ -2098,7 +2341,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CACHE_KEY = `bjorn_upd_${folder}_${filename}`;
|
const CACHE_KEY = STATE.wikiId ? `bjorn_upd_${STATE.wikiId}_${folder}_${filename}`.replace(/\//g, '_') : `bjorn_upd_${folder}_${filename}`;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
const cached = localStorage.getItem(CACHE_KEY);
|
const cached = localStorage.getItem(CACHE_KEY);
|
||||||
@@ -2129,18 +2372,23 @@
|
|||||||
const idx = flatList.findIndex(item => item.folder === currentFolder && item.title === currentTitle);
|
const idx = flatList.findIndex(item => item.folder === currentFolder && item.title === currentTitle);
|
||||||
|
|
||||||
[idx - 1, idx + 1].forEach(i => {
|
[idx - 1, idx + 1].forEach(i => {
|
||||||
if (flatList[i]) {
|
if (!flatList[i]) return;
|
||||||
const url = `./wiki/docs/${flatList[i].folder}/${flatList[i].file}`;
|
const item = flatList[i];
|
||||||
const cacheKey = `${flatList[i].folder}/${flatList[i].file}`;
|
const isRoot = item.folder === '';
|
||||||
|
const cacheKey = (isRoot && STATE.wikiId) ? `__root__${STATE.wikiId}/${item.file}` : `${item.folder}/${item.file}`;
|
||||||
|
const storageKey = `bjorn_content_${cacheKey.replace(/\//g, '_')}`;
|
||||||
|
|
||||||
// Check persistent cache first to avoid fetch
|
// On vérifie le cache local pour éviter une requête inutile
|
||||||
const storageKey = `bjorn_content_${cacheKey}`;
|
|
||||||
if (localStorage.getItem(storageKey)) return;
|
if (localStorage.getItem(storageKey)) return;
|
||||||
|
|
||||||
|
// Préchargement via l'API raw
|
||||||
|
const url = isRoot
|
||||||
|
? `https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/${item.file}`
|
||||||
|
: `https://raw.githubusercontent.com/${STATE.repo}/${STATE.branch}/wiki/docs/${item.folder}/${item.file}`;
|
||||||
|
|
||||||
fetch(url).then(r => r.text()).then(t => {
|
fetch(url).then(r => r.text()).then(t => {
|
||||||
STATE.contentCache[cacheKey] = t;
|
STATE.contentCache[cacheKey] = t;
|
||||||
}).catch(() => { });
|
}).catch(() => { });
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2231,7 +2479,7 @@
|
|||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = "pagination-card text-left p-4 rounded border border-hack-border hover:border-hack-green group transition-colors bg-hack-sidebar/50 hover:bg-hack-sidebar w-full flex flex-col items-start";
|
btn.className = "pagination-card text-left p-4 rounded border border-hack-border hover:border-hack-green group transition-colors bg-hack-sidebar/50 hover:bg-hack-sidebar w-full flex flex-col items-start";
|
||||||
btn.innerHTML = `<span class="text-[10px] text-gray-500 uppercase tracking-widest mb-1 group-hover:text-hack-green">Previous</span><span class="font-bold text-sm truncate text-hack-heading w-full">« ${prev.title}</span>`;
|
btn.innerHTML = `<span class="text-[10px] text-gray-500 uppercase tracking-widest mb-1 group-hover:text-hack-green">Previous</span><span class="font-bold text-sm truncate text-hack-heading w-full">« ${prev.title}</span>`;
|
||||||
btn.onclick = () => loadContent(prev.folder, prev.title, prev.file);
|
btn.onclick = () => loadContent(prev.folder, prev.title, prev.file, true, prev.folder === '');
|
||||||
navContainer.appendChild(btn);
|
navContainer.appendChild(btn);
|
||||||
} else { navContainer.appendChild(document.createElement('div')); }
|
} else { navContainer.appendChild(document.createElement('div')); }
|
||||||
|
|
||||||
@@ -2240,7 +2488,7 @@
|
|||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.className = "pagination-card text-right p-4 rounded border border-hack-border hover:border-hack-green group transition-colors bg-hack-sidebar/50 hover:bg-hack-sidebar w-full flex flex-col items-end";
|
btn.className = "pagination-card text-right p-4 rounded border border-hack-border hover:border-hack-green group transition-colors bg-hack-sidebar/50 hover:bg-hack-sidebar w-full flex flex-col items-end";
|
||||||
btn.innerHTML = `<span class="text-[10px] text-gray-500 uppercase tracking-widest mb-1 group-hover:text-hack-green">Next</span><span class="font-bold text-sm truncate text-hack-heading w-full">${next.title} »</span>`;
|
btn.innerHTML = `<span class="text-[10px] text-gray-500 uppercase tracking-widest mb-1 group-hover:text-hack-green">Next</span><span class="font-bold text-sm truncate text-hack-heading w-full">${next.title} »</span>`;
|
||||||
btn.onclick = () => loadContent(next.folder, next.title, next.file);
|
btn.onclick = () => loadContent(next.folder, next.title, next.file, true, next.folder === '');
|
||||||
navContainer.appendChild(btn);
|
navContainer.appendChild(btn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2398,8 +2646,40 @@
|
|||||||
function renderRecursive(data, parentContainer, currentPath = '', level = 0) {
|
function renderRecursive(data, parentContainer, currentPath = '', level = 0) {
|
||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach(key => {
|
||||||
const value = data[key];
|
const value = data[key];
|
||||||
const isFolder = typeof value === 'object' && value !== null;
|
const isRootSection = key === '__root__';
|
||||||
const cleanName = key.replace(/^\d+_/, '').replace(/_/g, ' ');
|
const isFolder = (typeof value === 'object' && value !== null) && !isRootSection;
|
||||||
|
const cleanName = isRootSection ? (CONFIG.ui.rootReadmeTitle || 'Project Home') : key.replace(/^\d+_/, '').replace(/_/g, ' ');
|
||||||
|
|
||||||
|
if (isRootSection && typeof value === 'object' && value !== null) {
|
||||||
|
const folderPath = '__root__';
|
||||||
|
if (searchResults && !searchResults['__root__']) return;
|
||||||
|
|
||||||
|
const group = document.createElement('div');
|
||||||
|
group.className = `nav-group mb-1 ${level > 0 ? 'ml-2' : ''}`;
|
||||||
|
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
const isExpanded = STATE.expandedSections.has(folderPath) || searchResults;
|
||||||
|
btn.className = `section-header w-full flex items-center justify-between px-2 py-2 text-gray-500 hover:text-hack-heading transition-colors rounded hover:bg-hack-bg focus:outline-none focus:bg-hack-bg ${isExpanded ? 'active' : ''}`;
|
||||||
|
btn.innerHTML = `
|
||||||
|
<span class="flex items-center gap-2 text-[11px] font-bold uppercase tracking-widest font-mono text-hack-green"><i data-lucide="home" class="w-3 h-3"></i> ${cleanName}</span>
|
||||||
|
<i data-lucide="chevron-right" class="section-arrow w-3 h-3 transition-transform"></i>
|
||||||
|
`;
|
||||||
|
btn.onclick = () => {
|
||||||
|
if (STATE.expandedSections.has(folderPath)) STATE.expandedSections.delete(folderPath);
|
||||||
|
else STATE.expandedSections.add(folderPath);
|
||||||
|
renderSidebar(searchResults);
|
||||||
|
};
|
||||||
|
|
||||||
|
const list = document.createElement('div');
|
||||||
|
list.className = `nav-list ${isExpanded ? 'expanded' : 'collapsed'}`;
|
||||||
|
|
||||||
|
renderRecursive(value, list, folderPath, level + 1);
|
||||||
|
|
||||||
|
group.appendChild(btn);
|
||||||
|
group.appendChild(list);
|
||||||
|
parentContainer.appendChild(group);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
const folderPath = currentPath ? `${currentPath}/${key}` : key;
|
const folderPath = currentPath ? `${currentPath}/${key}` : key;
|
||||||
@@ -2434,16 +2714,20 @@
|
|||||||
hasContent = true;
|
hasContent = true;
|
||||||
const filename = value;
|
const filename = value;
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
const fullPath = currentPath ? `${currentPath}/${filename}` : filename;
|
const isRootFile = currentPath === '__root__';
|
||||||
|
const fullPath = isRootFile
|
||||||
|
? (STATE.wikiId ? filename.replace(/\.md$/, `_${STATE.wikiId}.md`) : filename)
|
||||||
|
: (currentPath ? `${currentPath}/${filename}` : filename);
|
||||||
link.href = `?page=${fullPath}`;
|
link.href = `?page=${fullPath}`;
|
||||||
|
|
||||||
const isActive = STATE.currentFolder === currentPath && STATE.currentFilename === filename;
|
const linkFolder = isRootFile ? '' : currentPath;
|
||||||
|
const isActive = STATE.currentFolder === linkFolder && STATE.currentFilename === filename;
|
||||||
link.className = `nav-link group flex items-center gap-3 px-2 py-1.5 rounded-md text-sm font-medium text-gray-400 hover:text-hack-heading hover:bg-hack-bg transition-all outline-none focus:bg-hack-bg ${isActive ? 'active' : ''}`;
|
link.className = `nav-link group flex items-center gap-3 px-2 py-1.5 rounded-md text-sm font-medium text-gray-400 hover:text-hack-heading hover:bg-hack-bg transition-all outline-none focus:bg-hack-bg ${isActive ? 'active' : ''}`;
|
||||||
|
|
||||||
link.innerHTML = `<span class="w-1.5 h-1.5 rounded-full bg-hack-border group-hover:bg-hack-green flex-shrink-0 transition-colors"></span> ${key}`;
|
link.innerHTML = `<span class="w-1.5 h-1.5 rounded-full bg-hack-border group-hover:bg-hack-green flex-shrink-0 transition-colors"></span> ${key}`;
|
||||||
link.onclick = (e) => {
|
link.onclick = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
loadContent(currentPath, key, filename);
|
loadContent(linkFolder, key, filename, true, isRootFile);
|
||||||
document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
|
document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
|
||||||
link.classList.add('active');
|
link.classList.add('active');
|
||||||
};
|
};
|
||||||
@@ -2543,7 +2827,7 @@
|
|||||||
<div class="search-result-item p-3 rounded-xl cursor-pointer flex flex-col gap-1" onclick="selectSearchResult(${i})" data-index="${i}">
|
<div class="search-result-item p-3 rounded-xl cursor-pointer flex flex-col gap-1" onclick="selectSearchResult(${i})" data-index="${i}">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-hack-heading font-bold text-sm">${res.title}</span>
|
<span class="text-hack-heading font-bold text-sm">${res.title}</span>
|
||||||
<span class="text-[10px] text-gray-500 font-mono opacity-50 uppercase">${res.folder.replace(/_/g, ' ')}</span>
|
<span class="text-[10px] text-gray-500 font-mono opacity-50 uppercase">${(res.folder || (CONFIG.ui.rootReadmeTitle || 'Project Home')).replace(/_/g, ' ')}</span>
|
||||||
</div>
|
</div>
|
||||||
${res.snippet ? `<div class="text-xs text-gray-400 line-clamp-2 font-sans leading-relaxed">${res.snippet}</div>` : ''}
|
${res.snippet ? `<div class="text-xs text-gray-400 line-clamp-2 font-sans leading-relaxed">${res.snippet}</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
@@ -2576,9 +2860,8 @@
|
|||||||
toggleVersionsPage(null, true);
|
toggleVersionsPage(null, true);
|
||||||
} else {
|
} else {
|
||||||
// Comportement normal avec HIGHLIGHT
|
// Comportement normal avec HIGHLIGHT
|
||||||
// On récupère la requête depuis l'input de recherche
|
|
||||||
const query = document.getElementById('modal-search-input').value.trim();
|
const query = document.getElementById('modal-search-input').value.trim();
|
||||||
loadContent(res.folder, res.title, res.filename, true, false, query);
|
loadContent(res.folder, res.title, res.filename, true, res.folder === '', query);
|
||||||
}
|
}
|
||||||
closeSearch();
|
closeSearch();
|
||||||
}
|
}
|
||||||
@@ -2746,14 +3029,19 @@
|
|||||||
const tocSidebar = document.getElementById('mobile-toc-sidebar');
|
const tocSidebar = document.getElementById('mobile-toc-sidebar');
|
||||||
const overlay = document.getElementById('overlay');
|
const overlay = document.getElementById('overlay');
|
||||||
|
|
||||||
|
const kbMobileToggleBtn = document.getElementById('kb-mobile-toggle');
|
||||||
|
const kbMobileTocToggleBtn = document.getElementById('kb-mobile-toc-toggle');
|
||||||
|
|
||||||
function openMenu() {
|
function openMenu() {
|
||||||
sidebar.classList.remove('-translate-x-full');
|
sidebar.classList.remove('-translate-x-full');
|
||||||
closeTOC(); // Close TOC if open
|
closeTOC(); // Close TOC if open
|
||||||
overlay.classList.remove('hidden');
|
overlay.classList.remove('hidden');
|
||||||
setTimeout(() => overlay.classList.remove('opacity-0'), 10);
|
setTimeout(() => overlay.classList.remove('opacity-0'), 10);
|
||||||
|
kbMobileToggleBtn?.classList.add('kb-bottom-bar-btn-hidden');
|
||||||
}
|
}
|
||||||
function closeMenu() {
|
function closeMenu() {
|
||||||
sidebar.classList.add('-translate-x-full');
|
sidebar.classList.add('-translate-x-full');
|
||||||
|
kbMobileToggleBtn?.classList.remove('kb-bottom-bar-btn-hidden');
|
||||||
checkOverlay();
|
checkOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2766,9 +3054,11 @@
|
|||||||
closeMenu(); // Close Menu if open
|
closeMenu(); // Close Menu if open
|
||||||
overlay.classList.remove('hidden');
|
overlay.classList.remove('hidden');
|
||||||
setTimeout(() => overlay.classList.remove('opacity-0'), 10);
|
setTimeout(() => overlay.classList.remove('opacity-0'), 10);
|
||||||
|
kbMobileTocToggleBtn?.classList.add('kb-bottom-bar-btn-hidden');
|
||||||
}
|
}
|
||||||
function closeTOC() {
|
function closeTOC() {
|
||||||
tocSidebar.classList.add('translate-x-full');
|
tocSidebar.classList.add('translate-x-full');
|
||||||
|
kbMobileTocToggleBtn?.classList.remove('kb-bottom-bar-btn-hidden');
|
||||||
checkOverlay();
|
checkOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2783,8 +3073,9 @@
|
|||||||
document.getElementById('menu-btn').onclick = openMenu;
|
document.getElementById('menu-btn').onclick = openMenu;
|
||||||
document.getElementById('close-sidebar-btn').onclick = closeMenu;
|
document.getElementById('close-sidebar-btn').onclick = closeMenu;
|
||||||
document.getElementById('close-toc-btn').onclick = closeTOC;
|
document.getElementById('close-toc-btn').onclick = closeTOC;
|
||||||
document.getElementById('toc-btn-mobile').onclick = openTOC;
|
document.getElementById('toc-btn-desktop')?.addEventListener('click', openTOC);
|
||||||
document.getElementById('toc-btn-desktop').onclick = openTOC;
|
document.getElementById('kb-mobile-toggle')?.addEventListener('click', openMenu);
|
||||||
|
document.getElementById('kb-mobile-toc-toggle')?.addEventListener('click', openTOC);
|
||||||
|
|
||||||
// Unified overlay click closes whichever is open
|
// Unified overlay click closes whichever is open
|
||||||
overlay.onclick = () => {
|
overlay.onclick = () => {
|
||||||
@@ -2814,32 +3105,27 @@
|
|||||||
const progressBar = document.getElementById('reading-progress-bar');
|
const progressBar = document.getElementById('reading-progress-bar');
|
||||||
const scrollBtn = document.getElementById('scroll-top-btn');
|
const scrollBtn = document.getElementById('scroll-top-btn');
|
||||||
|
|
||||||
if (scrollContainer && progressBar) {
|
function updateScrollUI() {
|
||||||
const breadcrumbContainer = document.getElementById('breadcrumb-sticky-container');
|
if (!scrollContainer) return;
|
||||||
scrollContainer.addEventListener('scroll', () => {
|
|
||||||
const scrollTop = scrollContainer.scrollTop;
|
const scrollTop = scrollContainer.scrollTop;
|
||||||
const scrollHeight = scrollContainer.scrollHeight - scrollContainer.clientHeight;
|
const scrollHeight = scrollContainer.scrollHeight - scrollContainer.clientHeight;
|
||||||
const scrollPercent = (scrollHeight > 0) ? (scrollTop / scrollHeight) * 100 : 0;
|
const scrollPercent = (scrollHeight > 0) ? (scrollTop / scrollHeight) * 100 : 0;
|
||||||
progressBar.style.width = scrollPercent + '%';
|
if (progressBar) progressBar.style.width = scrollPercent + '%';
|
||||||
|
scrollBtn?.classList.toggle('visible', scrollTop > 200);
|
||||||
if (scrollTop > 300) scrollBtn.classList.remove('hidden');
|
const breadcrumbContainer = document.getElementById('breadcrumb-sticky-container');
|
||||||
else scrollBtn.classList.add('hidden');
|
if (breadcrumbContainer && typeof CONFIG !== 'undefined' && CONFIG.features.stickyBreadcrumbs) {
|
||||||
|
breadcrumbContainer.classList.toggle('stuck', scrollTop > 20);
|
||||||
if (breadcrumbContainer && CONFIG.features.stickyBreadcrumbs) {
|
|
||||||
// Use a small threshold and requestAnimationFrame for smoothness
|
|
||||||
if (scrollTop > 20) {
|
|
||||||
breadcrumbContainer.classList.add('stuck');
|
|
||||||
} else {
|
|
||||||
breadcrumbContainer.classList.remove('stuck');
|
|
||||||
}
|
|
||||||
} else if (breadcrumbContainer) {
|
} else if (breadcrumbContainer) {
|
||||||
breadcrumbContainer.classList.remove('stuck');
|
breadcrumbContainer.classList.remove('stuck');
|
||||||
}
|
}
|
||||||
|
if (typeof updateTOCActiveState === 'function') updateTOCActiveState();
|
||||||
updateTOCActiveState();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (scrollBtn) {
|
|
||||||
|
if (scrollContainer) {
|
||||||
|
scrollContainer.addEventListener('scroll', updateScrollUI);
|
||||||
|
updateScrollUI();
|
||||||
|
}
|
||||||
|
if (scrollBtn && scrollContainer) {
|
||||||
scrollBtn.onclick = () => scrollContainer.scrollTo({ top: 0, behavior: 'smooth' });
|
scrollBtn.onclick = () => scrollContainer.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2945,7 +3231,7 @@
|
|||||||
|
|
||||||
// Mouse tracking for glow effects
|
// Mouse tracking for glow effects
|
||||||
document.addEventListener('mousemove', (e) => {
|
document.addEventListener('mousemove', (e) => {
|
||||||
const target = e.target.closest('.nav-link, .section-header, .copy-btn, .search-result-item, #menu-btn, #theme-toggle-desktop, #theme-toggle-mobile, .badge-sm, .pagination-card, #toc-btn-mobile, #toc-btn-desktop, #clear-highlight-btn');
|
const target = e.target.closest('.nav-link, .section-header, .copy-btn, .search-result-item, #menu-btn, #theme-toggle-desktop, #theme-toggle-mobile, .badge-sm, .pagination-card, #toc-btn-desktop, #search-btn-mobile, #clear-highlight-btn, .kb-mobile-toggle, .kb-mobile-toc-toggle, .kb-scroll-top-btn');
|
||||||
if (target) {
|
if (target) {
|
||||||
const rect = target.getBoundingClientRect();
|
const rect = target.getBoundingClientRect();
|
||||||
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
const x = ((e.clientX - rect.left) / rect.width) * 100;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const CONFIG = {
|
|||||||
|
|
||||||
// GitHub Repository
|
// GitHub Repository
|
||||||
repo: "infinition/Bjorn",
|
repo: "infinition/Bjorn",
|
||||||
branch: "wiki",
|
branch: "main", // <-- On cible la branche "main" pour charger le contenu (les .md)
|
||||||
|
|
||||||
// Theme Settings
|
// Theme Settings
|
||||||
themes: [
|
themes: [
|
||||||
@@ -97,7 +97,7 @@ const CONFIG = {
|
|||||||
social: {
|
social: {
|
||||||
discord: "https://discord.gg/B3ZH9taVfT",
|
discord: "https://discord.gg/B3ZH9taVfT",
|
||||||
reddit: "https://www.reddit.com/r/Bjorn_CyberViking/",
|
reddit: "https://www.reddit.com/r/Bjorn_CyberViking/",
|
||||||
github: "https://github.com/infinition/Bjorn", // Virgule respectée par le script
|
github: "https://github.com/infinition/Bjorn",
|
||||||
buyMeACoffee: "https://buymeacoffee.com/infinition"
|
buyMeACoffee: "https://buymeacoffee.com/infinition"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user