Skip to content

Commit e789328

Browse files
Pre-update backup: Saving current state of doc engine
1 parent 616f89e commit e789328

16 files changed

Lines changed: 3425 additions & 43 deletions

File tree

package-lock.json

Lines changed: 1577 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,31 @@
1818
"@electron-toolkit/preload": "^3.0.2",
1919
"@electron-toolkit/utils": "^4.0.0",
2020
"@tanstack/react-query": "^5.62.0",
21+
"@tiptap/extension-link": "^3.22.2",
22+
"@tiptap/extension-placeholder": "^3.22.2",
23+
"@tiptap/extension-table": "^3.22.2",
24+
"@tiptap/extension-table-cell": "^3.22.2",
25+
"@tiptap/extension-table-header": "^3.22.2",
26+
"@tiptap/extension-table-row": "^3.22.2",
27+
"@tiptap/react": "^3.22.2",
28+
"@tiptap/starter-kit": "^3.22.2",
29+
"@types/markdown-it": "^14.1.2",
2130
"dexie": "^4.0.10",
2231
"dexie-react-hooks": "^1.1.7",
2332
"electron-store": "^10.0.0",
2433
"electron-updater": "^6.8.3",
2534
"html2canvas": "^1.4.1",
2635
"jspdf": "^4.2.0",
2736
"latex.js": "^0.12.6",
37+
"markdown-it": "^14.1.1",
2838
"mysql2": "^3.18.1",
2939
"pg": "^8.19.0",
40+
"puppeteer": "^24.40.0",
3041
"react": "^18.3.1",
3142
"react-dom": "^18.3.1",
3243
"react-markdown": "^10.1.0",
3344
"remark-gfm": "^4.0.1",
45+
"tiptap-markdown": "^0.9.0",
3446
"uuid": "^11.0.3",
3547
"zustand": "^5.0.2"
3648
},

src/main/index.ts

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import mysql from 'mysql2/promise'
77
import pg from 'pg'
88
import { spawn } from 'child_process'
99
import { autoUpdater } from 'electron-updater'
10+
import MarkdownIt from 'markdown-it'
11+
import puppeteer from 'puppeteer'
1012

1113
let mainWindow: BrowserWindow | null = null
1214

@@ -32,6 +34,11 @@ function createWindow(): void {
3234
mainWindow?.show()
3335
})
3436

