Skip to content

Commit 390dfa5

Browse files
committed
Initial commit
1 parent e351759 commit 390dfa5

5 files changed

Lines changed: 331 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Build and Deploy PDF Manifest
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'papers/**'
9+
- '.github/workflows/build-manifest.yml'
10+
- 'build_manifest.js'
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: '20'
24+
25+
- name: Run build script
26+
run: node build_manifest.js
27+
28+
- name: Commit and push manifest.json
29+
run: |
30+
git config --global user.name 'GitHub Actions Bot'
31+
git config --global user.email 'actions@github.com'
32+
git add manifest.json
33+
if ! git diff --staged --quiet; then
34+
git commit -m "Autogenerate manifest.json"
35+
git push
36+
else
37+
echo "No changes to manifest.json. No commit needed."
38+
fi
39+
env:
40+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build_manifest.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const papersDirectory = 'papers';
5+
const manifestFile = 'manifest.json';
6+
7+
const siteData = {};
8+
9+
try {
10+
const sections = fs.readdirSync(papersDirectory, { withFileTypes: true })
11+
.filter(dirent => dirent.isDirectory())
12+
.map(dirent => dirent.name);
13+
14+
console.log(`Found sections: ${sections.join(', ')}`);
15+
16+
sections.forEach(section => {
17+
const sectionPath = path.join(papersDirectory, section);
18+
siteData[section] = [];
19+
20+
const files = fs.readdirSync(sectionPath)
21+
.filter(file => path.extname(file).toLowerCase() === '.pdf');
22+
23+
console.log(` Section '${section}': Found PDF files: ${files.join(', ') || 'None'}`);
24+
25+
files.forEach(file => {
26+
const filePath = path.join(sectionPath, file);
27+
const stats = fs.statSync(filePath);
28+
const fileSizeInKB = Math.round(stats.size / 1024);
29+
const title = path.basename(file, '.pdf').replace(/_/g, ' ');
30+
31+
siteData[section].push({
32+
id: `${section.toLowerCase()}-${file.toLowerCase().replace('.pdf', '').replace(/[^a-z0-9]/gi, '')}`,
33+
name: file,
34+
title: title,
35+
path: `./${papersDirectory}/${section}/${file}`,
36+
size: `${fileSizeInKB}KB`
37+
});
38+
});
39+
});
40+
41+
fs.writeFileSync(manifestFile, JSON.stringify(siteData, null, 2));
42+
console.log(`\nFile ${manifestFile} generated successfully!`);
43+
44+
} catch (error) {
45+
console.error(`Error generating manifest: ${error.message}`);
46+
if (error.code === 'ENOENT' && error.path === papersDirectory) {
47+
console.error(`Ensure the directory '${papersDirectory}' exists in the project root.`);
48+
}
49+
process.exit(1);
50+
}

favicon.webp

656 KB
Loading

