add template + document framework in root README

template/README.md: stripped to a one-liner bootstrap command.
README.md: full project docs (structure, add page, add component, build.sh).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
GnomeZworc 2026-06-07 13:06:16 +02:00
commit 6796e102e0
Signed by: nicolas.boufideline
GPG key ID: 4406BBBF8845D632
16 changed files with 663 additions and 1 deletions

View file

@ -0,0 +1,3 @@
{
"api_url": "http://localhost:8080"
}

View file

@ -0,0 +1,4 @@
const response = await fetch('./config.json')
if (!response.ok) throw new Error('Failed to load config.json')
export const config = await response.json()

View file

@ -0,0 +1,10 @@
// Dynamic menu loader.
// Fetches data for groups declared with empty children in navigation.json
// and injects them into side-nav after it is ready.
//
// Imported non-blocking from index.html — does not delay routing.
//
// Example usage:
// const nav = document.querySelector('side-nav')
// await nav.ready
// nav.setGroupChildren('My Group', [{ label: 'Item', href: '#/route', icon: 'icon' }])

View file

@ -0,0 +1,8 @@
const response = await fetch('./components.json')
if (!response.ok) throw new Error('Failed to load components.json')
const components = await response.json()
for (const path of components) {
await import(`../${path}`)
}

View file

@ -0,0 +1,41 @@
const response = await fetch('./pages.json')
if (!response.ok) throw new Error('router: failed to load pages.json — run build.sh')
const PAGES = await response.json()
const MODULE_CACHE = new Map()
let activeCSS = null
function currentRoute() {
const hash = window.location.hash.replace(/^#\//, '') || PAGES[0]?.route || ''
return hash.split('?')[0]
}
async function render(routeName) {
const page = PAGES.find(p => p.route === routeName) ?? PAGES[0]
const main = document.querySelector('main')
if (!main || !page) return
const htmlRes = await fetch(page.html)
if (!htmlRes.ok) throw new Error(`router: failed to load ${page.html}`)
main.innerHTML = await htmlRes.text()
activeCSS?.remove()
activeCSS = null
if (page.css) {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = page.css
document.head.appendChild(link)
activeCSS = link
}
if (page.js) {
if (!MODULE_CACHE.has(page.js)) {
MODULE_CACHE.set(page.js, await import(`../${page.js}`))
}
MODULE_CACHE.get(page.js)?.init?.(main)
}
}
await render(currentRoute())
window.addEventListener('hashchange', () => render(currentRoute()))

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" rx="6" fill="#1e1e2e"/>
<text x="16" y="22" font-family="monospace" font-size="18" font-weight="bold"
text-anchor="middle" fill="#89b4fa">S</text>
</svg>

After

Width:  |  Height:  |  Size: 256 B

106
template/sources/index.html Normal file
View file

@ -0,0 +1,106 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mon Projet</title>
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #181825;
color: #cdd6f4;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
height: 100vh;
display: flex;
flex-direction: column;
}
header {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 24px;
border-bottom: 1px solid #313244;
background: #1e1e2e;
flex-shrink: 0;
}
header h1 {
font-size: 17px;
font-weight: 600;
color: #89b4fa;
letter-spacing: 0.02em;
}
header span {
font-size: 12px;
color: #6c7086;
}
header .spacer { flex: 1; }
.layout {
display: flex;
flex: 1;
overflow: hidden;
}
main {
flex: 1;
padding: 28px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
gap: 20px;
}
@media (max-width: 768px) {
main { padding: 16px; }
}
#error {
display: none;
background: #313244;
border: 1px solid #f38ba8;
border-radius: 8px;
padding: 14px 18px;
color: #f38ba8;
font-family: monospace;
font-size: 13px;
width: 100%;
}
</style>
</head>
<body>
<header>
<h1>Mon Projet</h1>
<span>powered by Syn</span>
<div class="spacer"></div>
</header>
<div class="layout">
<side-nav></side-nav>
<main></main>
</div>
<div id="error"></div>
<script type="module">
window.addEventListener('unhandledrejection', e => {
const el = document.getElementById('error')
if (el) {
el.style.display = 'block'
el.textContent = `Error: ${e.reason?.message ?? e.reason}`
}
})
await import('./core/registry.js')
await import('./core/router.js')
import('./core/menu.js')
</script>
</body>
</html>

View file

@ -0,0 +1,24 @@
.home {
padding: 8px;
}
.home h2 {
font-size: 20px;
font-weight: 600;
color: #cdd6f4;
margin-bottom: 8px;
}
.home p {
color: #6c7086;
font-size: 14px;
}
.home code {
background: #313244;
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
color: #89b4fa;
}

View file

@ -0,0 +1,14 @@
class HomePage extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<div class="home">
<h2>Bienvenue</h2>
<p>Modifiez cette page dans <code>sources/pages/home/</code>.</p>
</div>
`
}
}
customElements.define('home-page', HomePage)
export function init(_main) {}

View file

@ -0,0 +1 @@
<home-page></home-page>

View file

@ -0,0 +1,5 @@
{
"route": "home",
"label": "Home",
"icon": "home"
}