mirror of
https://github.com/ershisan99/md-preview-desktop.git
synced 2025-12-18 12:33:43 +00:00
chore: replace local components with mdx-components lib
feat: directory selection working feat: theme toggle working
This commit is contained in:
@@ -3,11 +3,14 @@ import path from 'node:path'
|
||||
import { join } from 'path'
|
||||
|
||||
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
||||
import { bundleMdx } from '@it-incubator/md-bundler'
|
||||
import { bundleMdx, generateToc } from '@it-incubator/md-bundler'
|
||||
import { BrowserWindow, app, ipcMain, shell } from 'electron'
|
||||
import Store from 'electron-store'
|
||||
|
||||
import icon from '../../resources/icon.png?asset'
|
||||
|
||||
const chokidar = require('chokidar')
|
||||
const store = new Store()
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
|
||||
@@ -35,6 +38,11 @@ function createWindow(): void {
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
mainWindow.webContents.on('will-navigate', (event, url) => {
|
||||
event.preventDefault()
|
||||
shell.openExternal(url)
|
||||
})
|
||||
|
||||
// HMR for renderer base on electron-vite cli.
|
||||
// Load the remote URL for development or the local html file for production.
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
@@ -79,8 +87,10 @@ app.on('window-all-closed', () => {
|
||||
})
|
||||
|
||||
let watcher: any = null
|
||||
let currentContent: any = null
|
||||
|
||||
function setupWatcher(filePath: string) {
|
||||
store.set('lastFilePath', filePath)
|
||||
// Close the existing watcher if it exists
|
||||
if (watcher) {
|
||||
watcher.close()
|
||||
@@ -101,13 +111,18 @@ function setupWatcher(filePath: string) {
|
||||
// Send file content to renderer
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
const bundled = await bundleMdx(content)
|
||||
const toc = await generateToc(content, {})
|
||||
const newContent = { ...bundled, fileName: path, toc }
|
||||
|
||||
mainWindow.webContents.send('file-changed', { ...bundled, fileName: path })
|
||||
currentContent = newContent
|
||||
|
||||
mainWindow.webContents.send('current-content', newContent)
|
||||
}
|
||||
})
|
||||
await shell.openPath(path)
|
||||
// await shell.openPath(path)
|
||||
}
|
||||
|
||||
bundleAndSend(filePath)
|
||||
// Add your event listeners
|
||||
watcher
|
||||
.on('add', async (path: string) => {
|
||||
@@ -119,15 +134,147 @@ function setupWatcher(filePath: string) {
|
||||
.on('unlink', (path: string) => console.warn(`File ${path} has been removed`))
|
||||
}
|
||||
|
||||
function prepareAndSendDir(dir: string) {
|
||||
const files = fs.readdirSync(dir)
|
||||
const dirName = path.basename(dir)
|
||||
const data = [
|
||||
{
|
||||
children: getFilesRecursive(
|
||||
dir,
|
||||
['.md', '.mdx'],
|
||||
['node_modules', 'README.md'],
|
||||
true,
|
||||
dir + '/'
|
||||
),
|
||||
name: dirName,
|
||||
path: dir,
|
||||
type: FsEntryType.Directory,
|
||||
},
|
||||
]
|
||||
|
||||
// Send the list of files to the renderer process
|
||||
mainWindow?.webContents.send('directory-contents', { data, dir, dirName, files })
|
||||
store.set('lastOpenDir', dir)
|
||||
}
|
||||
|
||||
ipcMain.on('dropped-file', (event, arg) => {
|
||||
console.warn('Dropped File(s):', arg)
|
||||
event.returnValue = `Received ${arg.length} paths.` // Synchronous reply
|
||||
|
||||
// Assuming the user only dropped one file, update the watcher to watch that file.
|
||||
event.returnValue = `Received ${arg.length} paths.`
|
||||
if (!mainWindow) {
|
||||
throw new Error('mainWindow is not defined')
|
||||
}
|
||||
if (arg.length > 0) {
|
||||
setupWatcher(arg[0])
|
||||
const pathToCheck = arg[0]
|
||||
|
||||
if (fs.statSync(pathToCheck).isDirectory()) {
|
||||
// If it's a directory, get the list of files
|
||||
prepareAndSendDir(pathToCheck)
|
||||
} else {
|
||||
setupWatcher(pathToCheck)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('get-current-content', event => {
|
||||
event.reply('current-content', currentContent)
|
||||
})
|
||||
ipcMain.on('get-current-dir', () => {
|
||||
const lastOpenDir = store.get('lastOpenDir') as string | undefined
|
||||
|
||||
if (!lastOpenDir) {
|
||||
return
|
||||
}
|
||||
prepareAndSendDir(lastOpenDir)
|
||||
})
|
||||
ipcMain.on('open-file', (_event, filePath) => {
|
||||
setupWatcher(filePath)
|
||||
})
|
||||
const lastFilePath = store.get('lastFilePath') as string | undefined
|
||||
const lastOpenDir = store.get('lastOpenDir') as string | undefined
|
||||
|
||||
if (lastOpenDir) {
|
||||
prepareAndSendDir(lastOpenDir)
|
||||
}
|
||||
|
||||
// Initially setup watcher for 'hello.md'
|
||||
setupWatcher(path.resolve(__dirname, '../../../hello.md'))
|
||||
setupWatcher(lastFilePath ?? path.resolve(__dirname, '../../../hello.md'))
|
||||
|
||||
function getFilesRecursive(
|
||||
directory: string,
|
||||
allowedExtensions: string[] = [],
|
||||
ignoredPaths: string[] = [],
|
||||
includeParent = true,
|
||||
prefix = ''
|
||||
): FileOrDirectory[] {
|
||||
const fileList: FileOrDirectory[] = []
|
||||
|
||||
const filesAndDirs = fs.readdirSync(directory)
|
||||
|
||||
for (const fileOrDir of filesAndDirs) {
|
||||
const absolutePath = path.join(directory, fileOrDir)
|
||||
const relativePath = path.join(prefix, fileOrDir)
|
||||
|
||||
// Skip dotfiles and dot directories
|
||||
if (fileOrDir.startsWith('.')) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip ignored files and directories
|
||||
if (ignoredPaths.some(ignoredPath => absolutePath.includes(ignoredPath))) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (fs.statSync(absolutePath).isDirectory()) {
|
||||
const nestedFiles = getFilesRecursive(
|
||||
absolutePath,
|
||||
allowedExtensions,
|
||||
ignoredPaths,
|
||||
includeParent,
|
||||
relativePath + '/'
|
||||
)
|
||||
|
||||
fileList.push({
|
||||
children: nestedFiles,
|
||||
name: fileOrDir,
|
||||
path: relativePath,
|
||||
type: FsEntryType.Directory,
|
||||
})
|
||||
} else {
|
||||
const extension = path.extname(fileOrDir).toLowerCase()
|
||||
|
||||
// Check the file has an allowed extension
|
||||
if (
|
||||
allowedExtensions.length === 0 ||
|
||||
allowedExtensions.map(e => e.toLowerCase()).includes(extension)
|
||||
) {
|
||||
fileList.push({
|
||||
name: fileOrDir,
|
||||
path: relativePath,
|
||||
type: FsEntryType.File,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileList
|
||||
}
|
||||
|
||||
enum FsEntryType {
|
||||
Directory = 'directory',
|
||||
File = 'file',
|
||||
}
|
||||
|
||||
type File = {
|
||||
name: string
|
||||
path: string
|
||||
type: FsEntryType.File
|
||||
}
|
||||
|
||||
type Directory = {
|
||||
children: Array<FileOrDirectory>
|
||||
name: string
|
||||
path: string
|
||||
type: FsEntryType.Directory
|
||||
}
|
||||
|
||||
type FileOrDirectory = Directory | File
|
||||
|
||||
Reference in New Issue
Block a user