Skip to content

Commit 962e667

Browse files
committed
fix: improve documentation page and API robustness
Fixes identified in PR code review: **docs/js/main.js:** - Improve platform asset classification using regex word boundaries instead of indexOf() to avoid substring conflicts (e.g., 'darwin-linux' misclassification) - Add cache expiration mechanism (1 hour) for GitHub API responses to show latest release information - Enhance URL validation to restrict to HTTPS and authorized GitHub domains (github.com, github.io, githubusercontent.com) **docs/documentation.html:** - Fix footer documentation links to point to correct GitHub file locations instead of non-existent local markdown files - Add ARIA labels (role='navigation', aria-label) for better accessibility - Implement slugify() function to generate stable heading IDs from text (e.g., 'C++ API Reference' -> 'cpp-api-reference') for durable anchor links - Improve error messages with context-specific feedback (network vs file not found) - Fix missing closing brace in generateSidebar() function - Optimize sidebar navigation to make all headings clickable without duplication **README.md:** - Restore 'Dual Language APIs' feature description (was commented out)
1 parent fcf5383 commit 962e667

3 files changed

Lines changed: 71 additions & 29 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ A high-performance, lightweight cross-platform camera capture library with hardw
3131
- **Lightweight**: Zero external dependencies - uses only system frameworks
3232
- **Cross Platform**: Windows (DirectShow), macOS/iOS (AVFoundation), Linux (V4L2)
3333
- **Multiple Formats**: RGB, BGR, YUV (NV12/I420) with automatic conversion
34-
<!-- - **Dual Language APIs**: ✨ **New Complete Pure C Interface** - Both modern C++ API and traditional C99 interface for various project integration and language bindings -->
34+
- **Dual Language APIs**: ✨ **Complete Pure C Interface** - Both modern C++ API and traditional C99 interface for various project integration and language bindings
3535
- **Production Ready**: Comprehensive test suite with 95%+ accuracy validation
3636
- **Virtual Camera Support**: Compatible with OBS Virtual Camera and similar tools
3737

docs/documentation.html

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
</header>
3737

3838
<div class="doc-layout">
39-
<aside class="doc-sidebar" id="doc-sidebar">
39+
<aside class="doc-sidebar" id="doc-sidebar" role="navigation" aria-label="Documentation navigation">
4040
<p class="lang-en">Loading navigation...</p>
4141
<p class="lang-zh">正在加载导航...</p>
4242
</aside>
@@ -65,9 +65,9 @@ <h4 class="lang-zh">下载</h4>
6565
<h4 class="lang-en">Documentation</h4>
6666
<h4 class="lang-zh">文档</h4>
6767
<ul>
68-
<li><a href="#"><span class="lang-en">Overview</span><span class="lang-zh">概述</span></a></li>
69-
<li><a href="C_Interface.md">C Interface</a></li>
70-
<li><a href="CMAKE_OPTIONS.md"><span class="lang-en">CMake Options</span><span class="lang-zh">CMake 选项</span></a></li>
68+
<li><a href="documentation.html"><span class="lang-en">Main Documentation</span><span class="lang-zh">主文档</span></a></li>
69+
<li><a href="https://github.com/wysaid/CameraCapture/blob/main/docs/C_Interface.md">C Interface</a></li>
70+
<li><a href="https://github.com/wysaid/CameraCapture/blob/main/docs/CMAKE_OPTIONS.md"><span class="lang-en">CMake Options</span><span class="lang-zh">CMake 选项</span></a></li>
7171
</ul>
7272
</div>
7373

@@ -196,12 +196,38 @@ <h4 class="lang-zh">许可证</h4>
196196
})
197197
.catch(function(error) {
198198
console.error('Error loading documentation:', error);
199+
var messageEn = 'Failed to load documentation. Please try again later.';
200+
var messageZh = '加载文档失败,请稍后重试。';
201+
202+
if (error && error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
203+
messageEn = 'Network error - please check your internet connection.';
204+
messageZh = '网络错误 - 请检查您的网络连接。';
205+
}
206+
199207
document.getElementById('doc-content').innerHTML =
200-
'<p class="lang-en">Failed to load documentation. Please try again later.</p>' +
201-
'<p class="lang-zh">加载文档失败,请稍后重试。</p>';
208+
'<p class="lang-en">' + messageEn + ' <a href="https://github.com/wysaid/CameraCapture/blob/main/README.md">View on GitHub</a></p>' +
209+
'<p class="lang-zh">' + messageZh + ' <a href="https://github.com/wysaid/CameraCapture/blob/main/README.zh-CN.md">在 GitHub 上查看</a></p>';
202210
});
203211
}
204212

