@@ -25,7 +25,8 @@ function collectSourceFiles(projectRoot) {
2525 const files = { } ;
2626 function scan ( dir , prefix = "" ) {
2727 if ( ! existsSync ( dir ) ) return ;
28- const entries = readdirSync ( dir , { withFileTypes : true } ) ;
28+ const entries = readdirSync ( dir , { withFileTypes : true } )
29+ . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
2930 for ( const e of entries ) {
3031 const name = e . name ;
3132 if ( name === "node_modules" || name . startsWith ( "." ) ) continue ;
@@ -81,81 +82,78 @@ export function generateSourceViewerHtml(root) {
8182<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/markdown.min.js"></script>
8283<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
8384<style>
84- /* GitHub Dark 100% - exact colors from github.com */
85+ /* GitHub dark theme - variables */
8586:root{
86- --color-canvas-default:#0d1117;
87- --color-canvas-subtle:#161b22;
88- --color-canvas-inset:#010409;
89- --color-border-default:#30363d;
90- --color-border-muted:#21262d;
91- --color-fg-default:#e6edf3;
92- --color-fg-muted:#8b949e;
93- --color-fg-subtle:#6e7681;
94- --color-accent-fg:#58a6ff;
95- --color-accent-subtle:rgba(56,139,253,0.15);
96- --font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif;
97- --font-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
87+ --github-bg: #0d1117;
88+ --github-bg-secondary: #161b22;
89+ --github-bg-tertiary: #21262d;
90+ --github-border: #30363d;
91+ --github-border-muted: #21262d;
92+ --github-fg: #e6edf3;
93+ --github-fg-muted: #8b949e;
94+ --github-accent: #58a6ff;
95+ --github-accent-soft: #79c0ff;
9896}
9997*{box-sizing:border-box;margin:0;padding:0}
100- body{font-family:var(--font- sans) ;background:var(--color-canvas-default );color:var(--color -fg-default );min-height:100vh;font-size:14px;line-height:1.5}
98+ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial, sans-serif ;background:var(--github-bg );color:var(--github -fg);min-height:100vh;font-size:14px;line-height:1.5}
10199
102- /* Top bar - GitHub blob header */
103- .top-bar{background:var(--color-canvas-subtle );border-bottom:1px solid var(--color -border-muted);padding:12px 16px;min-height:52px; display:flex;align-items:center;gap:4px ;flex-wrap:wrap}
104- .top-bar .crumb{color:var(--color -fg-muted);text-decoration:none}
105- .top-bar .crumb:hover{color:var(--color -accent-fg )}
106- .top-bar .crumb-sep{color:var(--color -fg-muted);user-select:none}
107- .top-bar .file-name{color:var(--color -fg-default );font-weight:600;font-family:var(--font-mono) }
100+ /* Top bar - GitHub style */
101+ .top-bar{background:var(--github-bg-secondary );border-bottom:1px solid var(--github -border-muted);padding:8px 16px;display:flex;align-items:center;gap:8px ;flex-wrap:wrap}
102+ .top-bar .crumb{color:var(--github -fg-muted);text-decoration:none}
103+ .top-bar .crumb:hover{color:var(--github -accent)}
104+ .top-bar .crumb-sep{color:var(--github -fg-muted);user-select:none}
105+ .top-bar .file-name{color:var(--github -fg);font-weight:600}
108106
109107/* Layout */
110- .layout{display:flex;height:calc(100vh - 52px )}
108+ .layout{display:flex;height:calc(100vh - 45px )}
111109
112110/* Sidebar - GitHub file tree */
113- .sidebar{width:260px;min-width:200px;background:var(--color-canvas-subtle );border-right:1px solid var(--color -border-muted);overflow-y:auto;padding:8px 0;flex-shrink:0}
111+ .sidebar{width:260px;min-width:200px;background:var(--github-bg-secondary );border-right:1px solid var(--github -border-muted);overflow-y:auto;padding:8px 0;flex-shrink:0}
114112.sidebar .folder,.sidebar .file{padding:4px 12px 4px 8px;font-size:13px;cursor:pointer;display:flex;align-items:center;gap:6px;border-radius:6px;margin:0 8px}
115- .sidebar .folder:hover,.sidebar .file:hover{background:var(--color-border-muted )}
116- .sidebar .file{color:var(--color -fg-muted)}
117- .sidebar .file.active{color:var(--color -accent-fg );background:var(--color-accent-subtle )}
113+ .sidebar .folder:hover,.sidebar .file:hover{background:var(--github-bg-tertiary )}
114+ .sidebar .file{color:var(--github -fg-muted)}
115+ .sidebar .file.active{color:var(--github -accent);background:var(--github-bg-tertiary )}
118116.sidebar .icon{width:16px;text-align:center;flex-shrink:0}
119117.sidebar .icon svg{vertical-align:middle}
120- .sidebar .folder .icon{color:var(--color -accent-fg )}
118+ .sidebar .folder .icon{color:var(--github -accent-soft )}
121119.sidebar .chevron{width:16px;transition:transform .15s;flex-shrink:0}
122120.sidebar .folder.collapsed .chevron{transform:rotate(-90deg)}
123121.sidebar .children{margin-left:4px}
124122.sidebar .children.hidden{display:none}
125- .sidebar .search-wrap{padding:8px;border-bottom:1px solid var(--color -border-muted)}
126- .sidebar .search-input{width:100%;padding:6px 10px;font-size:12px;border:1px solid var(--color -border-default );border-radius:6px;background:var(--color-canvas-default );color:var(--color -fg-default );outline:none}
127- .sidebar .search-input:focus{border-color:var(--color -accent-fg )}
128- .sidebar .search-input::placeholder{color:var(--color -fg-muted)}
129- .sidebar .tree-actions{display:flex;gap:4px;padding:4px 8px;border-bottom:1px solid var(--color -border-muted)}
130- .sidebar .tree-btn{padding:4px 8px;font-size:11px;border:1px solid var(--color -border-default );border-radius:4px;background:var(--color-border-muted );color:var(--color -fg-muted);cursor:pointer}
131- .sidebar .tree-btn:hover{color:var(--color -fg-default );background:var(--color -border-default )}
123+ .sidebar .search-wrap{padding:8px;border-bottom:1px solid var(--github -border-muted)}
124+ .sidebar .search-input{width:100%;padding:6px 10px;font-size:12px;border:1px solid var(--github -border);border-radius:6px;background:var(--github-bg );color:var(--github -fg);outline:none}
125+ .sidebar .search-input:focus{border-color:var(--github -accent)}
126+ .sidebar .search-input::placeholder{color:var(--github -fg-muted)}
127+ .sidebar .tree-actions{display:flex;gap:4px;padding:4px 8px;border-bottom:1px solid var(--github -border-muted)}
128+ .sidebar .tree-btn{padding:4px 8px;font-size:11px;border:1px solid var(--github -border);border-radius:4px;background:var(--github-bg-tertiary );color:var(--github -fg-muted);cursor:pointer}
129+ .sidebar .tree-btn:hover{color:var(--github -fg);background:var(--github -border)}
132130
133131/* Content - GitHub file view */
134- .content{flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--color-canvas-default )}
135- .content .file-header{background:var(--color-canvas-subtle );border-bottom:1px solid var(--color -border-muted);padding:8px 16px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
136- .content .file-header .file-path{font-weight:600;color:var(--color -fg-default )}
137- .content .file-header .copy-btn{padding:4px 12px;font-size:12px;border:1px solid var(--color -border-default );border-radius:6px;background:var(--color-border-muted );color:var(--color -fg-default );cursor:pointer}
138- .content .file-header .copy-btn:hover{background:var(--color -border-default );border-color:var(--color -fg-muted)}
132+ .content{flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--github-bg )}
133+ .content .file-header{background:var(--github-bg-secondary );border-bottom:1px solid var(--github -border-muted);padding:8px 16px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
134+ .content .file-header .file-path{font-weight:600;color:var(--github -fg)}
135+ .content .file-header .copy-btn{padding:4px 12px;font-size:12px;border:1px solid var(--github -border);border-radius:6px;background:var(--github-bg-tertiary );color:var(--github -fg);cursor:pointer}
136+ .content .file-header .copy-btn:hover{background:var(--github -border);border-color:var(--github -fg-muted)}
139137.content .code-wrap{flex:1;overflow:auto}
140138.content .code-table{width:100%;border-collapse:collapse;font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;font-size:12px;line-height:1.45}
141139.content .code-table td{vertical-align:top;padding:0}
142- .content .line-nums{padding:16px 0;text-align:right;user-select:none;color:var(--color -fg-muted);background:var(--color-canvas-subtle );border-right:1px solid var(--color -border-muted);width:1%;min-width:50px}
140+ .content .line-nums{padding:16px 0;text-align:right;user-select:none;color:var(--github -fg-muted);background:var(--github-bg-secondary );border-right:1px solid var(--github -border-muted);width:1%;min-width:50px}
143141.content .line-nums span{display:block;padding:0 16px}
144142.content .line-code{padding:16px 0;padding-left:16px}
145143.content .line-code .line{display:block;min-height:1.45em;white-space:pre}
146- .content .empty{color:var(--color -fg-muted);padding:24px;font-style:italic}
144+ .content .empty{color:var(--github -fg-muted);padding:24px;font-style:italic}
147145.content .md-preview{padding:24px;line-height:1.6}
148146.content .md-preview h1,.content .md-preview h2,.content .md-preview h3{margin-top:1em;margin-bottom:0.5em;font-weight:600}
149- .content .md-preview code{background:var(--color-border-muted );padding:2px 6px;border-radius:4px;font-size:0.9em}
150- .content .md-preview pre{background:var(--color-border-muted );padding:16px;overflow-x:auto;border-radius:6px;font-size:13px}
147+ .content .md-preview code{background:var(--github-bg-tertiary );padding:2px 6px;border-radius:4px;font-size:0.9em}
148+ .content .md-preview pre{background:var(--github-bg-tertiary );padding:16px;overflow-x:auto;border-radius:6px;font-size:13px}
151149.content .btn-group{display:flex;gap:8px;margin-bottom:16px}
152- .content .btn{padding:6px 12px;border:1px solid var(--color -border-default );border-radius:6px;background:var(--color-border-muted );color:var(--color -fg-default );cursor:pointer;font-size:12px}
153- .content .btn.active{background:var(--color -accent-fg );border-color:var(--color -accent-fg );color:#fff}
154- .content .btn:hover{background:var(--color -border-default )}
150+ .content .btn{padding:6px 12px;border:1px solid var(--github -border);border-radius:6px;background:var(--github-bg-tertiary );color:var(--github -fg);cursor:pointer;font-size:12px}
151+ .content .btn.active{background:var(--github -accent);border-color:var(--github -accent);color:#fff}
152+ .content .btn:hover{background:var(--github -border)}
155153
156154@media(max-width:767px){
157155 .layout{flex-direction:column;height:auto;min-height:100vh}
158- .sidebar{width:100%;max-height:200px;border-right:none;border-bottom:1px solid var(--color -border-muted)}
156+ .sidebar{width:100%;max-height:200px;border-right:none;border-bottom:1px solid var(--github -border-muted)}
159157 .content .code-wrap{min-height:400px}
160158}
161159</style>
@@ -204,26 +202,14 @@ function selectFile(path){
204202 document.querySelectorAll(".sidebar .file.active").forEach(function(n){n.classList.remove("active")});
205203 document.querySelectorAll(".sidebar .folder.active").forEach(function(n){n.classList.remove("active")});
206204 var node=document.querySelector('.sidebar .file[data-path="'+path.replace(/"/g,'\\"')+'"]');
207- if(node){
208- node.classList.add("active");
209- var parent=node.parentElement;
210- while(parent&&parent.id!=="tree"){
211- var folder=parent.previousElementSibling;
212- if(folder&&folder.classList.contains("folder")){
213- folder.classList.remove("collapsed");
214- parent.classList.remove("hidden");
215- }
216- parent=parent.parentElement;
217- }
218- node.scrollIntoView({block:"nearest"});
219- }
205+ if(node){node.classList.add("active");node.scrollIntoView({block:"nearest"})}
220206 document.getElementById("breadcrumbFile").textContent=path;
221207 document.getElementById("fileHeader").style.display="block";
222208 document.getElementById("headerPath").textContent=path;
223209 var content=files[path]||"";
224- var isMd =path.toLowerCase().endsWith(" .md") ;
210+ var isReadme =path.toLowerCase()==="readme .md";
225211 var viewer=document.getElementById("viewer");
226- if(isMd ){
212+ if(isReadme ){
227213 var btnHtml='<div class="btn-group"><button class="btn'+(viewMode==='preview'?' active':'')+'" data-mode="preview">Preview</button><button class="btn'+(viewMode==='code'?' active':'')+'" data-mode="code">Code</button></div>';
228214 if(viewMode==="preview"&&typeof marked!=="undefined"){
229215 viewer.innerHTML=btnHtml+'<div class="md-preview">'+marked.parse(content||"")+'</div>';
0 commit comments