mirror of
https://github.com/ershisan99/md-preview-desktop.git
synced 2025-12-16 20:59:24 +00:00
mvp created
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
out
|
||||||
|
.gitignore
|
||||||
9
.eslintrc.cjs
Normal file
9
.eslintrc.cjs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react/jsx-runtime',
|
||||||
|
'@electron-toolkit/eslint-config-ts/recommended',
|
||||||
|
'@electron-toolkit/eslint-config-prettier'
|
||||||
|
]
|
||||||
|
}
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
out
|
||||||
|
*.log*
|
||||||
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
11
.idea/aws.xml
generated
Normal file
11
.idea/aws.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="accountSettings">
|
||||||
|
<option name="activeRegion" value="us-east-1" />
|
||||||
|
<option name="recentlyUsedRegions">
|
||||||
|
<list>
|
||||||
|
<option value="us-east-1" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/jsLinters/eslint.xml
generated
Normal file
6
.idea/jsLinters/eslint.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="EslintConfiguration">
|
||||||
|
<option name="fix-on-save" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/md-preview-desktop.iml
generated
Normal file
12
.idea/md-preview-desktop.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/md-preview-desktop.iml" filepath="$PROJECT_DIR$/.idea/md-preview-desktop.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
7
.idea/prettier.xml
generated
Normal file
7
.idea/prettier.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PrettierConfiguration">
|
||||||
|
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||||
|
<option name="myRunOnSave" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
4
.idea/watcherTasks.xml
generated
Normal file
4
.idea/watcherTasks.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectTasksOptions" suppressed-tasks="SCSS" />
|
||||||
|
</project>
|
||||||
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
out
|
||||||
|
dist
|
||||||
|
pnpm-lock.yaml
|
||||||
|
LICENSE.md
|
||||||
|
tsconfig.json
|
||||||
|
tsconfig.*.json
|
||||||
4
.prettierrc.yaml
Normal file
4
.prettierrc.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
singleQuote: true
|
||||||
|
semi: false
|
||||||
|
printWidth: 100
|
||||||
|
trailingComma: none
|
||||||
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["dbaeumer.vscode-eslint"]
|
||||||
|
}
|
||||||
39
.vscode/launch.json
vendored
Normal file
39
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug Main Process",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
|
||||||
|
"windows": {
|
||||||
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
||||||
|
},
|
||||||
|
"runtimeArgs": ["--sourcemap"],
|
||||||
|
"env": {
|
||||||
|
"REMOTE_DEBUGGING_PORT": "9222"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug Renderer Process",
|
||||||
|
"port": 9222,
|
||||||
|
"request": "attach",
|
||||||
|
"type": "chrome",
|
||||||
|
"webRoot": "${workspaceFolder}/src/renderer",
|
||||||
|
"timeout": 60000,
|
||||||
|
"presentation": {
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compounds": [
|
||||||
|
{
|
||||||
|
"name": "Debug All",
|
||||||
|
"configurations": ["Debug Main Process", "Debug Renderer Process"],
|
||||||
|
"presentation": {
|
||||||
|
"order": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
|
"[json]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
README.md
Normal file
34
README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# md-preview-desktop
|
||||||
|
|
||||||
|
An Electron application with React and TypeScript
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For windows
|
||||||
|
$ pnpm build:win
|
||||||
|
|
||||||
|
# For macOS
|
||||||
|
$ pnpm build:mac
|
||||||
|
|
||||||
|
# For Linux
|
||||||
|
$ pnpm build:linux
|
||||||
|
```
|
||||||
12
build/entitlements.mac.plist
Normal file
12
build/entitlements.mac.plist
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
BIN
build/icon.ico
Normal file
BIN
build/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
BIN
build/icon.png
Normal file
BIN
build/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
3
dev-app-update.yml
Normal file
3
dev-app-update.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
provider: generic
|
||||||
|
url: https://example.com/auto-updates
|
||||||
|
updaterCacheDirName: md-preview-desktop-updater
|
||||||
43
electron-builder.yml
Normal file
43
electron-builder.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
appId: com.electron.app
|
||||||
|
productName: md-preview-desktop
|
||||||
|
directories:
|
||||||
|
buildResources: build
|
||||||
|
files:
|
||||||
|
- '!**/.vscode/*'
|
||||||
|
- '!src/*'
|
||||||
|
- '!electron.vite.config.{js,ts,mjs,cjs}'
|
||||||
|
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
|
||||||
|
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
|
||||||
|
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
||||||
|
asarUnpack:
|
||||||
|
- resources/**
|
||||||
|
win:
|
||||||
|
executableName: md-preview-desktop
|
||||||
|
nsis:
|
||||||
|
artifactName: ${name}-${version}-setup.${ext}
|
||||||
|
shortcutName: ${productName}
|
||||||
|
uninstallDisplayName: ${productName}
|
||||||
|
createDesktopShortcut: always
|
||||||
|
mac:
|
||||||
|
entitlementsInherit: build/entitlements.mac.plist
|
||||||
|
extendInfo:
|
||||||
|
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||||
|
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||||
|
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||||
|
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||||
|
notarize: false
|
||||||
|
dmg:
|
||||||
|
artifactName: ${name}-${version}.${ext}
|
||||||
|
linux:
|
||||||
|
target:
|
||||||
|
- AppImage
|
||||||
|
- snap
|
||||||
|
- deb
|
||||||
|
maintainer: electronjs.org
|
||||||
|
category: Utility
|
||||||
|
appImage:
|
||||||
|
artifactName: ${name}-${version}.${ext}
|
||||||
|
npmRebuild: false
|
||||||
|
publish:
|
||||||
|
provider: generic
|
||||||
|
url: https://example.com/auto-updates
|
||||||
39
electron.vite.config.ts
Normal file
39
electron.vite.config.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
main: {
|
||||||
|
// plugins: [externalizeDepsPlugin({ exclude: ['@it-incubator/md-bundler', 'builtin-modules'] })],
|
||||||
|
plugins: [externalizeDepsPlugin({ exclude: ['rehype-slug'] })],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
// external: ['builtin-modules'],
|
||||||
|
// output: {
|
||||||
|
// manualChunks(id) {
|
||||||
|
// if (id.includes('builtin-modules')) {
|
||||||
|
// return 'builtin-modules'
|
||||||
|
// }
|
||||||
|
// if (id.includes('@it-incubator/md-bundler')) {
|
||||||
|
// return '@it-incubator/md-bundler'
|
||||||
|
// }
|
||||||
|
// if (id.includes('esbuild')) {
|
||||||
|
// return 'esbuild'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
preload: {
|
||||||
|
plugins: [externalizeDepsPlugin()]
|
||||||
|
},
|
||||||
|
renderer: {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@renderer': resolve('src/renderer/src')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [react()]
|
||||||
|
}
|
||||||
|
})
|
||||||
56
package.json
Normal file
56
package.json
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"name": "md-preview-desktop",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "An Electron application with React and TypeScript",
|
||||||
|
"main": "./out/main/index.js",
|
||||||
|
"author": "example.com",
|
||||||
|
"homepage": "https://www.electronjs.org",
|
||||||
|
"scripts": {
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||||
|
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
||||||
|
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
|
||||||
|
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
||||||
|
"start": "electron-vite preview",
|
||||||
|
"dev": "electron-vite dev",
|
||||||
|
"build": "npm run typecheck && electron-vite build",
|
||||||
|
"postinstall": "electron-builder install-app-deps",
|
||||||
|
"build:win": "npm run build && electron-builder --win --config",
|
||||||
|
"build:mac": "electron-vite build && electron-builder --mac --config",
|
||||||
|
"build:linux": "electron-vite build && electron-builder --linux --config"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@electron-toolkit/preload": "^2.0.0",
|
||||||
|
"@electron-toolkit/utils": "^2.0.0",
|
||||||
|
"@fontsource/roboto": "^5.0.8",
|
||||||
|
"@it-incubator/ui-kit": "^0.2.7",
|
||||||
|
"builtin-modules": "^3.3.0",
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
|
"electron-updater": "^6.1.1",
|
||||||
|
"esbuild": "^0.19.3",
|
||||||
|
"mdx-bundler": "^9.2.1",
|
||||||
|
"react-toastify": "^9.1.3",
|
||||||
|
"rehype-pretty-code": "^0.10.1",
|
||||||
|
"rehype-slug": "^6.0.0",
|
||||||
|
"sass": "^1.67.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@electron-toolkit/eslint-config-prettier": "^1.0.1",
|
||||||
|
"@electron-toolkit/eslint-config-ts": "^1.0.0",
|
||||||
|
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||||
|
"@types/node": "^18.17.5",
|
||||||
|
"@types/react": "^18.2.20",
|
||||||
|
"@types/react-dom": "^18.2.7",
|
||||||
|
"@vitejs/plugin-react": "^4.0.4",
|
||||||
|
"electron": "^25.6.0",
|
||||||
|
"electron-builder": "^24.6.3",
|
||||||
|
"electron-vite": "^1.0.27",
|
||||||
|
"eslint": "^8.47.0",
|
||||||
|
"eslint-plugin-react": "^7.33.2",
|
||||||
|
"prettier": "^3.0.2",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"typescript": "^5.1.6",
|
||||||
|
"vite": "^4.4.9"
|
||||||
|
}
|
||||||
|
}
|
||||||
6718
pnpm-lock.yaml
generated
Normal file
6718
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/icon.png
Normal file
BIN
resources/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
45
src/main/bundle-mdx.ts
Normal file
45
src/main/bundle-mdx.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { bundleMDX } from 'mdx-bundler'
|
||||||
|
import rehypePrettyCode from 'rehype-pretty-code'
|
||||||
|
import rehypeSlug from 'rehype-slug'
|
||||||
|
|
||||||
|
import { CODE_BLOCK_FILENAME_REGEX } from './constants'
|
||||||
|
import { attachMeta, parseMeta } from './rehype'
|
||||||
|
import theme from './theme.json'
|
||||||
|
import { BundledMdx } from './types'
|
||||||
|
|
||||||
|
export const bundleMdx = async (source: string): Promise<BundledMdx> => {
|
||||||
|
return await bundleMDX({
|
||||||
|
source,
|
||||||
|
globals: {},
|
||||||
|
mdxOptions(options) {
|
||||||
|
options.rehypePlugins = [
|
||||||
|
...(options.rehypePlugins ?? []),
|
||||||
|
rehypeSlug,
|
||||||
|
[parseMeta, { defaultShowCopyCode: true }],
|
||||||
|
[
|
||||||
|
rehypePrettyCode,
|
||||||
|
{
|
||||||
|
theme,
|
||||||
|
onVisitLine(node: any) {
|
||||||
|
// Prevent lines from collapsing in `display: grid` mode, and
|
||||||
|
// allow empty lines to be copy/pasted
|
||||||
|
if (node.children.length === 0) {
|
||||||
|
node.children = [{ type: 'text', value: ' ' }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onVisitHighlightedLine(node: any) {
|
||||||
|
node.properties.className.push('highlighted')
|
||||||
|
},
|
||||||
|
onVisitHighlightedChars(node: any) {
|
||||||
|
node.properties.className = ['highlighted']
|
||||||
|
},
|
||||||
|
filterMetaString: (meta: string) => meta.replace(CODE_BLOCK_FILENAME_REGEX, '')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
attachMeta
|
||||||
|
]
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
1
src/main/constants.ts
Normal file
1
src/main/constants.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const CODE_BLOCK_FILENAME_REGEX = /filename="([^"]+)"/
|
||||||
102
src/main/index.ts
Normal file
102
src/main/index.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
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 { bundleMdx } from './bundle-mdx'
|
||||||
|
const chokidar = require('chokidar')
|
||||||
|
|
||||||
|
let mainWindow: BrowserWindow | null = null
|
||||||
|
function createWindow(): void {
|
||||||
|
// Create the browser window.
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
width: 900,
|
||||||
|
height: 670,
|
||||||
|
show: false,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
...(process.platform === 'linux' ? { icon } : {}),
|
||||||
|
webPreferences: {
|
||||||
|
preload: join(__dirname, '../preload/index.js'),
|
||||||
|
sandbox: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.on('ready-to-show', () => {
|
||||||
|
mainWindow?.show()
|
||||||
|
})
|
||||||
|
|
||||||
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
|
shell.openExternal(details.url)
|
||||||
|
return { action: 'deny' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 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']) {
|
||||||
|
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||||
|
} else {
|
||||||
|
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method will be called when Electron has finished
|
||||||
|
// initialization and is ready to create browser windows.
|
||||||
|
// Some APIs can only be used after this event occurs.
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
// Set app user model id for windows
|
||||||
|
electronApp.setAppUserModelId('com.electron')
|
||||||
|
|
||||||
|
// Default open or close DevTools by F12 in development
|
||||||
|
// and ignore CommandOrControl + R in production.
|
||||||
|
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||||
|
app.on('browser-window-created', (_, window) => {
|
||||||
|
optimizer.watchWindowShortcuts(window)
|
||||||
|
})
|
||||||
|
|
||||||
|
createWindow()
|
||||||
|
|
||||||
|
app.on('activate', function () {
|
||||||
|
// 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.
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Quit when all windows are closed, except on macOS. There, it's common
|
||||||
|
// for applications and their menu bar to stay active until the user quits
|
||||||
|
// explicitly with Cmd + Q.
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
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.
|
||||||
|
const log = console.log.bind(console)
|
||||||
|
// Add event listeners.
|
||||||
|
watcher
|
||||||
|
.on('add', (path) => log(`File ${path} has been added`))
|
||||||
|
.on('change', (path) => {
|
||||||
|
fs.readFile(path, 'utf8', async (err, content) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('Error reading the file:', err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('content', content)
|
||||||
|
// Send file content to renderer
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
const bundled = await bundleMdx(content)
|
||||||
|
// const toc = generateToc(content, {})
|
||||||
|
mainWindow.webContents.send('file-changed', bundled)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on('unlink', (path) => log(`File ${path} has been removed`))
|
||||||
45
src/main/rehype.ts
Normal file
45
src/main/rehype.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { CODE_BLOCK_FILENAME_REGEX } from './constants.js'
|
||||||
|
function visit(node, tagNames, handler) {
|
||||||
|
if (tagNames.includes(node.tagName)) {
|
||||||
|
handler(node)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ('children' in node) {
|
||||||
|
for (const n of node.children) {
|
||||||
|
visit(n, tagNames, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseMeta =
|
||||||
|
({ defaultShowCopyCode }) =>
|
||||||
|
(tree) => {
|
||||||
|
visit(tree, ['pre'], (preEl) => {
|
||||||
|
const [codeEl] = preEl.children
|
||||||
|
|
||||||
|
// Add default language `text` for code-blocks without languages
|
||||||
|
codeEl.properties.className ||= ['language-text']
|
||||||
|
const meta = codeEl.data?.meta
|
||||||
|
|
||||||
|
preEl.__nextra_filename = meta?.match(CODE_BLOCK_FILENAME_REGEX)?.[1]
|
||||||
|
|
||||||
|
preEl.__nextra_hasCopyCode = meta
|
||||||
|
? (defaultShowCopyCode && !/( |^)copy=false($| )/.test(meta)) || /( |^)copy($| )/.test(meta)
|
||||||
|
: defaultShowCopyCode
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const attachMeta = () => (tree) => {
|
||||||
|
visit(tree, ['div', 'pre'], (node) => {
|
||||||
|
if ('data-rehype-pretty-code-fragment' in node.properties) {
|
||||||
|
// remove <div data-rehype-pretty-code-fragment /> element that wraps <pre /> element
|
||||||
|
// because we'll wrap with our own <div />
|
||||||
|
Object.assign(node, node.children[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
node.properties.filename = node.__nextra_filename
|
||||||
|
node.properties.hasCopyCode = node.__nextra_hasCopyCode
|
||||||
|
})
|
||||||
|
}
|
||||||
224
src/main/theme.json
Normal file
224
src/main/theme.json
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
{
|
||||||
|
"name": "css-variables",
|
||||||
|
"type": "css",
|
||||||
|
"colors": {
|
||||||
|
"editor.foreground": "#000001",
|
||||||
|
"editor.background": "#000002",
|
||||||
|
"terminal.ansiBlack": "#A00000",
|
||||||
|
"terminal.ansiRed": "#A00001",
|
||||||
|
"terminal.ansiGreen": "#A00002",
|
||||||
|
"terminal.ansiYellow": "#A00003",
|
||||||
|
"terminal.ansiBlue": "#A00004",
|
||||||
|
"terminal.ansiMagenta": "#A00005",
|
||||||
|
"terminal.ansiCyan": "#A00006",
|
||||||
|
"terminal.ansiWhite": "#A00007",
|
||||||
|
"terminal.ansiBrightBlack": "#A00008",
|
||||||
|
"terminal.ansiBrightRed": "#A00009",
|
||||||
|
"terminal.ansiBrightGreen": "#A00010",
|
||||||
|
"terminal.ansiBrightYellow": "#A00011",
|
||||||
|
"terminal.ansiBrightBlue": "#A00012",
|
||||||
|
"terminal.ansiBrightMagenta": "#A00013",
|
||||||
|
"terminal.ansiBrightCyan": "#A00014",
|
||||||
|
"terminal.ansiBrightWhite": "#A00015"
|
||||||
|
},
|
||||||
|
"tokenColors": [
|
||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"markup.deleted",
|
||||||
|
"meta.diff.header.from-file",
|
||||||
|
"punctuation.definition.deleted"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#ef6270"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"markup.inserted",
|
||||||
|
"meta.diff.header.to-file",
|
||||||
|
"punctuation.definition.inserted"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#4bb74a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"keyword.operator.accessor",
|
||||||
|
"meta.group.braces.round.function.arguments",
|
||||||
|
"meta.template.expression",
|
||||||
|
"markup.fenced_code meta.embedded.block"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000001"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "emphasis",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"strong",
|
||||||
|
"markup.heading.markdown",
|
||||||
|
"markup.bold.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "bold"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"markup.italic.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "italic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "meta.link.inline.markdown",
|
||||||
|
"settings": {
|
||||||
|
"fontStyle": "underline",
|
||||||
|
"foreground": "#000004"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"string",
|
||||||
|
"markup.fenced_code",
|
||||||
|
"markup.inline"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000005"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"comment",
|
||||||
|
"string.quoted.docstring.multi"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000006"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"constant.numeric",
|
||||||
|
"constant.language",
|
||||||
|
"constant.other.placeholder",
|
||||||
|
"constant.character.format.placeholder",
|
||||||
|
"variable.language.this",
|
||||||
|
"variable.other.object",
|
||||||
|
"variable.other.class",
|
||||||
|
"variable.other.constant",
|
||||||
|
"meta.property-name",
|
||||||
|
"meta.property-value",
|
||||||
|
"support"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000004"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"keyword",
|
||||||
|
"storage.modifier",
|
||||||
|
"storage.type",
|
||||||
|
"storage.control.clojure",
|
||||||
|
"entity.name.function.clojure",
|
||||||
|
"entity.name.tag.yaml",
|
||||||
|
"support.function.node",
|
||||||
|
"support.type.property-name.json",
|
||||||
|
"punctuation.separator.key-value",
|
||||||
|
"punctuation.definition.template-expression"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000007"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "variable.parameter.function",
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000008"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"support.function",
|
||||||
|
"entity.name.type",
|
||||||
|
"entity.other.inherited-class",
|
||||||
|
"meta.function-call",
|
||||||
|
"meta.instance.constructor",
|
||||||
|
"entity.other.attribute-name",
|
||||||
|
"entity.name.function",
|
||||||
|
"constant.keyword.clojure"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000009"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"entity.name.tag",
|
||||||
|
"string.quoted",
|
||||||
|
"string.regexp",
|
||||||
|
"string.interpolated",
|
||||||
|
"string.template",
|
||||||
|
"string.unquoted.plain.out.yaml",
|
||||||
|
"keyword.other.template"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000010"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": [
|
||||||
|
"punctuation.definition.arguments",
|
||||||
|
"punctuation.definition.dict",
|
||||||
|
"punctuation.separator",
|
||||||
|
"meta.function-call.arguments"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000011"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "[Custom] Markdown links",
|
||||||
|
"scope": [
|
||||||
|
"markup.underline.link",
|
||||||
|
"punctuation.definition.metadata.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000012"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "[Custom] Markdown list",
|
||||||
|
"scope": [
|
||||||
|
"beginning.punctuation.definition.list.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000005"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "[Custom] Markdown punctuation definition brackets",
|
||||||
|
"scope": [
|
||||||
|
"punctuation.definition.string.begin.markdown",
|
||||||
|
"punctuation.definition.string.end.markdown",
|
||||||
|
"string.other.link.title.markdown",
|
||||||
|
"string.other.link.description.markdown"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"foreground": "#000007"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
4
src/main/types.ts
Normal file
4
src/main/types.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export type BundledMdx = {
|
||||||
|
code: string
|
||||||
|
frontmatter: Record<string, any>
|
||||||
|
}
|
||||||
8
src/preload/index.d.ts
vendored
Normal file
8
src/preload/index.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electron: ElectronAPI
|
||||||
|
api: unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/preload/index.ts
Normal file
22
src/preload/index.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { contextBridge } from 'electron'
|
||||||
|
import { electronAPI } from '@electron-toolkit/preload'
|
||||||
|
|
||||||
|
// Custom APIs for renderer
|
||||||
|
const api = {}
|
||||||
|
|
||||||
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
|
// renderer only if context isolation is enabled, otherwise
|
||||||
|
// just add to the DOM global.
|
||||||
|
if (process.contextIsolated) {
|
||||||
|
try {
|
||||||
|
contextBridge.exposeInMainWorld('electron', electronAPI)
|
||||||
|
contextBridge.exposeInMainWorld('api', api)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @ts-ignore (define in dts)
|
||||||
|
window.electron = electronAPI
|
||||||
|
// @ts-ignore (define in dts)
|
||||||
|
window.api = api
|
||||||
|
}
|
||||||
17
src/renderer/index.html
Normal file
17
src/renderer/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Electron</title>
|
||||||
|
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src *; font-src * 'self' data:;"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
51
src/renderer/src/App.tsx
Normal file
51
src/renderer/src/App.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { ComponentProps, ReactElement, useEffect, useState } from 'react'
|
||||||
|
import { getMDXComponent } from 'mdx-bundler/client'
|
||||||
|
import s from './view.module.scss'
|
||||||
|
import { Pre } from './components/pre'
|
||||||
|
import { ImagePreview } from '@it-incubator/ui-kit'
|
||||||
|
function App() {
|
||||||
|
const [code, setCode] = useState<any>()
|
||||||
|
const [srcPreview, setSrcPreview] = useState<string>('')
|
||||||
|
console.log(code)
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = (event, content) => {
|
||||||
|
console.log('file-changed', event, content)
|
||||||
|
setCode(content.code)
|
||||||
|
}
|
||||||
|
console.log(window.electron)
|
||||||
|
window.electron.ipcRenderer.on('file-changed', listener)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.electron.ipcRenderer.removeAllListeners('file-changed')
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
if (!code) return null
|
||||||
|
const Component = getMDXComponent(code)
|
||||||
|
return (
|
||||||
|
<article className={s.root}>
|
||||||
|
<ImagePreview open={!!srcPreview} src={srcPreview} onClose={() => setSrcPreview('')} />
|
||||||
|
<Component
|
||||||
|
components={{
|
||||||
|
code: Code,
|
||||||
|
pre: Pre,
|
||||||
|
img: (props) => (
|
||||||
|
<img
|
||||||
|
{...props}
|
||||||
|
onClick={() => setSrcPreview(props.src || '')}
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
const Code = ({ children, ...props }: ComponentProps<'code'>): ReactElement => {
|
||||||
|
return (
|
||||||
|
<code className={s.inline} dir="ltr" {...props}>
|
||||||
|
{children}
|
||||||
|
</code>
|
||||||
|
)
|
||||||
|
}
|
||||||
34
src/renderer/src/assets/icons.svg
Normal file
34
src/renderer/src/assets/icons.svg
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<symbol id="electron" viewBox="0 0 900 300">
|
||||||
|
<g fill="none" fill-rule="evenodd">
|
||||||
|
<g class="hero-apps" style="fill: #71abb7;">
|
||||||
|
<path d="M15 138l-4.9-.64L8 133l-2.1 4.36L1 138l3.6 3.26-.93 4.74L8 143.67l4.33 2.33-.93-4.74z"></path>
|
||||||
|
<path d="M897.2 114.0912l-5.2 3.63v-2.72c0-.55-.45-1-1-1h-8c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h8c.55 0 1-.45 1-1v-2.72l5.2 3.63c.33.23.8 0 .8-.41v-10c0-.41-.47-.64-.8-.41z"></path>
|
||||||
|
<path d="M65.4 188.625h-1.6c.88 0 1.6-.7313 1.6-1.625v-1.625c0-.8937-.72-1.625-1.6-1.625h-1.6c-.88 0-1.6.7313-1.6 1.625V187c0 .8937.72 1.625 1.6 1.625h-1.6c-.88 0-1.6.7313-1.6 1.625v3.25h1.6v4.875c0 .8937.72 1.625 1.6 1.625h1.6c.88 0 1.6-.7313 1.6-1.625V193.5H67v-3.25c0-.8937-.72-1.625-1.6-1.625zm-3.2-3.25h1.6V187h-1.6v-1.625zm3.2 6.5h-1.6v6.5h-1.6v-6.5h-1.6v-1.625h4.8v1.625zm3.344-5.6875c0-3.2175-2.576-5.8337-5.744-5.8337-3.168 0-5.744 2.6162-5.744 5.8337 0 .455.048.8937.144 1.3162v3.2175c-.976-1.2512-1.6-2.8112-1.6-4.55 0-4.03 3.232-7.3125 7.2-7.3125s7.2 3.2825 7.2 7.3125c0 1.7225-.624 3.2988-1.6 4.55v-3.2175c.096-.4387.144-.8612.144-1.3162zm6.256 0c0 4.68-2.608 8.7425-6.4 10.7738v-1.7063c2.976-1.885 4.944-5.2325 4.944-9.0675 0-5.915-4.72-10.7087-10.544-10.7087-5.824 0-10.544 4.7937-10.544 10.7087 0 3.835 1.968 7.1825 4.944 9.0675v1.7063c-3.792-2.0313-6.4-6.0938-6.4-10.7738C51 179.46 56.376 174 63 174s12 5.46 12 12.1875z"></path>
|
||||||
|
<path d="M830.7143 142.3333c-.8643 0-1.5714.7125-1.5714 1.5834v3.1666c0 .871.707 1.5834 1.5713 1.5834h12.5714c.8643 0 1.5714-.7125 1.5714-1.5834v-3.1666c0-.871-.707-1.5834-1.5713-1.5834h-12.5714zm12.5714 2.771l-1.9643 1.979h-2.357L837 145.1043l-1.9643 1.979h-2.357l-1.9644-1.979v-1.1876h1.1786l1.964 1.979 1.9644-1.979h2.3572l1.9643 1.979 1.964-1.979h1.1787v1.1875zm-9.4286 5.1457h6.286v1.5833h-6.286V150.25zM837 136c-6.0657 0-11 4.6075-11 10.2917v7.125c0 .8708.707 1.5833 1.5714 1.5833h18.8572c.8643 0 1.5714-.7125 1.5714-1.5833v-7.125C848 140.6075 843.0657 136 837 136zm9.4286 17.4167h-18.8572v-7.125c0-4.8925 4.1486-8.851 9.4286-8.851 5.28 0 9.4286 3.9585 9.4286 8.851v7.125z"></path>
|
||||||
|
<path d="M75 91.8065V96h4.1935L90.376 84.8174l-4.1934-4.1935L75 91.8064zm4.1935 2.7957h-2.7957v-2.7957h1.398v1.3978h1.3977v1.398zM93.591 81.6024l-1.817 1.817-4.1935-4.1934 1.817-1.817c.5453-.5453 1.426-.5453 1.971 0l2.2226 2.2224c.5453.5452.5453 1.4258 0 1.971z"></path>
|
||||||
|
<path d="M797 187h4v4h-4v-4zm12-1v19c0 1.1-.9 2-2 2h-20c-1.1 0-2-.9-2-2v-24c0-1.1.9-2 2-2h15l7 7zm-2 1l-6-6h-14v22l6-10 4 8 4-4 6 6v-16z"></path>
|
||||||
|
<path d="M138 125c-6.62 0-12 5-12 11 0 9.04 12 21 12 21s12-11.96 12-21c0-6-5.38-11-12-11zm0 29.1c-3.72-4.06-10-12.22-10-18.1 0-4.96 4.5-9 10-9 2.68 0 5.22.96 7.12 2.72 1.84 1.72 2.88 3.94 2.88 6.28 0 5.88-6.28 14.04-10 18.1zm4-18.1c0 2.22-1.78 4-4 4-2.22 0-4-1.78-4-4 0-2.22 1.78-4 4-4 2.22 0 4 1.78 4 4z"></path>
|
||||||
|
<path d="M771 82h8v2h-8v-2zm0 6h8v-2h-8v2zm0 4h8v-2h-8v2zm22-10h-8v2h8v-2zm0 4h-8v2h8v-2zm0 4h-8v2h8v-2zm4-12v18c0 1.1-.9 2-2 2h-11l-2 2-2-2h-11c-1.1 0-2-.9-2-2V78c0-1.1.9-2 2-2h11l2 2 2-2h11c1.1 0 2 .9 2 2zm-16 1l-1-1h-11v18h12V79zm14-1h-11l-1 1v17h12V78z"></path>
|
||||||
|
<path d="M176 203h-24c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4v7l7-7h13c1.1 0 2-.9 2-2v-16c0-1.1-.9-2-2-2zm0 18h-14l-4 4v-4h-6v-16h24v16z"></path>
|
||||||
|
<path d="M673 88.921c0 2.18-.9 4.18-2.34 5.66l-1.34-1.34c1.1-1.12 1.78-2.62 1.78-4.32 0-1.7-.68-3.22-1.78-4.32l1.34-1.34c1.44 1.44 2.34 3.44 2.34 5.66zm-8.56-11.48l-7.44 7.44h-4c-1.1 0-2 .9-2 2v4c0 1.1.9 2 2 2h4l7.44 7.44c.94.94 2.56.28 2.56-1.06v-20.76c0-1.34-1.62-2-2.56-1.06zm11.88.16l-1.34 1.34c2.56 2.56 4.12 6.06 4.12 9.96 0 3.88-1.56 7.4-4.12 9.96l1.34 1.34c2.9-2.9 4.68-6.9 4.68-11.32 0-4.44-1.78-8.44-4.68-11.32v.04zm-2.82 2.82l-1.38 1.34c1.84 1.84 2.96 4.38 2.96 7.16 0 2.78-1.12 5.32-2.96 7.12l1.38 1.34c2.16-2.16 3.5-5.16 3.5-8.46 0-3.3-1.34-6.32-3.5-8.5z"></path>
|
||||||
|
<path d="M226 79h-16c0-1.1-.9-2-2-2h-8c-1.1 0-2 .9-2 2-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h28c1.1 0 2-.9 2-2V81c0-1.1-.9-2-2-2zm-18 4h-8v-2h8v2zm9 14c-3.88 0-7-3.12-7-7s3.12-7 7-7 7 3.12 7 7-3.12 7-7 7zm5-7c0 2.76-2.26 5-5 5s-5-2.26-5-5 2.26-5 5-5 5 2.26 5 5z"></path>
|
||||||
|
<path d="M725.8393 157h-15.6498c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h15.6298C727 153 727 153.82 727 155c0 1.18 0 2-1.1807 2h.02zm-11.6473-10c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2h11.6273C727 143 727 143.82 727 145c0 1.18 0 2-1.1807 2H714.192zM695 146.82l2.8218-2.6 3.182 3.18 8.185-8.4 2.8218 2.82-11.0068 11-6.0038-6zM710.1895 163h15.6298C727 163 727 163.82 727 165c0 1.18 0 2-1.1807 2h-15.6298c-1.1807 0-1.1807-.82-1.1807-2 0-1.18 0-2 1.1807-2z"></path>
|
||||||
|
<path d="M223 152v24c0 1.65 1.35 3 3 3h36c1.65 0 3-1.35 3-3v-24c0-1.65-1.35-3-3-3h-36c-1.65 0-3 1.35-3 3zm39 0l-18 15-18-15h36zm-36 4.5l12 9-12 9v-18zm3 19.5l10.5-9 4.5 4.5 4.5-4.5 10.5 9h-30zm33-1.5l-12-9 12-9v18z"></path>
|
||||||
|
<path d="M648 182h-3v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-9v4.5c0 .84-.66 1.5-1.5 1.5h-6c-.84 0-1.5-.66-1.5-1.5V182h-3c-1.65 0-3 1.35-3 3v33c0 1.65 1.35 3 3 3h33c1.65 0 3-1.35 3-3v-33c0-1.65-1.35-3-3-3zm0 36h-33v-27h33v27zm-24-33h-3v-6h3v6zm18 0h-3v-6h3v6zm-15 12h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm-24 6h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3zm6 0h-3v-3h3v3z"></path>
|
||||||
|
</g>
|
||||||
|
<g class="hero-icons" style="fill: #c2f5ff;">
|
||||||
|
<path d="M441.1132 69.724c7.681 0 13.9075-6.207 13.9075-13.8636 0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9076 6.207-13.9076 13.8634 0 7.6566 6.2266 13.8635 13.9076 13.8635zm0-5.7932c-4.4713 0-8.096-3.6132-8.096-8.0704 0-4.457 3.6247-8.0703 8.096-8.0703 4.4712 0 8.096 3.6133 8.096 8.0704 0 4.4572-3.6248 8.0704-8.096 8.0704z"></path>
|
||||||
|
<path d="M354.8995 220.2693c7.681 0 13.9075-6.207 13.9075-13.8635s-6.2266-13.8634-13.9075-13.8634c-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635zm0-5.793c-4.4713 0-8.096-3.6133-8.096-8.0705 0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703c0 4.4572-3.6247 8.0704-8.096 8.0704z"></path>
|
||||||
|
<path d="M541.0343 206.4058c0-7.6565-6.2266-13.8634-13.9075-13.8634-7.681 0-13.9075 6.207-13.9075 13.8634 0 7.6566 6.2266 13.8635 13.9075 13.8635 7.681 0 13.9075-6.207 13.9075-13.8635zm-5.8115 0c0 4.4572-3.6247 8.0704-8.096 8.0704s-8.096-3.6132-8.096-8.0704c0-4.457 3.6247-8.0703 8.096-8.0703s8.096 3.6132 8.096 8.0703z"></path>
|
||||||
|
<path d="M397.6943 214.5258c9.7012 27.0033 25.5723 43.629 43.419 43.629 13.0157 0 25.0578-8.8443 34.4482-24.4154.827-1.371.3822-3.1507-.9932-3.975-1.3755-.824-3.1607-.3808-3.9876.9902-8.439 13.9938-18.8052 21.6072-29.4675 21.6072-14.8247 0-28.9803-14.8288-37.9476-39.7892-.541-1.506-2.2044-2.2897-3.7153-1.7504-1.511.5394-2.297 2.1975-1.756 3.7036z"></path>
|
||||||
|
<path d="M514.124 163.4733c18.5545-21.85 25.033-43.826 16.122-59.2117-6.557-11.321-20.419-17.2982-38.841-17.537-1.6047-.021-2.9225 1.259-2.9434 2.8586-.0208 1.5996 1.263 2.9132 2.8678 2.934 16.5683.2148 28.5106 5.3642 33.8836 14.641 7.4018 12.7797 1.6243 32.3774-15.5247 52.5722-1.037 1.221-.8844 3.0487.3405 4.0822 1.2248 1.0336 3.0584.8817 4.0952-.3393z"></path>
|
||||||
|
<path d="M411.5672 88.457c-28.3373-5.1448-50.7424.24-59.672 15.6575-6.6635 11.505-4.7588 26.7585 4.6193 43.0637.7982 1.3878 2.574 1.8678 3.966 1.072 1.3923-.7956 1.874-2.5656 1.0756-3.9534-8.4477-14.688-10.0915-27.8524-4.628-37.2857 7.418-12.8074 27.403-17.6105 53.5978-12.8546 1.579.2866 3.092-.7568 3.3794-2.3307.2876-1.5738-.7592-3.082-2.338-3.3687z"></path>
|
||||||
|
<path d="M486.3075 209.2436c5.022-15.998 7.7194-34.453 7.7194-53.6842 0-47.9875-16.849-89.3545-40.8478-99.977-1.4667-.649-3.1837.0098-3.835 1.472-.6512 1.462.01 3.1735 1.4766 3.8227 21.404 9.474 37.3945 48.7337 37.3945 94.6824 0 18.6574-2.612 36.5297-7.454 51.954-.4794 1.5268.3736 3.1518 1.9052 3.6295s3.1617-.3727 3.641-1.8994z"></path>
|
||||||
|
<path d="M466.439 89.4215c-16.7763 3.583-34.6332 10.5886-51.7827 20.4585-42.434 24.4216-70.1147 60.4323-66.2703 86.5432.233 1.5828 1.709 2.6776 3.297 2.4453 1.5877-.2323 2.686-1.7037 2.453-3.2865-3.4135-23.1838 22.825-57.3183 63.426-80.685 16.6365-9.5746 33.9267-16.3578 50.0946-19.811 1.5692-.335 2.5687-1.8748 2.2325-3.439-.336-1.5642-1.8807-2.5606-3.45-2.2255z"></path>
|
||||||
|
<path d="M371.2508 166.997c11.458 12.5516 26.3438 24.3243 43.3203 34.0947 41.106 23.6572 84.866 29.9805 106.4328 15.3217 1.326-.9013 1.668-2.7033.7638-4.025-.904-1.3217-2.712-1.6626-4.0378-.7614-19.302 13.1195-60.871 7.1128-100.253-15.5523-16.469-9.4783-30.8834-20.8782-41.9277-32.9767-1.08-1.1832-2.9178-1.2695-4.1048-.1928-1.187 1.0766-1.2735 2.9086-.1934 4.0918z"></path>
|
||||||
|
<path d="M443.2374 165.3634c-5.432 1.17-10.7838-2.2712-11.9598-7.686-1.1714-5.415 2.2785-10.7498 7.7106-11.922 5.432-1.17 10.7838 2.2712 11.9598 7.686 1.1737 5.415-2.2785 10.7498-7.7106 11.922z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.8 KiB |
206
src/renderer/src/assets/index.css
Normal file
206
src/renderer/src/assets/index.css
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.code-toolbar {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-to-clipboard-button {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
background-color: #011727;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #4891f7;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--shiki-color-text: oklch(37.53% 0 0);
|
||||||
|
--shiki-color-background: hsl(var(--primary-hue) 100% 39% / 5%);
|
||||||
|
--shiki-token-constant: oklch(56.45% 0.163 253.27);
|
||||||
|
--shiki-token-string: oklch(54.64% 0.144 147.32);
|
||||||
|
--shiki-token-comment: oklch(73.8% 0 0);
|
||||||
|
--shiki-token-keyword: oklch(56.8% 0.2 26.41);
|
||||||
|
--shiki-token-parameter: oklch(77.03% 0.174 64.05);
|
||||||
|
--shiki-token-function: oklch(50.15% 0.188 294.99);
|
||||||
|
--shiki-token-string-expression: var(--shiki-token-string);
|
||||||
|
--shiki-token-punctuation: oklch(24.78% 0 0);
|
||||||
|
--shiki-token-link: var(--shiki-token-string);
|
||||||
|
|
||||||
|
/* from github-light */
|
||||||
|
--shiki-color-ansi-black: #24292e;
|
||||||
|
--shiki-color-ansi-black-dim: #24292e80;
|
||||||
|
--shiki-color-ansi-red: #d73a49;
|
||||||
|
--shiki-color-ansi-red-dim: #d73a4980;
|
||||||
|
--shiki-color-ansi-green: #28a745;
|
||||||
|
--shiki-color-ansi-green-dim: #28a74580;
|
||||||
|
--shiki-color-ansi-yellow: #dbab09;
|
||||||
|
--shiki-color-ansi-yellow-dim: #dbab0980;
|
||||||
|
--shiki-color-ansi-blue: #0366d6;
|
||||||
|
--shiki-color-ansi-blue-dim: #0366d680;
|
||||||
|
--shiki-color-ansi-magenta: #5a32a3;
|
||||||
|
--shiki-color-ansi-magenta-dim: #5a32a380;
|
||||||
|
--shiki-color-ansi-cyan: #1b7c83;
|
||||||
|
--shiki-color-ansi-cyan-dim: #1b7c8380;
|
||||||
|
--shiki-color-ansi-white: #6a737d;
|
||||||
|
--shiki-color-ansi-white-dim: #6a737d80;
|
||||||
|
--shiki-color-ansi-bright-black: #959da5;
|
||||||
|
--shiki-color-ansi-bright-black-dim: #959da580;
|
||||||
|
--shiki-color-ansi-bright-red: #cb2431;
|
||||||
|
--shiki-color-ansi-bright-red-dim: #cb243180;
|
||||||
|
--shiki-color-ansi-bright-green: #22863a;
|
||||||
|
--shiki-color-ansi-bright-green-dim: #22863a80;
|
||||||
|
--shiki-color-ansi-bright-yellow: #b08800;
|
||||||
|
--shiki-color-ansi-bright-yellow-dim: #b0880080;
|
||||||
|
--shiki-color-ansi-bright-blue: #005cc5;
|
||||||
|
--shiki-color-ansi-bright-blue-dim: #005cc580;
|
||||||
|
--shiki-color-ansi-bright-magenta: #5a32a3;
|
||||||
|
--shiki-color-ansi-bright-magenta-dim: #5a32a380;
|
||||||
|
--shiki-color-ansi-bright-cyan: #3192aa;
|
||||||
|
--shiki-color-ansi-bright-cyan-dim: #3192aa80;
|
||||||
|
--shiki-color-ansi-bright-white: #d1d5da;
|
||||||
|
--shiki-color-ansi-bright-white-dim: #d1d5da80;
|
||||||
|
--primary-hue: 212deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode {
|
||||||
|
--primary-hue: 204deg;
|
||||||
|
--shiki-color-background: hsl(var(--primary-hue) 100% 77% / 10%);
|
||||||
|
--shiki-color-text: oklch(86.07% 0 0);
|
||||||
|
--shiki-token-constant: oklch(76.85% 0.121 252.34);
|
||||||
|
--shiki-token-string: oklch(81.11% 0.124 55.08);
|
||||||
|
--shiki-token-comment: oklch(55.18% 0.017 251.27);
|
||||||
|
--shiki-token-keyword: oklch(72.14% 0.162 15.49);
|
||||||
|
|
||||||
|
/* --shiki-token-parameter: #ff9800; is same as in light mode */
|
||||||
|
--shiki-token-function: oklch(72.67% 0.137 299.15);
|
||||||
|
--shiki-token-string-expression: oklch(69.28% 0.179 143.2);
|
||||||
|
--shiki-token-punctuation: oklch(79.21% 0 0);
|
||||||
|
--shiki-token-link: var(--shiki-token-string);
|
||||||
|
|
||||||
|
/* from github-dark */
|
||||||
|
--shiki-color-ansi-black: #586069;
|
||||||
|
--shiki-color-ansi-black-dim: #58606980;
|
||||||
|
--shiki-color-ansi-red: #ea4a5a;
|
||||||
|
--shiki-color-ansi-red-dim: #ea4a5a80;
|
||||||
|
--shiki-color-ansi-green: #34d058;
|
||||||
|
--shiki-color-ansi-green-dim: #34d05880;
|
||||||
|
--shiki-color-ansi-yellow: #ffea7f;
|
||||||
|
--shiki-color-ansi-yellow-dim: #ffea7f80;
|
||||||
|
--shiki-color-ansi-blue: #2188ff;
|
||||||
|
--shiki-color-ansi-blue-dim: #2188ff80;
|
||||||
|
--shiki-color-ansi-magenta: #b392f0;
|
||||||
|
--shiki-color-ansi-magenta-dim: #b392f080;
|
||||||
|
--shiki-color-ansi-cyan: #39c5cf;
|
||||||
|
--shiki-color-ansi-cyan-dim: #39c5cf80;
|
||||||
|
--shiki-color-ansi-white: #d1d5da;
|
||||||
|
--shiki-color-ansi-white-dim: #d1d5da80;
|
||||||
|
--shiki-color-ansi-bright-black: #959da5;
|
||||||
|
--shiki-color-ansi-bright-black-dim: #959da580;
|
||||||
|
--shiki-color-ansi-bright-red: #f97583;
|
||||||
|
--shiki-color-ansi-bright-red-dim: #f9758380;
|
||||||
|
--shiki-color-ansi-bright-green: #85e89d;
|
||||||
|
--shiki-color-ansi-bright-green-dim: #85e89d80;
|
||||||
|
--shiki-color-ansi-bright-yellow: #ffea7f;
|
||||||
|
--shiki-color-ansi-bright-yellow-dim: #ffea7f80;
|
||||||
|
--shiki-color-ansi-bright-blue: #79b8ff;
|
||||||
|
--shiki-color-ansi-bright-blue-dim: #79b8ff80;
|
||||||
|
--shiki-color-ansi-bright-magenta: #b392f0;
|
||||||
|
--shiki-color-ansi-bright-magenta-dim: #b392f080;
|
||||||
|
--shiki-color-ansi-bright-cyan: #56d4dd;
|
||||||
|
--shiki-color-ansi-bright-cyan-dim: #56d4dd80;
|
||||||
|
--shiki-color-ansi-bright-white: #fafbfc;
|
||||||
|
--shiki-color-ansi-bright-white-dim: #fafbfc80;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||||
|
counter-reset: line;
|
||||||
|
font-feature-settings: 'rlig' 1, 'calt' 1, 'ss01' 1;
|
||||||
|
box-decoration-break: slice;
|
||||||
|
|
||||||
|
&[data-line-numbers] > [data-line] {
|
||||||
|
padding-inline: 1rem;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: counter(line);
|
||||||
|
counter-increment: line;
|
||||||
|
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
padding-right: 1rem;
|
||||||
|
|
||||||
|
color: #6b7280;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-nextra-word-wrap] & {
|
||||||
|
word-break: break-word;
|
||||||
|
|
||||||
|
@apply whitespace-pre-wrap md:whitespace-pre;
|
||||||
|
|
||||||
|
[data-line] {
|
||||||
|
@apply inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nextra-copy-icon {
|
||||||
|
animation: fade-in 0.3s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
|
||||||
|
.nextra-button {
|
||||||
|
@apply backdrop-blur-md bg-opacity-[.85] dark:bg-opacity-80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ordered-list {
|
||||||
|
padding-left: 20px;
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unordered-list {
|
||||||
|
padding-left: 20px;
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paragraph {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: blue;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
16
src/renderer/src/components/Versions.tsx
Normal file
16
src/renderer/src/components/Versions.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
function Versions(): JSX.Element {
|
||||||
|
const [versions] = useState(window.electron.process.versions)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="versions">
|
||||||
|
<li className="electron-version">Electron v{versions.electron}</li>
|
||||||
|
<li className="chrome-version">Chromium v{versions.chrome}</li>
|
||||||
|
<li className="node-version">Node v{versions.node}</li>
|
||||||
|
<li className="v8-version">V8 v{versions.v8}</li>
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Versions
|
||||||
13
src/renderer/src/components/pre/check.tsx
Normal file
13
src/renderer/src/components/pre/check.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import type { ComponentProps, ReactElement } from 'react'
|
||||||
|
|
||||||
|
export function CheckIcon(props: ComponentProps<'svg'>): ReactElement {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 20 20" width="1em" height="1em" fill="currentColor" {...props}>
|
||||||
|
<path
|
||||||
|
fillRule="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"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
.root {
|
||||||
|
pointer-events: none;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
all: unset;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
48
src/renderer/src/components/pre/copy-to-clipboard.tsx
Normal file
48
src/renderer/src/components/pre/copy-to-clipboard.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import type { ComponentProps, ReactElement } from 'react'
|
||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
import { CheckIcon } from './check'
|
||||||
|
import { CopyIcon } from './copy'
|
||||||
|
import s from './copy-to-clipboard.module.scss'
|
||||||
|
|
||||||
|
export const CopyToClipboard = ({
|
||||||
|
getValue,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
getValue: () => string
|
||||||
|
} & ComponentProps<'button'>): ReactElement => {
|
||||||
|
const [isCopied, setCopied] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isCopied) return
|
||||||
|
const timerId = setTimeout(() => {
|
||||||
|
setCopied(false)
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timerId)
|
||||||
|
}
|
||||||
|
}, [isCopied])
|
||||||
|
|
||||||
|
const handleClick = useCallback<NonNullable<ComponentProps<'button'>['onClick']>>(async () => {
|
||||||
|
setCopied(true)
|
||||||
|
if (!navigator?.clipboard) {
|
||||||
|
console.error('Access to clipboard rejected!')
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(getValue())
|
||||||
|
} catch {
|
||||||
|
console.error('Failed to copy!')
|
||||||
|
}
|
||||||
|
}, [getValue])
|
||||||
|
|
||||||
|
const IconToUse = isCopied ? CheckIcon : CopyIcon
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button onClick={handleClick} className={s.button} title="Copy code" tabIndex={0} {...props}>
|
||||||
|
<IconToUse className={clsx('nextra-copy-icon', s.root)} />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
32
src/renderer/src/components/pre/copy.tsx
Normal file
32
src/renderer/src/components/pre/copy.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import type { ComponentProps, ReactElement } from 'react'
|
||||||
|
|
||||||
|
export function CopyIcon(props: ComponentProps<'svg'>): ReactElement {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
stroke="currentColor"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
x="9"
|
||||||
|
y="9"
|
||||||
|
width="13"
|
||||||
|
height="13"
|
||||||
|
rx="2"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<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"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
1
src/renderer/src/components/pre/index.ts
Normal file
1
src/renderer/src/components/pre/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './pre'
|
||||||
80
src/renderer/src/components/pre/pre.module.scss
Normal file
80
src/renderer/src/components/pre/pre.module.scss
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
.codeBlock {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filename {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
background-color: hsl(var(--primary-hue) 100% 39% / 5%);
|
||||||
|
border-radius: 0.375rem 0.375rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preCommon {
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preWithFilename {
|
||||||
|
padding-top: 3rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preWithoutFilename {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controlDiv {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
|
||||||
|
margin: 11px 0 0 -11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top8 {
|
||||||
|
top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top0 {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdHidden {
|
||||||
|
@media (width >= 768px) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconDimensions {
|
||||||
|
pointer-events: none;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
42
src/renderer/src/components/pre/pre.tsx
Normal file
42
src/renderer/src/components/pre/pre.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { ComponentProps, ReactElement, useRef } from 'react'
|
||||||
|
|
||||||
|
import { Scrollbar } from '@it-incubator/ui-kit'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
import { CopyToClipboard } from './copy-to-clipboard'
|
||||||
|
import styles from './pre.module.scss'
|
||||||
|
export const Pre = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
filename,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<'pre'> & {
|
||||||
|
filename?: string
|
||||||
|
hasCopyCode?: boolean
|
||||||
|
}): ReactElement => {
|
||||||
|
const preRef = useRef<HTMLPreElement | null>(null)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Scrollbar className={styles.codeBlock} type={'hover'}>
|
||||||
|
{filename && (
|
||||||
|
<div className={styles.filename}>
|
||||||
|
{filename}
|
||||||
|
<CopyToClipboard
|
||||||
|
getValue={() => preRef.current?.querySelector('code')?.textContent || ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<pre
|
||||||
|
className={clsx(
|
||||||
|
styles.preCommon,
|
||||||
|
filename ? styles.preWithFilename : styles.preWithoutFilename,
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={preRef}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</pre>
|
||||||
|
</Scrollbar>
|
||||||
|
)
|
||||||
|
}
|
||||||
1
src/renderer/src/env.d.ts
vendored
Normal file
1
src/renderer/src/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
14
src/renderer/src/main.tsx
Normal file
14
src/renderer/src/main.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom/client'
|
||||||
|
import '@fontsource/roboto/400.css'
|
||||||
|
import '@fontsource/roboto/500.css'
|
||||||
|
import '@fontsource/roboto/700.css'
|
||||||
|
import '@it-incubator/ui-kit/dist/style.css'
|
||||||
|
import './assets/index.css'
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
)
|
||||||
205
src/renderer/src/view.module.scss
Normal file
205
src/renderer/src/view.module.scss
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
.page {
|
||||||
|
width: 100%;
|
||||||
|
padding: 22px 0 43px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 200px minmax(65ch, 100%) 190px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
background-color: var(--color-light-mode-100);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
|
||||||
|
:global(.dark-mode) & {
|
||||||
|
background-color: var(--color-dark-mode-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
border-right: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
border-left: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.root {
|
||||||
|
overflow: auto;
|
||||||
|
padding: 31px 24px;
|
||||||
|
|
||||||
|
:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& h1 {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
|
||||||
|
font-size: 2.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 2.5rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
|
||||||
|
font-size: 1.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 2.25rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 2rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h4 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h5 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
font-size: 1.125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& h6 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& ul {
|
||||||
|
margin: 1.5rem 0 0 1.5rem;
|
||||||
|
list-style-type: disc;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& ol {
|
||||||
|
margin: 1.5rem 0 0 1.5rem;
|
||||||
|
list-style-type: decimal;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& li {
|
||||||
|
margin-inline: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& blockquote {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
|
||||||
|
font-style: italic;
|
||||||
|
color: #374151;
|
||||||
|
|
||||||
|
border-left: 4px solid #d1d5db;
|
||||||
|
|
||||||
|
:global(.dark-mode) & {
|
||||||
|
color: rgb(156 163 175);
|
||||||
|
border-color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& hr {
|
||||||
|
margin-block: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& code {
|
||||||
|
padding: 0.125rem 0.25em;
|
||||||
|
|
||||||
|
font-size: 0.9em;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
|
background-color: rgb(0 0 0 / 3%);
|
||||||
|
border: 1px solid rgb(0 0 0 / 4%);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& pre {
|
||||||
|
contain: paint;
|
||||||
|
|
||||||
|
& code {
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
min-width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
color: currentcolor;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
border-style: none;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
[data-line] {
|
||||||
|
padding-inline: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not([data-theme]) {
|
||||||
|
@apply px-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark-mode) & code {
|
||||||
|
background-color: rgb(255 255 255 / 6%);
|
||||||
|
border: 1px solid rgb(255 255 255 / 7%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.dark-mode) & pre code {
|
||||||
|
background-color: transparent;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/renderer/src/view.tsx
Normal file
3
src/renderer/src/view.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const View = ({ code }: { code: string }) => {
|
||||||
|
return <div>View</div>
|
||||||
|
}
|
||||||
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }]
|
||||||
|
}
|
||||||
8
tsconfig.node.json
Normal file
8
tsconfig.node.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||||
|
"include": ["electron.vite.config.*", "src/main/*", "src/preload/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"types": ["electron-vite/node"]
|
||||||
|
}
|
||||||
|
}
|
||||||
19
tsconfig.web.json
Normal file
19
tsconfig.web.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
||||||
|
"include": [
|
||||||
|
"src/renderer/src/env.d.ts",
|
||||||
|
"src/renderer/src/**/*",
|
||||||
|
"src/renderer/src/**/*.tsx",
|
||||||
|
"src/preload/*.d.ts"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@renderer/*": [
|
||||||
|
"src/renderer/src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user