|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>EU AI Act Open Source Guide</title> |
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
|
<style> |
|
* { |
|
box-sizing: border-box; |
|
} |
|
|
|
body { |
|
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; |
|
line-height: 1.75; |
|
margin: 0; |
|
padding: 0; |
|
color: #374151; |
|
background-color: #ffffff; |
|
font-size: 16px; |
|
} |
|
|
|
|
|
.layout { |
|
display: flex; |
|
min-height: 100vh; |
|
} |
|
|
|
|
|
.toc-sidebar { |
|
position: fixed; |
|
left: 0; |
|
top: 0; |
|
width: 280px; |
|
height: 100vh; |
|
background-color: #f9fafb; |
|
border-right: 1px solid #e5e7eb; |
|
padding: 24px 16px; |
|
overflow-y: auto; |
|
z-index: 100; |
|
transition: transform 0.3s ease; |
|
} |
|
|
|
.toc-sidebar.collapsed { |
|
transform: translateX(-240px); |
|
} |
|
|
|
.toc-toggle { |
|
position: absolute; |
|
top: 20px; |
|
right: -5px; |
|
width: 40px; |
|
height: 40px; |
|
background: #3b82f6; |
|
color: white; |
|
border: none; |
|
border-radius: 0 6px 6px 0; |
|
cursor: pointer; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 16px; |
|
z-index: 101; |
|
transition: all 0.3s ease; |
|
} |
|
|
|
.toc-sidebar h3 { |
|
margin: 0 0 16px 0; |
|
font-size: 1.125rem; |
|
font-weight: 600; |
|
color: #111827; |
|
border-bottom: 1px solid #e5e7eb; |
|
padding-bottom: 8px; |
|
} |
|
|
|
.toc-sidebar ul { |
|
list-style: none; |
|
margin: 0; |
|
padding: 0; |
|
} |
|
|
|
.toc-sidebar li { |
|
margin: 0; |
|
padding: 0; |
|
} |
|
|
|
.toc-sidebar a { |
|
display: block; |
|
padding: 6px 12px; |
|
color: #6b7280; |
|
text-decoration: none; |
|
font-size: 0.875rem; |
|
border-radius: 4px; |
|
transition: all 0.2s; |
|
} |
|
|
|
.toc-sidebar a:hover { |
|
background-color: #e5e7eb; |
|
color: #374151; |
|
} |
|
|
|
.toc-sidebar a.active { |
|
background-color: #dbeafe; |
|
color: #1d4ed8; |
|
font-weight: 500; |
|
} |
|
|
|
|
|
.toc-sidebar .toc-level-2 { |
|
padding-left: 16px; |
|
} |
|
|
|
.toc-sidebar .toc-level-3 { |
|
padding-left: 32px; |
|
} |
|
|
|
|
|
.main-content { |
|
max-width: 1000px; |
|
margin: 0 auto; |
|
padding: 32px 48px; |
|
width: 100%; |
|
} |
|
|
|
|
|
.content-wrapper { |
|
flex: 1; |
|
display: flex; |
|
justify-content: center; |
|
min-height: 100vh; |
|
} |
|
|
|
|
|
h1 { |
|
font-size: 2.25rem; |
|
font-weight: 700; |
|
line-height: 1.2; |
|
margin: 0 0 2rem 0; |
|
color: #111827; |
|
} |
|
|
|
h2 { |
|
font-size: 1.875rem; |
|
font-weight: 600; |
|
line-height: 1.3; |
|
margin: 3rem 0 1.5rem 0; |
|
color: #111827; |
|
scroll-margin-top: 20px; |
|
} |
|
|
|
h3 { |
|
font-size: 1.5rem; |
|
font-weight: 600; |
|
line-height: 1.4; |
|
margin: 2.5rem 0 1rem 0; |
|
color: #111827; |
|
scroll-margin-top: 20px; |
|
} |
|
|
|
h4 { |
|
font-size: 1.25rem; |
|
font-weight: 600; |
|
line-height: 1.5; |
|
margin: 2rem 0 1rem 0; |
|
color: #111827; |
|
scroll-margin-top: 20px; |
|
} |
|
|
|
h5, h6 { |
|
font-size: 1.125rem; |
|
font-weight: 600; |
|
line-height: 1.5; |
|
margin: 1.5rem 0 1rem 0; |
|
color: #111827; |
|
scroll-margin-top: 20px; |
|
} |
|
|
|
p { |
|
margin: 0 0 1.5rem 0; |
|
color: #374151; |
|
} |
|
|
|
strong { |
|
font-weight: 600; |
|
color: #111827; |
|
} |
|
|
|
em { |
|
font-style: italic; |
|
} |
|
|
|
a { |
|
color: #3b82f6; |
|
text-decoration: none; |
|
font-weight: 500; |
|
} |
|
|
|
a:hover { |
|
text-decoration: underline; |
|
} |
|
|
|
code { |
|
background-color: #f3f4f6; |
|
color: #374151; |
|
padding: 0.125rem 0.375rem; |
|
border-radius: 0.375rem; |
|
font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace; |
|
font-size: 0.875em; |
|
} |
|
|
|
pre { |
|
background-color: #f9fafb; |
|
border: 1px solid #e5e7eb; |
|
border-radius: 0.5rem; |
|
padding: 1.5rem; |
|
overflow-x: auto; |
|
margin: 1.5rem 0; |
|
} |
|
|
|
pre code { |
|
background: none; |
|
padding: 0; |
|
border-radius: 0; |
|
font-size: 0.875rem; |
|
} |
|
|
|
ul, ol { |
|
margin: 0 0 1.5rem 0; |
|
padding-left: 1.5rem; |
|
} |
|
|
|
li { |
|
margin: 0.5rem 0; |
|
} |
|
|
|
blockquote { |
|
border-left: 4px solid #e5e7eb; |
|
margin: 1.5rem 0; |
|
padding-left: 1.5rem; |
|
color: #6b7280; |
|
font-style: italic; |
|
word-wrap: break-word; |
|
overflow-wrap: break-word; |
|
white-space: normal; |
|
max-width: 100%; |
|
} |
|
|
|
table { |
|
border-collapse: collapse; |
|
width: 100%; |
|
margin: 1.5rem 0; |
|
border: 1px solid #e5e7eb; |
|
border-radius: 0.5rem; |
|
overflow: hidden; |
|
} |
|
|
|
th, td { |
|
border-bottom: 1px solid #e5e7eb; |
|
padding: 0.75rem 1rem; |
|
text-align: left; |
|
vertical-align: top; |
|
} |
|
|
|
th { |
|
background-color: #f9fafb; |
|
font-weight: 600; |
|
color: #374151; |
|
} |
|
|
|
tr:last-child td { |
|
border-bottom: none; |
|
} |
|
|
|
hr { |
|
border: none; |
|
border-top: 1px solid #e5e7eb; |
|
margin: 3rem 0; |
|
} |
|
|
|
img, iframe { |
|
max-width: 100%; |
|
height: auto; |
|
border-radius: 0.5rem; |
|
} |
|
|
|
iframe { |
|
border: 1px solid #e5e7eb; |
|
} |
|
|
|
input[type="checkbox"] { |
|
margin-right: 0.5rem; |
|
transform: scale(1.1); |
|
} |
|
|
|
|
|
div[style*="border: 2px solid black"] { |
|
border: 1px solid #e5e7eb !important; |
|
border-radius: 0.75rem !important; |
|
background-color: #f9fafb !important; |
|
padding: 1.5rem !important; |
|
margin: 2rem 0 !important; |
|
} |
|
|
|
#loading { |
|
text-align: center; |
|
padding: 3rem; |
|
color: #6b7280; |
|
font-size: 1.125rem; |
|
} |
|
|
|
|
|
@media (max-width: 1024px) { |
|
.toc-sidebar { |
|
transform: translateX(-100%); |
|
} |
|
|
|
.toc-sidebar.open { |
|
transform: translateX(0); |
|
} |
|
|
|
.main-content { |
|
padding: 16px; |
|
} |
|
|
|
.mobile-toc-toggle { |
|
display: block; |
|
position: fixed; |
|
top: 16px; |
|
left: 16px; |
|
z-index: 102; |
|
background: #3b82f6; |
|
color: white; |
|
border: none; |
|
padding: 8px 12px; |
|
border-radius: 4px; |
|
cursor: pointer; |
|
} |
|
} |
|
|
|
.mobile-toc-toggle { |
|
display: none; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="layout"> |
|
|
|
<aside class="toc-sidebar" id="toc-sidebar"> |
|
<button class="toc-toggle" onclick="toggleTOC()" id="toc-toggle">◀</button> |
|
<h3>Table of Contents</h3> |
|
<div id="toc-content"> |
|
|
|
</div> |
|
</aside> |
|
|
|
|
|
<button class="mobile-toc-toggle" onclick="toggleMobileTOC()">☰ TOC</button> |
|
|
|
|
|
<div class="content-wrapper"> |
|
<main class="main-content"> |
|
<div id="loading">Loading...</div> |
|
<div id="content"></div> |
|
</main> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
function generateTOC(content) { |
|
const headings = content.querySelectorAll('h1, h2, h3, h4, h5, h6'); |
|
const toc = document.getElementById('toc-content'); |
|
|
|
let tocHTML = '<ul>'; |
|
|
|
headings.forEach((heading, index) => { |
|
const level = parseInt(heading.tagName.charAt(1)); |
|
const text = heading.textContent; |
|
const id = text.toLowerCase() |
|
.replace(/[^\w\s-]/g, '') |
|
.replace(/\s+/g, '-') |
|
.replace(/-+/g, '-') |
|
.replace(/^-|-$/g, ''); |
|
|
|
heading.id = id; |
|
|
|
const tocClass = `toc-level-${level}`; |
|
tocHTML += `<li><a href="#${id}" class="${tocClass}" onclick="setActiveLink(this)">${text}</a></li>`; |
|
}); |
|
|
|
tocHTML += '</ul>'; |
|
toc.innerHTML = tocHTML; |
|
} |
|
|
|
function setActiveLink(clickedLink) { |
|
document.querySelectorAll('.toc-sidebar a').forEach(link => { |
|
link.classList.remove('active'); |
|
}); |
|
clickedLink.classList.add('active'); |
|
} |
|
|
|
function toggleTOC() { |
|
const sidebar = document.getElementById('toc-sidebar'); |
|
const btn = document.getElementById('toc-toggle'); |
|
|
|
sidebar.classList.toggle('collapsed'); |
|
btn.innerHTML = sidebar.classList.contains('collapsed') ? '▶' : '◀'; |
|
} |
|
|
|
function toggleMobileTOC() { |
|
document.getElementById('toc-sidebar').classList.toggle('open'); |
|
} |
|
|
|
function highlightCurrentSection() { |
|
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6'); |
|
const tocLinks = document.querySelectorAll('.toc-sidebar a'); |
|
|
|
let current = ''; |
|
headings.forEach(heading => { |
|
const rect = heading.getBoundingClientRect(); |
|
if (rect.top <= 100) { |
|
current = heading.id; |
|
} |
|
}); |
|
|
|
tocLinks.forEach(link => { |
|
link.classList.remove('active'); |
|
if (link.getAttribute('href') === `#${current}`) { |
|
link.classList.add('active'); |
|
} |
|
}); |
|
} |
|
|
|
async function loadMarkdown() { |
|
try { |
|
const response = await fetch('eu-act-os-guideai.md'); |
|
const markdown = await response.text(); |
|
|
|
marked.setOptions({ |
|
breaks: true, |
|
gfm: true |
|
}); |
|
|
|
const html = marked.parse(markdown); |
|
const contentDiv = document.getElementById('content'); |
|
contentDiv.innerHTML = html; |
|
|
|
generateTOC(contentDiv); |
|
document.getElementById('loading').style.display = 'none'; |
|
window.addEventListener('scroll', highlightCurrentSection); |
|
|
|
} catch (error) { |
|
document.getElementById('loading').innerHTML = 'Error loading content: ' + error.message; |
|
} |
|
} |
|
|
|
loadMarkdown(); |
|
</script> |
|
</body> |
|
</html> |
|
|