213+
// Generate slugified ID from heading text for stable anchor links
214+
function slugify(text) {
215+
if (!text) return '';
216+
return text
217+
.toLowerCase()
218+
.trim()
219+
.replace(/[^\w\s-]/g, '')
220+
.replace(/\s+/g, '-')
221+
.replace(/-+/g, '-');
222+
}
223+
224+
// Simple HTML escaping
225+
function escapeHtml(text) {
226+
var div = document.createElement('div');
227+
div.textContent = text;
228+
return div.innerHTML;
229+
}
230+
205231
// Generate sidebar navigation from h2 and h3 headings
206232
function generateSidebar() {
207233
var content = document.getElementById('doc-content');
@@ -216,10 +242,15 @@ <h4 class="lang-zh">许可证</h4>
216242
var html = '';
217243
var currentH2 = null;
218244

219-
headings.forEach(function(heading, index) {
220-
// Add an id to each heading if it doesn't have one
245+
headings.forEach(function(heading) {
246+
// Generate stable ID from heading text
247+
if (!heading.id) {
248+
heading.id = slugify(heading.textContent);
249+
}
250+
251+
// Ensure ID is not empty
221252
if (!heading.id) {
222-
heading.id = 'section-' + index;
253+
heading.id = 'heading-' + Math.random().toString(36).substr(2, 9);
223254
}
224255

225256
var text = heading.textContent;
@@ -229,11 +260,13 @@ <h4 class="lang-zh">许可证</h4>
229260
if (currentH2) {
230261
html += '</ul>';
231262
}
232-
html += '<h3>' + escapeHtml(text) + '</h3>';
263+
// H2 as a clickable category title
264+
html += '<h3><a href="#' + escapeHtml(id) + '">' + escapeHtml(text) + '</a></h3>';
233265
html += '<ul>';
234266
currentH2 = heading;
235267
} else if (heading.tagName === 'H3') {
236-
html += '<li><a href="#' + id + '">' + escapeHtml(text) + '</a></li>';
268+
// H3 as a list item link
269+
html += '<li><a href="#' + escapeHtml(id) + '">' + escapeHtml(text) + '</a></li>';
237270
}
238271
});
239272

@@ -244,13 +277,6 @@ <h4 class="lang-zh">许可证</h4>
244277
sidebar.innerHTML = html;
245278
}
246279

247-
// Simple HTML escaping
248-
function escapeHtml(text) {
249-
var div = document.createElement('div');
250-
div.textContent = text;
251-
return div.innerHTML;
252-
}
253-
254280
// Initialize scroll spy for sidebar
255281
function initScrollSpy() {
256282
var headings = document.querySelectorAll('#doc-content h2, #doc-content h3');

docs/js/main.js

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,12 @@
6060
// GitHub Release API
6161
var GITHUB_REPO = 'wysaid/CameraCapture';
6262
var cachedRelease = null;
63+
var releaseCacheTime = null;
64+
var CACHE_DURATION = 3600000; // 1 hour in milliseconds
6365

6466
function fetchLatestRelease(callback) {
65-
if (cachedRelease) {
67+
var now = Date.now();
68+
if (cachedRelease && releaseCacheTime && (now - releaseCacheTime) < CACHE_DURATION) {
6669
callback(cachedRelease);
6770
return;
6871
}
@@ -75,6 +78,7 @@
7578
if (xhr.status >= 200 && xhr.status < 300) {
7679
try {
7780
cachedRelease = JSON.parse(xhr.responseText);
81+
releaseCacheTime = Date.now();
7882
callback(cachedRelease);
7983
} catch (e) {
8084
callback(null);
@@ -100,13 +104,16 @@
100104
xhr.send();
101105
}
102106

103-
// URL validation for GitHub assets
107+
// URL validation for GitHub assets - only allow HTTPS GitHub URLs
104108
function isValidGitHubUrl(url) {
105109
if (!url || typeof url !== 'string') return false;
106110
try {
107111
var parsed = new URL(url);
108-
return parsed.protocol === 'https:' &&
109-
(parsed.hostname === 'github.com' || parsed.hostname.endsWith('.github.com'));
112+
if (parsed.protocol !== 'https:') return false;
113+
return parsed.hostname === 'github.com' ||
114+
parsed.hostname.endsWith('.github.com') ||
115+
parsed.hostname === 'github.io' ||
116+
parsed.hostname.endsWith('.githubusercontent.com');
110117
} catch (e) {
111118
return false;
112119
}
@@ -154,15 +161,24 @@
154161

155162
assets.forEach(function(asset) {
156163
var name = (asset.name || '').toLowerCase();
157-
if (name.indexOf('windows') !== -1 || name.indexOf('win') !== -1) {
164+
var matched = false;
165+
if (/\b(windows|win32|win64)\b/.test(name)) {
158166
platforms.windows.push(asset);
159-
} else if (name.indexOf('macos') !== -1 || name.indexOf('darwin') !== -1 || name.indexOf('mac') !== -1) {
167+
matched = true;
168+
}
169+
if (!matched && /\b(macos|darwin|mac)\b/.test(name)) {
160170
platforms.macos.push(asset);
161-
} else if (name.indexOf('linux') !== -1) {
162-
platforms.linux.push(asset);
163-
} else if (name.indexOf('ios') !== -1) {
171+
matched = true;
172+
}
173+
if (!matched && /\bios\b/.test(name)) {
164174
platforms.ios.push(asset);
165-
} else {
175+
matched = true;
176+
}
177+
if (!matched && /\b(linux|ubuntu|debian|fedora|arch)\b/.test(name)) {
178+
platforms.linux.push(asset);
179+
matched = true;
180+
}
181+
if (!matched) {
166182
platforms.other.push(asset);
167183
}
168184
});

0 commit comments

Comments
 (0)