37+
mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => {
38+
console.error(`[Main Window] Failed to load URL: ${validatedURL}`)
39+
console.error(`Error Code: ${errorCode} (${errorDescription})`)
40+
})
41+
3542
mainWindow.webContents.setWindowOpenHandler((details) => {
3643
shell.openExternal(details.url)
3744
return { action: 'deny' }
@@ -138,6 +145,275 @@ ipcMain.handle("export-pdf", async (_, html, fileName) => {
138145
win.close()
139146
})
140147

148+
// Helper for PDF generation
149+
async function generatePdfBuffer(markdownContent: string) {
150+
const md = new MarkdownIt({
151+
html: true,
152+
linkify: true,
153+
typographer: true
154+
})
155+
156+
const htmlContent = md.render(markdownContent)
157+
const fullHtml = `
158+
<!DOCTYPE html>
159+
<html>
160+
<head>
161+
<meta charset="UTF-8">
162+
<style>
163+
@page {
164+
size: A4;
165+
margin: 20mm;
166+
}
167+
body {
168+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
169+
line-height: 1.5;
170+
color: #111827;
171+
background-color: #ffffff;
172+
margin: 0;
173+
padding: 0;
174+
}
175+
.container {
176+
width: 100%;
177+
}
178+
h1 {
179+
font-size: 2.5rem;
180+
font-weight: 800;
181+
color: #111827;
182+
margin-top: 0;
183+
margin-bottom: 24px;
184+
border-bottom: 2px solid #E5E7EB;
185+
padding-bottom: 12px;
186+
}
187+
h2 {
188+
font-size: 1.875rem;
189+
font-weight: 700;
190+
color: #1F2937;
191+
margin-top: 48px;
192+
margin-bottom: 16px;
193+
background: #F3F4F6;
194+
padding: 12px 16px;
195+
border-radius: 8px;
196+
}
197+
h3 {
198+
font-size: 1.5rem;
199+
font-weight: 600;
200+
color: #374151;
201+
margin-top: 32px;
202+
margin-bottom: 12px;
203+
border-left: 4px solid #3B82F6;
204+
padding-left: 16px;
205+
}
206+
h4 {
207+
font-size: 1.125rem;
208+
font-weight: 600;
209+
color: #4B5563;
210+
margin-top: 24px;
211+
margin-bottom: 8px;
212+
text-transform: uppercase;
213+
letter-spacing: 0.025em;
214+
}
215+
h5 {
216+
font-size: 1rem;
217+
font-weight: 600;
218+
color: #6B7280;
219+
margin-top: 16px;
220+
margin-bottom: 8px;
221+
}
222+
p {
223+
margin: 12px 0;
224+
}
225+
.method {
226+
display: inline-block;
227+
padding: 2px 8px;
228+
border-radius: 4px;
229+
font-family: monospace;
230+
font-weight: 700;
231+
font-size: 0.875rem;
232+
margin-right: 8px;
233+
color: white;
234+
}
235+
.method-GET { background: #10B981; }
236+
.method-POST { background: #3B82F6; }
237+
.method-PUT { background: #F59E0B; }
238+
.method-DELETE { background: #EF4444; }
239+
.method-PATCH { background: #8B5CF6; }
240+
241+
/* Table of Contents Styling */
242+
.toc-link {
243+
color: #2563EB;
244+
text-decoration: none;
245+
}
246+
.toc-list {
247+
list-style: none;
248+
padding-left: 0;
249+
}
250+
.toc-item {
251+
margin-bottom: 8px;
252+
}
253+
254+
/* Page Break support */
255+
.page-break {
256+
page-break-after: always;
257+
}
258+
259+
/* Custom Header IDs for TOC anchoring */
260+
h2[id], h3[id] {
261+
scroll-margin-top: 20px;
262+
}
263+
264+
.status-code {
265+
display: inline-block;
266+
padding: 2px 6px;
267+
border-radius: 4px;
268+
font-weight: 700;
269+
font-size: 0.8125rem;
270+
margin-left: 8px;
271+
}
272+
.status-2xx { background: #DCFCE7; color: #166534; }
273+
.status-3xx { background: #FEF3C7; color: #92400E; }
274+
.status-4xx { background: #FEE2E2; color: #991B1B; }
275+
.status-5xx { background: #FEF2F2; color: #991B1B; }
276+
277+
.endpoint-header {
278+
display: flex;
279+
align-items: center;
280+
gap: 12px;
281+
margin-bottom: 24px;
282+
}
283+
284+
pre {
285+
background-color: #0F172A;
286+
color: #CBD5E1;
287+
padding: 18px;
288+
border-radius: 12px;
289+
font-family: 'ui-monospace', SFMono-Regular, Menlo, Monaco, Consolas, monospace;
290+
font-size: 0.875rem;
291+
line-height: 1.6;
292+
overflow-x: auto;
293+
margin: 16px 0;
294+
border: 1px solid #1E293B;
295+
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.05);
296+
}
297+
code {
298+
font-family: 'ui-monospace', SFMono-Regular, Menlo, Monaco, Consolas, monospace;
299+
background-color: #F1F5F9;
300+
color: #334155;
301+
padding: 0.2rem 0.4rem;
302+
border-radius: 4px;
303+
font-size: 0.8125rem;
304+
}
305+
pre code {
306+
background-color: transparent;
307+
color: inherit;
308+
padding: 0;
309+
}
310+
table {
311+
width: 100%;
312+
border-collapse: collapse;
313+
margin: 24px 0;
314+
font-size: 0.875rem;
315+
}
316+
table th, table td {
317+
padding: 12px 16px;
318+
border: 1px solid #E2E8F0;
319+
text-align: left;
320+
vertical-align: top;
321+
}
322+
table th {
323+
background-color: #F8FAFC;
324+
font-weight: 700;
325+
color: #475569;
326+
text-transform: uppercase;
327+
font-size: 0.75rem;
328+
letter-spacing: 0.05em;
329+
}
330+
blockquote {
331+
padding: 12px 24px;
332+
color: #64748B;
333+
border-left: 6px solid #E2E8F0;
334+
background: #F8FAFC;
335+
margin: 24px 0;
336+
font-style: italic;
337+
border-radius: 0 8px 8px 0;
338+
}
339+
.hr {
340+
border: none;
341+
border-top: 2px solid #F1F5F9;
342+
margin: 60px 0;
343+
}
344+
img {
345+
max-width: 100%;
346+
border-radius: 8px;
347+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
348+
}
349+
</style>
350+
</head>
351+
<body>
352+
<div class="container">
353+
${htmlContent}
354+
</div>
355+
</body>
356+
</html>
357+
`
358+
359+
const browser = await puppeteer.launch({
360+
headless: true,
361+
args: ['--no-sandbox', '--disable-setuid-sandbox']
362+
})
363+
const page = await browser.newPage()
364+
await page.setContent(fullHtml, { waitUntil: 'networkidle0' })
365+
366+
const buffer = await page.pdf({
367+
format: 'A4',
368+
margin: {
369+
top: '25mm',
370+
right: '25mm',
371+
bottom: '25mm',
372+
left: '25mm'
373+
},
374+
displayHeaderFooter: true,
375+
headerTemplate: '<span></span>',
376+
footerTemplate: `
377+
<div style="font-size: 10px; color: #aaa; width: 100%; text-align: center; font-family: 'Segoe UI', Arial, sans-serif;">
378+
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
379+
</div>`,
380+
printBackground: true
381+
})
382+
383+
await browser.close()
384+
return buffer
385+
}
386+
387+
ipcMain.handle('preview-doc-pdf', async (_event, markdownContent: string) => {
388+
try {
389+
const buffer = await generatePdfBuffer(markdownContent)
390+
return { success: true, data: buffer }
391+
} catch (error: any) {
392+
console.error('Error during PDF preview generation:', error)
393+
return { success: false, error: error.message }
394+
}
395+
})
396+
397+
ipcMain.handle('generate-doc-pdf', async (_event, markdownContent: string, fileName: string) => {
398+
try {
399+
const { filePath } = await dialog.showSaveDialog({
400+
defaultPath: fileName,
401+
filters: [{ name: 'PDF Files', extensions: ['pdf'] }]
402+
})
403+
404+
if (!filePath) return { success: false, error: 'Cancelled' }
405+
406+
const buffer = await generatePdfBuffer(markdownContent)
407+
fs.writeFileSync(filePath, buffer)
408+
409+
return { success: true }
410+
} catch (error: any) {
411+
412+
console.error('Error during PDF conversion:', error)
413+
return { success: false, error: error.message }
414+
}
415+
})
416+
141417
// ─── HTTP Request Handler (Postman-like API testing) ─────────────
142418
ipcMain.handle('send-http-request', async (_event, opts: {
143419
url: string

src/main/types/pg.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module 'pg';

src/preload/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export interface ElectronAPI {
5656
platform: string
5757
// Document Generation
5858
exportPdf: (html: string, fileName: string) => Promise<void>
59+
generateDocPdf: (markdown: string, fileName: string) => Promise<{ success: boolean; error?: string }>
60+
previewDocPdf: (markdown: string) => Promise<{ success: boolean; data?: Uint8Array; error?: string }>
5961
}
6062

6163
const electronAPI: ElectronAPI = {
@@ -100,7 +102,9 @@ const electronAPI: ElectronAPI = {
100102
return () => ipcRenderer.removeListener('update-progress', subscription)
101103
},
102104
platform: process.platform,
103-
exportPdf: (html, fileName) => ipcRenderer.invoke('export-pdf', html, fileName)
105+
exportPdf: (html, fileName) => ipcRenderer.invoke('export-pdf', html, fileName),
106+
generateDocPdf: (markdown, fileName) => ipcRenderer.invoke('generate-doc-pdf', markdown, fileName),
107+
previewDocPdf: (markdown) => ipcRenderer.invoke('preview-doc-pdf', markdown)
104108
}
105109

106110
contextBridge.exposeInMainWorld('electronAPI', electronAPI)

src/renderer/src/App.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ import { DeployProxyDialog } from './components/DeployProxyDialog'
1616
import { GeneralSettingsDialog } from './components/GeneralSettingsDialog'
1717
import { EnvironmentsDialog } from './components/EnvironmentsDialog'
1818
import { UpdaterNotifier } from './components/UpdaterNotifier'
19+
import { ApiDocumentationPage } from '@/components/ApiDocumentationPage'
1920

2021
export function App() {
2122
const {
2223
currentApiId, currentProjectId,
2324
isOnline, setIsOnline,
2425
showCreateProject, showCreateFolder, showCreateApi, showTeamConnect,
25-
showDatabaseSettings, showRbacSettings, showDeploySettings, showGeneralSettings
26+
showDatabaseSettings, showRbacSettings, showDeploySettings, showGeneralSettings,
27+
showApiDocumentation
2628
} = useAppStore()
2729
const { data: projects } = useProjects()
2830

@@ -77,7 +79,9 @@ export function App() {
7779

7880
{/* Editor area */}
7981
<main style={{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column', position: 'relative' }}>
80-
{currentApiId ? (
82+
{showApiDocumentation ? (
83+
<ApiDocumentationPage />
84+
) : currentApiId ? (
8185
<RequestEditor apiId={currentApiId} />
8286
) : (
8387
<EmptyState hasProject={hasProject && !!currentProjectId} />

0 commit comments

Comments
 (0)