Skip to content

Commit ad53b28

Browse files
committed
feat: implement marked.js dynamic Markdown fetcher and expandable study guide accordions
1 parent 0a6f8ff commit ad53b28

3 files changed

Lines changed: 232 additions & 3 deletions

File tree

app.js

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -803,9 +803,21 @@ function renderTopicDetails(topic) {
803803
<h3 class="section-title"><i class="fa-solid fa-list-check"></i> Table of Contents (TOC)</h3>
804804
<div class="timeline">
805805
${topic.syllabus.map((item, idx) => `
806-
<div class="timeline-item">
807-
<h4 class="timeline-title">${idx + 1}. ${item.title}</h4>
808-
<p class="timeline-desc">${item.desc}</p>
806+
<div class="timeline-item" id="chapter-${idx}">
807+
<div class="timeline-content-wrapper">
808+
<h4 class="timeline-title">${idx + 1}. ${item.title}</h4>
809+
<p class="timeline-desc">${item.desc}</p>
810+
<button class="load-notes-btn" onclick="toggleChapterNotes('${topic.id}', ${idx})">
811+
<i class="fa-solid fa-book-open"></i> Read Study Notes
812+
</button>
813+
</div>
814+
<div class="chapter-accordion" id="accordion-${idx}">
815+
<div class="markdown-body" id="notes-content-${idx}">
816+
<div style="display:flex; align-items:center; gap:8px; color:var(--text-muted);">
817+
<i class="fa-solid fa-spinner fa-spin"></i> Loading notes...
818+
</div>
819+
</div>
820+
</div>
809821
</div>
810822
`).join('')}
811823
</div>
@@ -848,6 +860,91 @@ function renderTopicDetails(topic) {
848860
`;
849861
}
850862

863+
// Cache for storing fetched and parsed chapter notes
864+
const loadedNotesCache = {};
865+
866+
async function toggleChapterNotes(topicId, index) {
867+
const accordion = document.getElementById(`accordion-${index}`);
868+
const notesContent = document.getElementById(`notes-content-${index}`);
869+
const itemContainer = document.getElementById(`chapter-${index}`);
870+
const button = itemContainer.querySelector('.load-notes-btn');
871+
872+
// Toggle accordion state
873+
if (accordion.classList.contains('open')) {
874+
accordion.classList.remove('open');
875+
button.innerHTML = `<i class="fa-solid fa-book-open"></i> Read Study Notes`;
876+
return;
877+
}
878+
879+
// Open accordion
880+
accordion.classList.add('open');
881+
button.innerHTML = `<i class="fa-solid fa-chevron-up"></i> Hide Notes`;
882+
883+
const cacheKey = `${topicId}-${index}`;
884+
if (loadedNotesCache[cacheKey]) {
885+
notesContent.innerHTML = loadedNotesCache[cacheKey];
886+
return;
887+
}
888+
889+
const topic = topicsData.find(t => t.id === topicId);
890+
const chapter = topic.syllabus[index];
891+
892+
// Dynamic Path Derivation
893+
let chapterPath = chapter.path;
894+
if (!chapterPath) {
895+
const titleClean = chapter.title.toLowerCase();
896+
if (titleClean.includes('basics') || titleClean.includes('fundamental') || titleClean.includes('setup') || titleClean.includes('syntax') || titleClean.includes('first')) {
897+
chapterPath = `${topic.githubPath}/Basics/README.md`;
898+
} else if (titleClean.includes('advanced') || titleClean.includes('oop') || titleClean.includes('class')) {
899+
chapterPath = `${topic.githubPath}/Advanced/README.md`;
900+
} else if (titleClean.includes('intermediate')) {
901+
chapterPath = `${topic.githubPath}/Intermediate/README.md`;
902+
} else {
903+
chapterPath = `${topic.githubPath}/README.md`;
904+
}
905+
}
906+
907+
try {
908+
// Fetch target file
909+
let response = await fetch(chapterPath);
910+
if (!response.ok) {
911+
// Fallback to parent directory README
912+
response = await fetch(`${topic.githubPath}/README.md`);
913+
}
914+
915+
if (!response.ok) {
916+
throw new Error("Notes file not found");
917+
}
918+
919+
const markdown = await response.text();
920+
const htmlContent = marked.parse(markdown);
921+
loadedNotesCache[cacheKey] = htmlContent;
922+
notesContent.innerHTML = htmlContent;
923+
} catch (err) {
924+
// Ultimate fallback layout
925+
const placeholderHTML = `
926+
<div style="border-left: 4px solid var(--primary); padding-left:14px; margin: 4px 0;">
927+
<h4 style="margin: 0 0 8px 0; color:var(--primary); font-weight:700;"><i class="fa-solid fa-graduation-cap"></i> ${chapter.title} Notes</h4>
928+
<p style="margin-bottom:12px;">${chapter.desc}</p>
929+
<h5 style="margin:12px 0 6px 0; font-weight:700; color:var(--text-primary);">🎯 Core Study Areas</h5>
930+
<ul style="padding-left:20px; margin-bottom:12px;">
931+
<li><strong>Fundamentals & Setup</strong>: Explore runtime configuration, compiler paths, package variables, and core execution layers.</li>
932+
<li><strong>Syntax Patterns</strong>: Practice variables declaration, logic flows, iterations, and control branches.</li>
933+
<li><strong>Standard Execution</strong>: Compile and debug first programs logging parameters.</li>
934+
</ul>
935+
<div class="code-panel" style="margin-top:14px; background:#06070d; border:1px solid var(--card-border);">
936+
<div class="code-header" style="background: rgba(255,255,255,0.02); padding: 8px 12px; border-bottom:1px solid var(--card-border);">
937+
<span style="font-size:12px; color:var(--text-muted);"><i class="fa-solid fa-code"></i> Reference Snippet</span>
938+
</div>
939+
<pre style="margin:0; padding:12px; font-family:var(--font-mono); font-size:13px; color:#e2e8f0; overflow-x:auto;">${topic.codeSnippet || '// Execute program and test configurations'}</pre>
940+
</div>
941+
</div>
942+
`;
943+
loadedNotesCache[cacheKey] = placeholderHTML;
944+
notesContent.innerHTML = placeholderHTML;
945+
}
946+
}
947+
851948
// 7. Render Landing View
852949
function renderLanding() {
853950
activeBreadcrumb.textContent = "Overview";

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
<link rel="stylesheet" href="styles.css">
1616
<!-- Favicon -->
1717
<link rel="icon" type="image/svg+xml" href="favicon.svg">
18+
<!-- Marked.js Markdown Parser -->
19+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
1820
</head>
1921
<body>
2022
<div class="glow-bg"></div>

styles.css

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,133 @@ body {
724724
padding: 0 16px;
725725
}
726726
}
727+
728+
/* ==========================================================================
729+
Interactive Markdown Accordion & Rendered Guide Styles
730+
========================================================================== */
731+
.timeline-content-wrapper {
732+
display: flex;
733+
flex-direction: column;
734+
gap: 10px;
735+
}
736+
737+
.load-notes-btn {
738+
display: inline-flex;
739+
align-items: center;
740+
gap: 6px;
741+
padding: 6px 12px;
742+
background: rgba(255, 255, 255, 0.04);
743+
border: 1px solid var(--card-border);
744+
border-radius: 6px;
745+
color: var(--primary);
746+
font-size: 12px;
747+
font-weight: 600;
748+
cursor: pointer;
749+
width: fit-content;
750+
transition: var(--transition-smooth);
751+
user-select: none;
752+
}
753+
754+
.load-notes-btn:hover {
755+
background: rgba(0, 242, 254, 0.08);
756+
border-color: var(--primary);
757+
}
758+
759+
.chapter-accordion {
760+
max-height: 0;
761+
opacity: 0;
762+
overflow: hidden;
763+
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s ease-out, padding 0.3s ease-out;
764+
background: rgba(0, 0, 0, 0.2);
765+
border-radius: 8px;
766+
border: 0 solid var(--card-border);
767+
}
768+
769+
.chapter-accordion.open {
770+
max-height: 1200px;
771+
opacity: 1;
772+
padding: 18px;
773+
margin-top: 10px;
774+
border-width: 1px;
775+
overflow-y: auto;
776+
}
777+
778+
body.light-mode .chapter-accordion {
779+
background: rgba(255, 255, 255, 0.5);
780+
}
781+
782+
/* Rendered Markdown Typography */
783+
.markdown-body {
784+
font-size: 14px;
785+
color: var(--text-secondary);
786+
line-height: 1.6;
787+
}
788+
789+
.markdown-body h1,
790+
.markdown-body h2,
791+
.markdown-body h3,
792+
.markdown-body h4 {
793+
color: var(--text-primary);
794+
margin: 20px 0 10px 0;
795+
font-weight: 600;
796+
}
797+
798+
.markdown-body h1 {
799+
font-size: 20px;
800+
border-bottom: 1px solid var(--card-border);
801+
padding-bottom: 6px;
802+
}
803+
804+
.markdown-body h2 { font-size: 18px; }
805+
.markdown-body h3 { font-size: 15px; }
806+
807+
.markdown-body p {
808+
margin-bottom: 12px;
809+
}
810+
811+
.markdown-body ul,
812+
.markdown-body ol {
813+
padding-left: 20px;
814+
margin-bottom: 12px;
815+
}
816+
817+
.markdown-body li {
818+
margin-bottom: 4px;
819+
}
820+
821+
.markdown-body code {
822+
font-family: var(--font-mono);
823+
background: rgba(255, 255, 255, 0.06);
824+
padding: 2px 6px;
825+
border-radius: 4px;
826+
font-size: 13px;
827+
color: var(--primary);
828+
}
829+
830+
body.light-mode .markdown-body code {
831+
background: rgba(15, 23, 42, 0.05);
832+
}
833+
834+
.markdown-body pre {
835+
background: #06070d;
836+
padding: 16px;
837+
border-radius: 8px;
838+
overflow-x: auto;
839+
margin-bottom: 14px;
840+
border: 1px solid var(--card-border);
841+
}
842+
843+
.markdown-body pre code {
844+
background: none;
845+
padding: 0;
846+
color: #e2e8f0;
847+
font-size: 13px;
848+
}
849+
850+
.markdown-body blockquote {
851+
border-left: 4px solid var(--primary);
852+
padding-left: 16px;
853+
color: var(--text-muted);
854+
margin: 12px 0;
855+
font-style: italic;
856+
}

0 commit comments

Comments
 (0)