index.html

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<meta name="description" content="ChainPaper: An open-source, collaborative archive of key blockchain and cryptocurrency whitepapers and documents. Browse by section, download the papers you're interested in, and contribute by adding new documents!">
7+
<title>ChainPaper - Whitepaper Archive</title>
8+
<link rel="icon" href="./favicon.webp" type="image/webp">
9+
<style>
10+
body {
11+
font-family: Arial, Helvetica, sans-serif;
12+
margin: 0;
13+
padding: 0;
14+
background-color: #ffffff;
15+
color: #000000;
16+
line-height: 1.5;
17+
}
18+
.container {
19+
display: flex;
20+
width: 100%;
21+
}
22+
aside {
23+
width: 200px;
24+
padding: 15px;
25+
border-right: 1px solid #cccccc;
26+
min-height: 100vh;
27+
background-color: #f0f0f0;
28+
}
29+
main {
30+
flex-grow: 1;
31+
padding: 15px;
32+
}
33+
h1 {
34+
font-size: 24px;
35+
color: #000000;
36+
margin-top: 0;
37+
}
38+
h2 {
39+
font-size: 20px;
40+
border-bottom: 1px solid #cccccc;
41+
padding-bottom: 5px;
42+
margin-top: 0;
43+
margin-bottom: 15px;
44+
}
45+
h3 {
46+
font-size: 16px;
47+
margin-top: 0;
48+
margin-bottom: 5px;
49+
}
50+
p {
51+
margin-top: 0;
52+
margin-bottom: 10px;
53+
}
54+
nav ul, #pdfListContainer {
55+
list-style-type: none;
56+
padding: 0;
57+
margin: 0;
58+
}
59+
nav li a {
60+
text-decoration: none;
61+
color: #0000ff;
62+
display: block;
63+
padding: 5px 0;
64+
}
65+
nav li a:hover {
66+
text-decoration: underline;
67+
color: #ff0000;
68+
}
69+
nav li a.active {
70+
font-weight: bold;
71+
color: #000000;
72+
text-decoration: none;
73+
}
74+
.file-item {
75+
border: 1px solid #dddddd;
76+
padding: 10px;
77+
margin-bottom: 10px;
78+
background-color: #f9f9f9;
79+
}
80+
.file-item p {
81+
font-size: 14px;
82+
margin: 3px 0;
83+
}
84+
.download-link {
85+
text-decoration: none;
86+
color: #0000ff;
87+
font-weight: bold;
88+
display: inline-block;
89+
padding: 3px 6px;
90+
border: 1px solid #0000ff;
91+
background-color: #e0e0ff;
92+
}
93+
.download-link:hover {
94+
background-color: #d0d0ff;
95+
text-decoration: underline;
96+
}
97+
.message {
98+
color: #333333;
99+
font-style: italic;
100+
margin-top: 20px;
101+
}
102+
</style>
103+
</head>
104+
<body>
105+
<div class="container">
106+
<aside>
107+
<h1>ChainPaper</h1>
108+
<nav id="sectionList">
109+
<ul></ul>
110+
</nav>
111+
</aside>
112+
113+
<main>
114+
<header>
115+
<h2 id="currentSectionTitle"></h2>
116+
</header>
117+
118+
<section id="pdf-display-area">
119+
<ul id="pdfListContainer">
120+
</ul>
121+
<p id="noFilesMessage" class="message" style="display: none;">
122+
No PDF files available in this section.
123+
</p>
124+
<p id="selectSectionMessage" class="message" style="display: none;">
125+
Please select a section from the sidebar to view PDFs.
126+
</p>
127+
<p id="loadingMessage" class="message" style="display: block;">
128+
Loading documents...
129+
</p>
130+
</section>
131+
</main>
132+
</div>
133+
134+
<script>
135+
const sectionListUl = document.querySelector('#sectionList ul');
136+
const pdfListUl = document.getElementById('pdfListContainer');
137+
const noFilesMessage = document.getElementById('noFilesMessage');
138+
const selectSectionMessage = document.getElementById('selectSectionMessage');
139+
const loadingMessage = document.getElementById('loadingMessage');
140+
const currentSectionTitleElement = document.getElementById('currentSectionTitle');
141+
let activeSection = null;
142+
let sitePdfData = {};
143+
144+
function renderPdfsForSection(sectionName) {
145+
currentSectionTitleElement.textContent = sectionName;
146+
pdfListUl.innerHTML = '';
147+
selectSectionMessage.style.display = 'none';
148+
noFilesMessage.style.display = 'none';
149+
pdfListUl.style.display = 'block';
150+
151+
const files = sitePdfData[sectionName];
152+
153+
if (!files || files.length === 0) {
154+
noFilesMessage.style.display = 'block';
155+
pdfListUl.style.display = 'none';
156+
return;
157+
}
158+
159+
files.forEach(file => {
160+
const listItem = document.createElement('li');
161+
listItem.className = 'file-item';
162+
163+
let content = `<h3>${file.title || file.name}</h3>`;
164+
if (file.size) {
165+
content += `<p><em>Size:</em> ${file.size}</p>`;
166+
}
167+
content += `<p><a href="${file.path}" download="${file.name}" class="download-link">Download Document</a></p>`;
168+
169+
listItem.innerHTML = content;
170+
pdfListUl.appendChild(listItem);
171+
});
172+
}
173+
174+
function renderSectionLinks() {
175+
sectionListUl.innerHTML = '';
176+
Object.keys(sitePdfData).forEach(sectionName => {
177+
const listItem = document.createElement('li');
178+
const link = document.createElement('a');
179+
link.href = '#';
180+
link.textContent = sectionName;
181+
if (sectionName === activeSection) {
182+
link.classList.add('active');
183+
}
184+
185+
link.addEventListener('click', (e) => {
186+
e.preventDefault();
187+
setActiveSection(sectionName);
188+
renderPdfsForSection(sectionName);
189+
});
190+
listItem.appendChild(link);
191+
sectionListUl.appendChild(listItem);
192+
});
193+
}
194+
195+
function setActiveSection(sectionName) {
196+
activeSection = sectionName;
197+
renderSectionLinks();
198+
}
199+
200+
document.addEventListener('DOMContentLoaded', () => {
201+
loadingMessage.style.display = 'block';
202+
noFilesMessage.style.display = 'none';
203+
selectSectionMessage.style.display = 'none';
204+
pdfListUl.style.display = 'none';
205+
206+
fetch('./manifest.json')
207+
.then(response => {
208+
if (!response.ok) {
209+
throw new Error(`HTTP error! status: ${response.status}`);
210+
}
211+
return response.json();
212+
})
213+
.then(data => {
214+
sitePdfData = data;
215+
loadingMessage.style.display = 'none';
216+
const sectionNames = Object.keys(sitePdfData);
217+
218+
if (sectionNames.length > 0) {
219+
setActiveSection(sectionNames[0]);
220+
renderPdfsForSection(activeSection);
221+
} else {
222+
currentSectionTitleElement.textContent = "No Sections Available";
223+
selectSectionMessage.textContent = "There are no sections or PDF files to display.";
224+
selectSectionMessage.style.display = 'block';
225+
}
226+
})
227+
.catch(error => {
228+
console.error("Could not load manifest.json:", error);
229+
loadingMessage.style.display = 'none';
230+
currentSectionTitleElement.textContent = "Error";
231+
selectSectionMessage.textContent = "Could not load document data. Please try again later.";
232+
selectSectionMessage.style.display = 'block';
233+
});
234+
235+
noFilesMessage.textContent = "No PDF files available in this section.";
236+
selectSectionMessage.textContent = "Please select a section from the sidebar to view PDFs.";
237+
loadingMessage.textContent = "Loading documents...";
238+
});
239+
</script>
240+
</body>
241+
</html>

papers/Bitcoin/bitcoin.pdf

180 KB
Binary file not shown.

0 commit comments

Comments
 (0)