Skip to content

Commit 153c39f

Browse files
authored
add main file
1 parent 86eeea7 commit 153c39f

File tree

1 file changed

+255
-0
lines changed

1 file changed

+255
-0
lines changed

index.html

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
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+
<title>GitHub HTML Visualizer</title>
7+
<style>
8+
* { box-sizing: border-box; margin: 0; padding: 0; }
9+
body {
10+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
11+
background: #0d1117;
12+
color: #e6edf3;
13+
min-height: 100vh;
14+
display: flex;
15+
flex-direction: column;
16+
}
17+
header {
18+
padding: 18px 24px;
19+
background: #161b22;
20+
border-bottom: 1px solid #30363d;
21+
display: flex;
22+
align-items: center;
23+
gap: 12px;
24+
}
25+
header h1 { font-size: 16px; font-weight: 600; }
26+
header span { font-size: 13px; color: #8b949e; }
27+
.star-btn {
28+
margin-left: auto;
29+
display: flex;
30+
align-items: center;
31+
gap: 6px;
32+
background: #21262d;
33+
border: 1px solid #30363d;
34+
color: #e6edf3;
35+
border-radius: 6px;
36+
padding: 5px 12px;
37+
font-size: 13px;
38+
font-weight: 600;
39+
text-decoration: none;
40+
transition: background .2s, border-color .2s;
41+
}
42+
.star-btn:hover { background: #30363d; border-color: #8b949e; }
43+
.star-btn svg { fill: #e3b341; }
44+
.input-bar {
45+
padding: 16px 24px;
46+
background: #161b22;
47+
border-bottom: 1px solid #30363d;
48+
display: flex;
49+
gap: 10px;
50+
}
51+
.url-input {
52+
flex: 1;
53+
background: #0d1117;
54+
border: 1px solid #30363d;
55+
border-radius: 6px;
56+
padding: 8px 14px;
57+
color: #e6edf3;
58+
font-size: 14px;
59+
outline: none;
60+
transition: border-color .2s;
61+
}
62+
.url-input::placeholder { color: #484f58; }
63+
.url-input:focus { border-color: #388bfd; }
64+
.btn {
65+
background: #238636;
66+
color: #fff;
67+
border: none;
68+
border-radius: 6px;
69+
padding: 8px 20px;
70+
font-size: 14px;
71+
font-weight: 600;
72+
cursor: pointer;
73+
transition: background .2s;
74+
}
75+
.btn:hover { background: #2ea043; }
76+
.btn:disabled { background: #21262d; color: #484f58; cursor: not-allowed; }
77+
.status-bar {
78+
padding: 6px 24px;
79+
font-size: 12px;
80+
min-height: 28px;
81+
display: flex;
82+
align-items: center;
83+
gap: 8px;
84+
}
85+
.status-bar.error { color: #f85149; }
86+
.status-bar.success{ color: #3fb950; }
87+
.status-bar.loading{ color: #58a6ff; }
88+
.spinner {
89+
width: 12px; height: 12px;
90+
border: 2px solid #30363d;
91+
border-top-color: #58a6ff;
92+
border-radius: 50%;
93+
animation: spin .7s linear infinite;
94+
display: none;
95+
}
96+
@keyframes spin { to { transform: rotate(360deg); } }
97+
.api-badge {
98+
padding: 4px 24px;
99+
background: #161b22;
100+
border-bottom: 1px solid #30363d;
101+
font-size: 11px;
102+
color: #484f58;
103+
display: none;
104+
}
105+
.api-badge a { color: #58a6ff; text-decoration: none; font-family: monospace; font-size: 11px; }
106+
.api-badge a:hover { text-decoration: underline; }
107+
iframe {
108+
flex: 1;
109+
border: none;
110+
display: none;
111+
min-height: calc(100vh - 130px);
112+
background: #fff;
113+
}
114+
.placeholder {
115+
flex: 1;
116+
display: flex;
117+
flex-direction: column;
118+
align-items: center;
119+
justify-content: center;
120+
gap: 14px;
121+
color: #484f58;
122+
background: #0d1117;
123+
min-height: calc(100vh - 130px);
124+
}
125+
.placeholder svg { opacity: .25; }
126+
.placeholder p { font-size: 14px; }
127+
.placeholder code {
128+
font-size: 12px; background: #161b22;
129+
padding: 6px 14px; border-radius: 6px;
130+
border: 1px solid #30363d; color: #8b949e; font-family: monospace;
131+
}
132+
</style>
133+
</head>
134+
<body>
135+
136+
<header>
137+
<svg width="22" height="22" viewBox="0 0 24 24" fill="#e6edf3">
138+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/>
139+
</svg>
140+
<h1>GitHub HTML Visualizer</h1>
141+
<span>— paste any GitHub .html blob URL to preview it live</span>
142+
<a class="star-btn" href="https://github.com/ghviz/github-html-viewer" target="_blank">
143+
<svg width="15" height="15" viewBox="0 0 24 24"><path d="M12 .587l3.668 7.431 8.2 1.192-5.934 5.782 1.4 8.168L12 18.896l-7.334 3.864 1.4-8.168L.132 9.21l8.2-1.192z"/></svg>
144+
Star on GitHub
145+
</a>
146+
</header>
147+
148+
<div class="input-bar">
149+
<input id="urlInput" class="url-input" type="text"
150+
placeholder="https://github.com/user/repo/blob/main/file.html"
151+
value="https://github.com/shanraisshan/claude-code-best-practice/blob/main/presentation/index.html"/>
152+
<button class="btn" id="loadBtn" onclick="loadPreview()">Render ▶</button>
153+
</div>
154+
155+
<div class="status-bar" id="statusBar">
156+
<div class="spinner" id="spinner"></div>
157+
<span id="statusText">Enter a GitHub HTML file URL above and click Render.</span>
158+
</div>
159+
160+
<div class="api-badge" id="apiBadge">
161+
📡 Via GitHub API: <a id="apiLink" href="#" target="_blank"></a>
162+
</div>
163+
164+
<div class="placeholder" id="placeholder">
165+
<svg width="60" height="60" viewBox="0 0 24 24" fill="#e6edf3">
166+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 1.5L18.5 9H13V3.5zM6 20V4h5v7h7v9H6z"/>
167+
</svg>
168+
<p>No preview yet</p>
169+
<code>github.com/user/repo/blob/branch/file.html</code>
170+
</div>
171+
172+
<iframe id="previewFrame" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
173+
174+
<script>
175+
document.getElementById('urlInput').addEventListener('keydown', e => {
176+
if (e.key === 'Enter') loadPreview();
177+
});
178+
179+
// Parse a GitHub blob URL → { apiUrl, rawUrl }
180+
function parseGitHubUrl(input) {
181+
try {
182+
const url = new URL(input.trim());
183+
if (!url.hostname.includes('github.com')) return null;
184+
const m = url.pathname.match(/^\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/);
185+
if (!m) return null;
186+
const [, owner, repo, branch, path] = m;
187+
return {
188+
apiUrl: `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`,
189+
rawUrl: `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`
190+
};
191+
} catch { return null; }
192+
}
193+
194+
function setStatus(msg, type = '') {
195+
const bar = document.getElementById('statusBar');
196+
document.getElementById('statusText').textContent = msg;
197+
document.getElementById('spinner').style.display = type === 'loading' ? 'block' : 'none';
198+
bar.className = 'status-bar' + (type ? ' ' + type : '');
199+
}
200+
201+
async function loadPreview() {
202+
const parsed = parseGitHubUrl(document.getElementById('urlInput').value);
203+
const badge = document.getElementById('apiBadge');
204+
const link = document.getElementById('apiLink');
205+
const frame = document.getElementById('previewFrame');
206+
const ph = document.getElementById('placeholder');
207+
const btn = document.getElementById('loadBtn');
208+
209+
if (!parsed) {
210+
setStatus('⚠ Invalid URL — must be a GitHub blob URL: github.com/user/repo/blob/branch/file.html', 'error');
211+
return;
212+
}
213+
214+
// Show API badge
215+
badge.style.display = 'block';
216+
link.href = link.textContent = parsed.apiUrl;
217+
218+
btn.disabled = true;
219+
setStatus('Fetching via GitHub API…', 'loading');
220+
ph.style.display = 'none';
221+
frame.style.display = 'none';
222+
223+
try {
224+
const res = await fetch(parsed.apiUrl, {
225+
headers: { Accept: 'application/vnd.github.v3+json' }
226+
});
227+
228+
if (res.status === 403) throw new Error('Rate limit hit — try again in a minute, or the repo may be private.');
229+
if (res.status === 404) throw new Error('File not found — check the URL.');
230+
if (!res.ok) throw new Error(`GitHub API error ${res.status}`);
231+
232+
const json = await res.json();
233+
if (!json.content) throw new Error('No content returned from GitHub API.');
234+
235+
// GitHub returns base64 with newlines — strip them then decode
236+
const html = decodeURIComponent(
237+
atob(json.content.replace(/\n/g, ''))
238+
.split('')
239+
.map(c => '%' + c.charCodeAt(0).toString(16).padStart(2, '0'))
240+
.join('')
241+
);
242+
243+
frame.srcdoc = html;
244+
frame.style.display = 'block';
245+
setStatus(`✓ Rendered successfully — ${(html.length / 1024).toFixed(1)} KB`, 'success');
246+
} catch (err) {
247+
ph.style.display = 'flex';
248+
setStatus(`✗ ${err.message}`, 'error');
249+
} finally {
250+
btn.disabled = false;
251+
}
252+
}
253+
</script>
254+
</body>
255+
</html>

0 commit comments

Comments
 (0)