@@ -28,6 +28,10 @@ export function generateDocsHtml(routes: RouteInfo[], options: DocsGeneratorOpti
2828 <span id="configButtonText">Configure Base URL</span>
2929 </button>
3030
31+ <button class="scroll-top-btn" id="scrollTopBtn" onclick="window.scrollTo({top:0,behavior:'smooth'})" aria-label="Scroll to top">
32+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 15l-6-6-6 6"/></svg>
33+ </button>
34+
3135 <div class="config-modal" id="configModal">
3236 <div class="config-modal-content">
3337 <div class="config-modal-header">
@@ -60,44 +64,65 @@ export function generateDocsHtml(routes: RouteInfo[], options: DocsGeneratorOpti
6064 </div>
6165 </div>
6266
63- <button class="mobile-menu-toggle" id="mobileMenuToggle" aria-label="Toggle navigation">
64- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
65- <line x1="3" y1="12" x2="21" y2="12"></line>
66- <line x1="3" y1="6" x2="21" y2="6"></line>
67- <line x1="3" y1="18" x2="21" y2="18"></line>
68- </svg>
69- </button>
67+ <div class="mobile-topbar">
68+ <button class="mobile-topbar-toggle" id="mobileMenuToggle" aria-label="Toggle navigation">
69+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
70+ <line x1="3" y1="12" x2="21" y2="12"></line>
71+ <line x1="3" y1="6" x2="21" y2="6"></line>
72+ <line x1="3" y1="18" x2="21" y2="18"></line>
73+ </svg>
74+ </button>
75+ <span class="mobile-topbar-title">${ title } </span>
76+ <button class="mobile-topbar-config" id="mobileConfigButton" onclick="openConfigModal()" aria-label="Configure Base URL">
77+ <span class="iconify" data-icon="mdi:cog" style="width: 18px; height: 18px;"></span>
78+ </button>
79+ </div>
7080 <div class="sidebar-overlay" id="sidebarOverlay"></div>
7181 <div class="container">
7282 <nav class="sidebar" id="sidebar">
7383 <div class="sidebar-header">
7484 <img src="${ getLogo ( ) } " alt="Logo" class="sidebar-logo" />
85+ <span class="sidebar-brand">${ title } </span>
86+ </div>
87+ <div class="sidebar-nav">
88+ <div class="sidebar-title">Navigation</div>
89+ ${ Object . entries ( groupedRoutes )
90+ . map (
91+ ( [ group , groupRoutes ] ) => `
92+ <div class="sidebar-group">
93+ <div class="sidebar-group-title">${ formatGroupName ( group ) } </div>
94+ ${ groupRoutes
95+ . map (
96+ route => `
97+ <a href="#${ route . path . replace ( / \. / g, '-' ) } " class="sidebar-link">
98+ <span class="link-type-dot dot-${ route . type } "></span>
99+ ${ route . meta ?. name ?? route . path . split ( '.' ) . pop ( ) }
100+ </a>
101+ `
102+ )
103+ . join ( '' ) }
104+ </div>
105+ `
106+ )
107+ . join ( '' ) }
108+ </div>
109+ <div class="sidebar-footer">
110+ <a href="https://github.com/liorcodev/trpc-docs-generator" target="_blank" rel="noopener noreferrer" class="sidebar-footer-link" title="View on GitHub">
111+ <span class="iconify" data-icon="mdi:github" style="width: 20px; height: 20px;"></span>
112+ <span>View on GitHub</span>
113+ </a>
75114 </div>
76- <div class="sidebar-title"><span class="iconify" data-icon="mdi:book-open-page-variant" style="vertical-align: -0.125em; margin-right: 0.5rem;"></span>Navigation</div>
77- ${ Object . entries ( groupedRoutes )
78- . map (
79- ( [ group , groupRoutes ] ) => `
80- <div class="sidebar-group">
81- <div class="sidebar-group-title">${ formatGroupName ( group ) } </div>
82- ${ groupRoutes
83- . map (
84- route => `
85- <a href="#${ route . path . replace ( / \. / g, '-' ) } " class="sidebar-link">
86- ${ route . meta ?. name ?? route . path . split ( '.' ) . pop ( ) }
87- </a>
88- `
89- )
90- . join ( '' ) }
91- </div>
92- `
93- )
94- . join ( '' ) }
95115 </nav>
96116
97117 <main class="main-content">
98118 <header>
99- <h1>${ title } </h1>
100- <p><span class="iconify" data-icon="mdi:sparkles" style="vertical-align: -0.125em;"></span> tRPC API Documentation <span class="iconify" data-icon="mdi:sparkles" style="vertical-align: -0.125em;"></span></p>
119+ <div class="header-top">
120+ <h1>${ title } </h1>
121+ <p class="header-subtitle">
122+ <span class="header-pill">tRPC</span>
123+ API Documentation
124+ </p>
125+ </div>
101126 <div class="stats">
102127 <div class="stat">
103128 <div class="stat-value">${ routes . length } </div>
@@ -122,65 +147,66 @@ export function generateDocsHtml(routes: RouteInfo[], options: DocsGeneratorOpti
122147 </div>
123148 </div>
124149 </div>
150+ </header>
125151
126- <div class="search-filter-bar">
127- <div class="search-box">
128- <span class="iconify search-icon" data-icon="mdi:magnify"></span>
129- <input
130- type="search"
131- id="searchInput"
132- class="search-input"
133- placeholder="Search endpoints, descriptions..."
134- />
135- </div>
136-
137- <div class="filter-group">
138- <select id="typeFilter" class="filter-select">
139- <option value="all">All Types</option>
140- <option value="query">Queries</option>
141- <option value="mutation">Mutations</option>
142- </select>
143-
144- <select id="authFilter" class="filter-select">
145- <option value="all">All Endpoints</option>
146- <option value="public">Public</option>
147- <option value="protected">Protected</option>
148- </select>
149-
150- <select id="tagFilter" class="filter-select">
151- <option value="all">All Tags</option>
152- ${ Array . from (
153- new Set ( routes . flatMap ( r => ( r . meta ?. docs as RouteMeta [ 'docs' ] ) ?. tags || [ ] ) )
154- )
155- . sort ( )
156- . map ( tag => `<option value="${ escapeHtml ( tag ) } ">${ escapeHtml ( tag ) } </option>` )
157- . join ( '' ) }
158- </select>
159- </div>
152+ <div class="search-filter-bar">
153+ <div class="search-box">
154+ <span class="iconify search-icon" data-icon="mdi:magnify"></span>
155+ <input
156+ type="search"
157+ id="searchInput"
158+ class="search-input"
159+ placeholder="Search endpoints, descriptions..."
160+ />
161+ </div>
162+
163+ <div class="filter-group">
164+ <select id="typeFilter" class="filter-select">
165+ <option value="all">All Types</option>
166+ <option value="query">Queries</option>
167+ <option value="mutation">Mutations</option>
168+ </select>
160169
161- <div class="filter-controls">
162- <button class="clear-filters-btn" id="clearFiltersBtn" onclick="clearFilters()">
163- <span class="iconify" data-icon="mdi:filter-remove" style="width: 14px; height: 14px;"></span>
164- Clear Filters
165- </button>
166- </div>
170+ <select id="authFilter" class="filter-select">
171+ <option value="all">All Endpoints</option>
172+ <option value="public">Public</option>
173+ <option value="protected">Protected</option>
174+ </select>
167175
176+ <select id="tagFilter" class="filter-select">
177+ <option value="all">All Tags</option>
178+ ${ Array . from (
179+ new Set ( routes . flatMap ( r => ( r . meta ?. docs as RouteMeta [ 'docs' ] ) ?. tags || [ ] ) )
180+ )
181+ . sort ( )
182+ . map ( tag => `<option value="${ escapeHtml ( tag ) } ">${ escapeHtml ( tag ) } </option>` )
183+ . join ( '' ) }
184+ </select>
185+ </div>
186+
187+ <div class="filter-controls">
168188 <div class="results-count" id="resultsCount">
169189 Showing ${ routes . length } of ${ routes . length } endpoints
170190 </div>
191+ <button class="clear-filters-btn" id="clearFiltersBtn" onclick="clearFilters()">
192+ <span class="iconify" data-icon="mdi:filter-remove" style="width: 14px; height: 14px;"></span>
193+ Clear Filters
194+ </button>
171195 </div>
172- </header >
196+ </div >
173197
174- ${ Object . entries ( groupedRoutes )
175- . map (
176- ( [ group , groupRoutes ] ) => `
177- <section class="route-group" id="group-${ group } ">
178- <h2 class="route-group-header">${ formatGroupName ( group ) } </h2>
179- ${ groupRoutes . map ( route => generateRouteCard ( route ) ) . join ( '' ) }
180- </section>
181- `
182- )
183- . join ( '' ) }
198+ <div class="main-body">
199+ ${ Object . entries ( groupedRoutes )
200+ . map (
201+ ( [ group , groupRoutes ] ) => `
202+ <section class="route-group" id="group-${ group } ">
203+ <h2 class="route-group-header">${ formatGroupName ( group ) } </h2>
204+ ${ groupRoutes . map ( route => generateRouteCard ( route ) ) . join ( '' ) }
205+ </section>
206+ `
207+ )
208+ . join ( '' ) }
209+ </div>
184210 </main>
185211 </div>
186212
@@ -240,7 +266,7 @@ function generateRouteCard(route: RouteInfo): string {
240266 . toLowerCase ( ) ;
241267
242268 return `
243- <div class="route-card"
269+ <div class="route-card type- ${ route . type } "
244270 id="${ routeId } "
245271 data-type="${ route . type } "
246272 data-auth="${ docs ?. auth ? 'true' : 'false' } "
@@ -293,8 +319,10 @@ function generateRouteCard(route: RouteInfo): string {
293319 ? `
294320 <div class="route-section">
295321 <div class="route-section-title"><span class="iconify" data-icon="mdi:import" style="vertical-align: -0.125em; margin-right: 0.5rem;"></span>Input Schema</div>
296- <div class="schema-block">
297- <pre>${ escapeHtml ( route . inputTypeScript ) } </pre>
322+ <div class="schema-block"> <div class="schema-block-header">
323+ <span class="schema-lang-label">TypeScript</span>
324+ <button class="copy-btn" onclick="copySchema(this)">Copy</button>
325+ </div> <pre>${ escapeHtml ( route . inputTypeScript ) } </pre>
298326 </div>
299327 </div>
300328 `
@@ -306,8 +334,10 @@ function generateRouteCard(route: RouteInfo): string {
306334 ? `
307335 <div class="route-section">
308336 <div class="route-section-title"><span class="iconify" data-icon="mdi:export" style="vertical-align: -0.125em; margin-right: 0.5rem;"></span>Output Schema</div>
309- <div class="schema-block">
310- <pre>${ escapeHtml ( route . outputTypeScript ) } </pre>
337+ <div class="schema-block"> <div class="schema-block-header">
338+ <span class="schema-lang-label">TypeScript</span>
339+ <button class="copy-btn" onclick="copySchema(this)">Copy</button>
340+ </div> <pre>${ escapeHtml ( route . outputTypeScript ) } </pre>
311341 </div>
312342 </div>
313343 `
@@ -321,6 +351,7 @@ function generateRouteCard(route: RouteInfo): string {
321351 Test Endpoint
322352 </div>
323353 </div>
354+ <div class="test-panel-body">
324355
325356 <div class="headers-manager">
326357 <label class="test-section-label">Request Headers</label>
@@ -392,6 +423,7 @@ function generateRouteCard(route: RouteInfo): string {
392423 </button>
393424
394425 <div id="response-${ routeId } " class="response-container" style="display: none;"></div>
426+ </div>
395427 </div>
396428 </div>
397429 </div>
0 commit comments