mirror of
https://github.com/ershisan99/md-preview-desktop.git
synced 2025-12-16 20:59:24 +00:00
feat: add d'n'd listener to open files on drop
chore: apply eslint-config
This commit is contained in:
@@ -1,9 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: [
|
extends: '@it-incubator/eslint-config',
|
||||||
'eslint:recommended',
|
}
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:react/jsx-runtime',
|
|
||||||
'@electron-toolkit/eslint-config-ts/recommended',
|
|
||||||
'@electron-toolkit/eslint-config-prettier'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
15
.idea/git_toolbox_prj.xml
generated
Normal file
15
.idea/git_toolbox_prj.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GitToolBoxProjectSettings">
|
||||||
|
<option name="commitMessageIssueKeyValidationOverride">
|
||||||
|
<BoolValueOverride>
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</BoolValueOverride>
|
||||||
|
</option>
|
||||||
|
<option name="commitMessageValidationEnabledOverride">
|
||||||
|
<BoolValueOverride>
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</BoolValueOverride>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
.idea/prettier.xml
generated
1
.idea/prettier.xml
generated
@@ -3,5 +3,6 @@
|
|||||||
<component name="PrettierConfiguration">
|
<component name="PrettierConfiguration">
|
||||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||||
<option name="myRunOnSave" value="true" />
|
<option name="myRunOnSave" value="true" />
|
||||||
|
<option name="myFilesPattern" value="{**/*,*}.{js,ts,jsx,tsx,vue,astro,css,cjs,mjs,json,scss}" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.prettierrc.cjs
Normal file
3
.prettierrc.cjs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
...require('@it-incubator/prettier-config'),
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
singleQuote: true
|
|
||||||
semi: false
|
|
||||||
printWidth: 100
|
|
||||||
trailingComma: none
|
|
||||||
3
.stylelintrc.cjs
Normal file
3
.stylelintrc.cjs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: '@it-incubator/stylelint-config',
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
main: {
|
main: {
|
||||||
// plugins: [externalizeDepsPlugin({ exclude: ['@it-incubator/md-bundler', 'builtin-modules'] })],
|
|
||||||
plugins: [externalizeDepsPlugin({ exclude: ['rehype-slug'] })],
|
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// external: ['builtin-modules'],
|
// external: ['builtin-modules'],
|
||||||
@@ -22,18 +21,20 @@ export default defineConfig({
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
// plugins: [externalizeDepsPlugin({ exclude: ['@it-incubator/md-bundler', 'builtin-modules'] })],
|
||||||
|
plugins: [externalizeDepsPlugin({ exclude: ['rehype-slug'] })],
|
||||||
},
|
},
|
||||||
preload: {
|
preload: {
|
||||||
plugins: [externalizeDepsPlugin()]
|
plugins: [externalizeDepsPlugin()],
|
||||||
},
|
},
|
||||||
renderer: {
|
renderer: {
|
||||||
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@renderer': resolve('src/renderer/src')
|
'@renderer': resolve('src/renderer/src'),
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
plugins: [react()]
|
},
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"@electron-toolkit/preload": "^2.0.0",
|
"@electron-toolkit/preload": "^2.0.0",
|
||||||
"@electron-toolkit/utils": "^2.0.0",
|
"@electron-toolkit/utils": "^2.0.0",
|
||||||
"@fontsource/roboto": "^5.0.8",
|
"@fontsource/roboto": "^5.0.8",
|
||||||
|
"@it-incubator/mdx-components": "^0.0.2",
|
||||||
"@it-incubator/ui-kit": "^0.2.7",
|
"@it-incubator/ui-kit": "^0.2.7",
|
||||||
"builtin-modules": "^3.3.0",
|
"builtin-modules": "^3.3.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
@@ -38,6 +39,9 @@
|
|||||||
"@electron-toolkit/eslint-config-prettier": "^1.0.1",
|
"@electron-toolkit/eslint-config-prettier": "^1.0.1",
|
||||||
"@electron-toolkit/eslint-config-ts": "^1.0.0",
|
"@electron-toolkit/eslint-config-ts": "^1.0.0",
|
||||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||||
|
"@it-incubator/eslint-config": "^1.0.1",
|
||||||
|
"@it-incubator/prettier-config": "^0.1.2",
|
||||||
|
"@it-incubator/stylelint-config": "^0.1.5",
|
||||||
"@types/node": "^18.17.5",
|
"@types/node": "^18.17.5",
|
||||||
"@types/react": "^18.2.20",
|
"@types/react": "^18.2.20",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
|
|||||||
1092
pnpm-lock.yaml
generated
1092
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,9 @@ import { BundledMdx } from './types'
|
|||||||
|
|
||||||
export const bundleMdx = async (source: string): Promise<BundledMdx> => {
|
export const bundleMdx = async (source: string): Promise<BundledMdx> => {
|
||||||
return await bundleMDX({
|
return await bundleMDX({
|
||||||
source,
|
globals: {
|
||||||
globals: {},
|
components: '@it-incubator/mdx-components',
|
||||||
|
},
|
||||||
mdxOptions(options) {
|
mdxOptions(options) {
|
||||||
options.rehypePlugins = [
|
options.rehypePlugins = [
|
||||||
...(options.rehypePlugins ?? []),
|
...(options.rehypePlugins ?? []),
|
||||||
@@ -19,7 +20,13 @@ export const bundleMdx = async (source: string): Promise<BundledMdx> => {
|
|||||||
[
|
[
|
||||||
rehypePrettyCode,
|
rehypePrettyCode,
|
||||||
{
|
{
|
||||||
theme,
|
filterMetaString: (meta: string) => meta.replace(CODE_BLOCK_FILENAME_REGEX, ''),
|
||||||
|
onVisitHighlightedChars(node: any) {
|
||||||
|
node.properties.className = ['highlighted']
|
||||||
|
},
|
||||||
|
onVisitHighlightedLine(node: any) {
|
||||||
|
node.properties.className.push('highlighted')
|
||||||
|
},
|
||||||
onVisitLine(node: any) {
|
onVisitLine(node: any) {
|
||||||
// Prevent lines from collapsing in `display: grid` mode, and
|
// Prevent lines from collapsing in `display: grid` mode, and
|
||||||
// allow empty lines to be copy/pasted
|
// allow empty lines to be copy/pasted
|
||||||
@@ -27,19 +34,14 @@ export const bundleMdx = async (source: string): Promise<BundledMdx> => {
|
|||||||
node.children = [{ type: 'text', value: ' ' }]
|
node.children = [{ type: 'text', value: ' ' }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onVisitHighlightedLine(node: any) {
|
theme,
|
||||||
node.properties.className.push('highlighted')
|
},
|
||||||
},
|
|
||||||
onVisitHighlightedChars(node: any) {
|
|
||||||
node.properties.className = ['highlighted']
|
|
||||||
},
|
|
||||||
filterMetaString: (meta: string) => meta.replace(CODE_BLOCK_FILENAME_REGEX, '')
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
attachMeta
|
attachMeta,
|
||||||
]
|
]
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
},
|
||||||
|
source,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
import { app, shell, BrowserWindow } from 'electron'
|
|
||||||
import { join } from 'path'
|
|
||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
|
||||||
import icon from '../../resources/icon.png?asset'
|
|
||||||
import path from 'node:path'
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
||||||
|
import { BrowserWindow, app, ipcMain, shell } from 'electron'
|
||||||
|
|
||||||
|
import icon from '../../resources/icon.png?asset'
|
||||||
import { bundleMdx } from './bundle-mdx'
|
import { bundleMdx } from './bundle-mdx'
|
||||||
const chokidar = require('chokidar')
|
const chokidar = require('chokidar')
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null
|
let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
function createWindow(): void {
|
function createWindow(): void {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 900,
|
autoHideMenuBar: true,
|
||||||
height: 670,
|
height: 670,
|
||||||
show: false,
|
show: false,
|
||||||
autoHideMenuBar: true,
|
width: 900,
|
||||||
...(process.platform === 'linux' ? { icon } : {}),
|
...(process.platform === 'linux' ? { icon } : {}),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: join(__dirname, '../preload/index.js'),
|
preload: join(__dirname, '../preload/index.js'),
|
||||||
sandbox: false
|
sandbox: false,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.on('ready-to-show', () => {
|
mainWindow.on('ready-to-show', () => {
|
||||||
mainWindow?.show()
|
mainWindow?.show()
|
||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
mainWindow.webContents.setWindowOpenHandler(details => {
|
||||||
shell.openExternal(details.url)
|
shell.openExternal(details.url)
|
||||||
|
|
||||||
return { action: 'deny' }
|
return { action: 'deny' }
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -59,7 +63,9 @@ app.whenReady().then(() => {
|
|||||||
app.on('activate', function () {
|
app.on('activate', function () {
|
||||||
// On macOS it's common to re-create a window in the app when the
|
// On macOS it's common to re-create a window in the app when the
|
||||||
// dock icon is clicked and there are no other windows open.
|
// dock icon is clicked and there are no other windows open.
|
||||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -71,32 +77,57 @@ app.on('window-all-closed', () => {
|
|||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log('dirname', __dirname)
|
|
||||||
// In this file you can include the rest of your app"s specific main process
|
|
||||||
// code. You can also put them in separate files and require them here.
|
|
||||||
const watcher = chokidar.watch(path.resolve(__dirname, '../../../hello.md'), {
|
|
||||||
ignored: /(^|[\/\\])\../, // ignore dotfiles
|
|
||||||
persistent: true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Something to use when events are received.
|
let watcher: any = null
|
||||||
const log = console.log.bind(console)
|
|
||||||
// Add event listeners.
|
function setupWatcher(filePath: string) {
|
||||||
watcher
|
// Close the existing watcher if it exists
|
||||||
.on('add', (path) => log(`File ${path} has been added`))
|
if (watcher) {
|
||||||
.on('change', (path) => {
|
watcher.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher = chokidar.watch(filePath, {
|
||||||
|
ignored: /(^|[/\\])\../, // ignore dotfiles
|
||||||
|
persistent: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const bundleAndSend = async (path: string) => {
|
||||||
fs.readFile(path, 'utf8', async (err, content) => {
|
fs.readFile(path, 'utf8', async (err, content) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Error reading the file:', err)
|
console.error('Error reading the file:', err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('content', content)
|
|
||||||
// Send file content to renderer
|
// Send file content to renderer
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
const bundled = await bundleMdx(content)
|
const bundled = await bundleMdx(content)
|
||||||
// const toc = generateToc(content, {})
|
|
||||||
mainWindow.webContents.send('file-changed', bundled)
|
mainWindow.webContents.send('file-changed', { ...bundled, fileName: path })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
await shell.openPath(path)
|
||||||
.on('unlink', (path) => log(`File ${path} has been removed`))
|
}
|
||||||
|
|
||||||
|
// Add your event listeners
|
||||||
|
watcher
|
||||||
|
.on('add', async (path: string) => {
|
||||||
|
await bundleAndSend(path)
|
||||||
|
|
||||||
|
console.warn(`File ${path} has been added`)
|
||||||
|
})
|
||||||
|
.on('change', bundleAndSend)
|
||||||
|
.on('unlink', (path: string) => console.warn(`File ${path} has been removed`))
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
if (arg.length > 0) {
|
||||||
|
setupWatcher(arg[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Initially setup watcher for 'hello.md'
|
||||||
|
setupWatcher(path.resolve(__dirname, '../../../hello.md'))
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ function visit(node, tagNames, handler) {
|
|||||||
|
|
||||||
export const parseMeta =
|
export const parseMeta =
|
||||||
({ defaultShowCopyCode }) =>
|
({ defaultShowCopyCode }) =>
|
||||||
(tree) => {
|
tree => {
|
||||||
visit(tree, ['pre'], (preEl) => {
|
visit(tree, ['pre'], preEl => {
|
||||||
const [codeEl] = preEl.children
|
const [codeEl] = preEl.children
|
||||||
|
|
||||||
// Add default language `text` for code-blocks without languages
|
// Add default language `text` for code-blocks without languages
|
||||||
@@ -31,8 +31,8 @@ export const parseMeta =
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const attachMeta = () => (tree) => {
|
export const attachMeta = () => tree => {
|
||||||
visit(tree, ['div', 'pre'], (node) => {
|
visit(tree, ['div', 'pre'], node => {
|
||||||
if ('data-rehype-pretty-code-fragment' in node.properties) {
|
if ('data-rehype-pretty-code-fragment' in node.properties) {
|
||||||
// remove <div data-rehype-pretty-code-fragment /> element that wraps <pre /> element
|
// remove <div data-rehype-pretty-code-fragment /> element that wraps <pre /> element
|
||||||
// because we'll wrap with our own <div />
|
// because we'll wrap with our own <div />
|
||||||
|
|||||||
2
src/preload/index.d.ts
vendored
2
src/preload/index.d.ts
vendored
@@ -2,7 +2,7 @@ import { ElectronAPI } from '@electron-toolkit/preload'
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: ElectronAPI
|
|
||||||
api: unknown
|
api: unknown
|
||||||
|
electron: ElectronAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { contextBridge } from 'electron'
|
|
||||||
import { electronAPI } from '@electron-toolkit/preload'
|
import { electronAPI } from '@electron-toolkit/preload'
|
||||||
|
import { contextBridge } from 'electron'
|
||||||
|
|
||||||
// Custom APIs for renderer
|
// Custom APIs for renderer
|
||||||
const api = {}
|
const api = {}
|
||||||
|
|||||||
@@ -1,50 +1,64 @@
|
|||||||
import { ComponentProps, ReactElement, useEffect, useState } from 'react'
|
import { ComponentProps, ReactElement, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { IpcRendererListener } from '@electron-toolkit/preload'
|
||||||
|
import * as components from '@it-incubator/mdx-components'
|
||||||
|
import { ImagePreview, Typography } from '@it-incubator/ui-kit'
|
||||||
import { getMDXComponent } from 'mdx-bundler/client'
|
import { getMDXComponent } from 'mdx-bundler/client'
|
||||||
|
|
||||||
import s from './view.module.scss'
|
import s from './view.module.scss'
|
||||||
|
|
||||||
import { Pre } from './components/pre'
|
import { Pre } from './components/pre'
|
||||||
import { ImagePreview } from '@it-incubator/ui-kit'
|
|
||||||
function App() {
|
function App() {
|
||||||
const [code, setCode] = useState<any>()
|
const [code, setCode] = useState<string>('')
|
||||||
|
const [fileName, setFileName] = useState<string>('')
|
||||||
const [srcPreview, setSrcPreview] = useState<string>('')
|
const [srcPreview, setSrcPreview] = useState<string>('')
|
||||||
console.log(code)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = (event, content) => {
|
const listener: IpcRendererListener = (_event, content) => {
|
||||||
console.log('file-changed', event, content)
|
|
||||||
setCode(content.code)
|
setCode(content.code)
|
||||||
|
setFileName(content.fileName)
|
||||||
}
|
}
|
||||||
console.log(window.electron)
|
|
||||||
window.electron.ipcRenderer.on('file-changed', listener)
|
window.electron.ipcRenderer.on('file-changed', listener)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.electron.ipcRenderer.removeAllListeners('file-changed')
|
window.electron.ipcRenderer.removeAllListeners('file-changed')
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
if (!code) return null
|
|
||||||
const Component = getMDXComponent(code)
|
if (!code) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const Component = getMDXComponent(code, { components: components })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={s.root}>
|
<div>
|
||||||
<ImagePreview open={!!srcPreview} src={srcPreview} onClose={() => setSrcPreview('')} />
|
<Typography.H1>{fileName}</Typography.H1>
|
||||||
<Component
|
<article className={s.root}>
|
||||||
components={{
|
<ImagePreview onClose={() => setSrcPreview('')} open={!!srcPreview} src={srcPreview} />
|
||||||
code: Code,
|
<Component
|
||||||
pre: Pre,
|
components={{
|
||||||
img: (props) => (
|
code: Code,
|
||||||
<img
|
img: props => (
|
||||||
{...props}
|
<img
|
||||||
onClick={() => setSrcPreview(props.src || '')}
|
{...props}
|
||||||
style={{ cursor: 'pointer' }}
|
onClick={() => setSrcPreview(props.src || '')}
|
||||||
/>
|
style={{ cursor: 'pointer' }}
|
||||||
)
|
/>
|
||||||
}}
|
),
|
||||||
/>
|
pre: Pre,
|
||||||
</article>
|
}}
|
||||||
|
/>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
const Code = ({ children, ...props }: ComponentProps<'code'>): ReactElement => {
|
const Code = ({ children, ...props }: ComponentProps<'code'>): ReactElement => {
|
||||||
return (
|
return (
|
||||||
<code className={s.inline} dir="ltr" {...props}>
|
<code className={s.inline} dir={'ltr'} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</code>
|
</code>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
function Versions(): JSX.Element {
|
function Versions() {
|
||||||
const [versions] = useState(window.electron.process.versions)
|
const [versions] = useState(window.electron.process.versions)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className="versions">
|
<ul className={'versions'}>
|
||||||
<li className="electron-version">Electron v{versions.electron}</li>
|
<li className={'electron-version'}>Electron v{versions.electron}</li>
|
||||||
<li className="chrome-version">Chromium v{versions.chrome}</li>
|
<li className={'chrome-version'}>Chromium v{versions.chrome}</li>
|
||||||
<li className="node-version">Node v{versions.node}</li>
|
<li className={'node-version'}>Node v{versions.node}</li>
|
||||||
<li className="v8-version">V8 v{versions.v8}</li>
|
<li className={'v8-version'}>V8 v{versions.v8}</li>
|
||||||
</ul>
|
</ul>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import type { ComponentProps, ReactElement } from 'react'
|
|||||||
|
|
||||||
export function CheckIcon(props: ComponentProps<'svg'>): ReactElement {
|
export function CheckIcon(props: ComponentProps<'svg'>): ReactElement {
|
||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 20 20" width="1em" height="1em" fill="currentColor" {...props}>
|
<svg fill={'currentColor'} height={'1em'} viewBox={'0 0 20 20'} width={'1em'} {...props}>
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
clipRule={'evenodd'}
|
||||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
d={
|
||||||
clipRule="evenodd"
|
'M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z'
|
||||||
|
}
|
||||||
|
fillRule={'evenodd'}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { ComponentProps, ReactElement } from 'react'
|
import { ComponentProps, ReactElement, useCallback, useEffect, useState } from 'react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
import s from './copy-to-clipboard.module.scss'
|
||||||
|
|
||||||
import { CheckIcon } from './check'
|
import { CheckIcon } from './check'
|
||||||
import { CopyIcon } from './copy'
|
import { CopyIcon } from './copy'
|
||||||
import s from './copy-to-clipboard.module.scss'
|
|
||||||
|
|
||||||
export const CopyToClipboard = ({
|
export const CopyToClipboard = ({
|
||||||
getValue,
|
getValue,
|
||||||
@@ -16,7 +16,9 @@ export const CopyToClipboard = ({
|
|||||||
const [isCopied, setCopied] = useState(false)
|
const [isCopied, setCopied] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isCopied) return
|
if (!isCopied) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const timerId = setTimeout(() => {
|
const timerId = setTimeout(() => {
|
||||||
setCopied(false)
|
setCopied(false)
|
||||||
}, 2000)
|
}, 2000)
|
||||||
@@ -41,7 +43,7 @@ export const CopyToClipboard = ({
|
|||||||
const IconToUse = isCopied ? CheckIcon : CopyIcon
|
const IconToUse = isCopied ? CheckIcon : CopyIcon
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button onClick={handleClick} className={s.button} title="Copy code" tabIndex={0} {...props}>
|
<button className={s.button} onClick={handleClick} tabIndex={0} title={'Copy code'} {...props}>
|
||||||
<IconToUse className={clsx('nextra-copy-icon', s.root)} />
|
<IconToUse className={clsx('nextra-copy-icon', s.root)} />
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,29 +3,31 @@ import type { ComponentProps, ReactElement } from 'react'
|
|||||||
export function CopyIcon(props: ComponentProps<'svg'>): ReactElement {
|
export function CopyIcon(props: ComponentProps<'svg'>): ReactElement {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="24"
|
fill={'none'}
|
||||||
height="24"
|
height={'24'}
|
||||||
viewBox="0 0 24 24"
|
stroke={'currentColor'}
|
||||||
fill="none"
|
viewBox={'0 0 24 24'}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
width={'24'}
|
||||||
stroke="currentColor"
|
xmlns={'http://www.w3.org/2000/svg'}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x="9"
|
height={'13'}
|
||||||
y="9"
|
rx={'2'}
|
||||||
width="13"
|
strokeLinecap={'round'}
|
||||||
height="13"
|
strokeLinejoin={'round'}
|
||||||
rx="2"
|
strokeWidth={'2'}
|
||||||
strokeWidth="2"
|
width={'13'}
|
||||||
strokeLinecap="round"
|
x={'9'}
|
||||||
strokeLinejoin="round"
|
y={'9'}
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5"
|
d={
|
||||||
strokeWidth="2"
|
'M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5'
|
||||||
strokeLinecap="round"
|
}
|
||||||
strokeLinejoin="round"
|
strokeLinecap={'round'}
|
||||||
|
strokeLinejoin={'round'}
|
||||||
|
strokeWidth={'2'}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { ComponentProps, ReactElement, useRef } from 'react'
|
|||||||
import { Scrollbar } from '@it-incubator/ui-kit'
|
import { Scrollbar } from '@it-incubator/ui-kit'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
import { CopyToClipboard } from './copy-to-clipboard'
|
|
||||||
import styles from './pre.module.scss'
|
import styles from './pre.module.scss'
|
||||||
|
|
||||||
|
import { CopyToClipboard } from './copy-to-clipboard'
|
||||||
export const Pre = ({
|
export const Pre = ({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
|
|||||||
@@ -1,12 +1,34 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import ReactDOM from 'react-dom/client'
|
import ReactDOM from 'react-dom/client'
|
||||||
|
|
||||||
|
import './assets/index.css'
|
||||||
import '@fontsource/roboto/400.css'
|
import '@fontsource/roboto/400.css'
|
||||||
import '@fontsource/roboto/500.css'
|
import '@fontsource/roboto/500.css'
|
||||||
import '@fontsource/roboto/700.css'
|
import '@fontsource/roboto/700.css'
|
||||||
|
import '@it-incubator/mdx-components/dist/style.css'
|
||||||
import '@it-incubator/ui-kit/dist/style.css'
|
import '@it-incubator/ui-kit/dist/style.css'
|
||||||
import './assets/index.css'
|
|
||||||
import App from './App'
|
import App from './App'
|
||||||
|
|
||||||
|
document.addEventListener('dragover', e => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('drop', event => {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
if (!event?.dataTransfer?.files.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const pathArr: string[] = []
|
||||||
|
|
||||||
|
for (const f of event.dataTransfer.files) {
|
||||||
|
pathArr.push(f.path) // assemble array for main.js
|
||||||
|
}
|
||||||
|
window.electron.ipcRenderer.sendSync('dropped-file', pathArr)
|
||||||
|
})
|
||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
|||||||
Reference in New Issue
Block a user