mirror of
https://github.com/ershisan99/md-preview-desktop.git
synced 2025-12-18 12:33:43 +00:00
chore: replace local components with mdx-components lib
feat: directory selection working feat: theme toggle working
This commit is contained in:
@@ -24,10 +24,11 @@
|
|||||||
"@electron-toolkit/utils": "^2.0.0",
|
"@electron-toolkit/utils": "^2.0.0",
|
||||||
"@fontsource/roboto": "^5.0.8",
|
"@fontsource/roboto": "^5.0.8",
|
||||||
"@it-incubator/md-bundler": "0.0.8",
|
"@it-incubator/md-bundler": "0.0.8",
|
||||||
"@it-incubator/mdx-components": "0.0.3",
|
"@it-incubator/mdx-components": "0.0.4",
|
||||||
"@it-incubator/ui-kit": "0.2.17",
|
"@it-incubator/ui-kit": "0.2.18",
|
||||||
"builtin-modules": "^3.3.0",
|
"builtin-modules": "^3.3.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
|
"electron-store": "^8.1.0",
|
||||||
"electron-updater": "^6.1.1",
|
"electron-updater": "^6.1.1",
|
||||||
"esbuild": "^0.19.3",
|
"esbuild": "^0.19.3",
|
||||||
"mdx-bundler": "^9.2.1",
|
"mdx-bundler": "^9.2.1",
|
||||||
|
|||||||
131
pnpm-lock.yaml
generated
131
pnpm-lock.yaml
generated
@@ -18,17 +18,20 @@ dependencies:
|
|||||||
specifier: 0.0.8
|
specifier: 0.0.8
|
||||||
version: 0.0.8
|
version: 0.0.8
|
||||||
'@it-incubator/mdx-components':
|
'@it-incubator/mdx-components':
|
||||||
specifier: 0.0.3
|
specifier: 0.0.4
|
||||||
version: 0.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(esbuild@0.19.3)(react-dom@18.2.0)(react@18.2.0)
|
version: 0.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(esbuild@0.19.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@it-incubator/ui-kit':
|
'@it-incubator/ui-kit':
|
||||||
specifier: 0.2.17
|
specifier: 0.2.18
|
||||||
version: 0.2.17(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react-toastify@9.1.3)(react@18.2.0)
|
version: 0.2.18(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react-toastify@9.1.3)(react@18.2.0)
|
||||||
builtin-modules:
|
builtin-modules:
|
||||||
specifier: ^3.3.0
|
specifier: ^3.3.0
|
||||||
version: 3.3.0
|
version: 3.3.0
|
||||||
chokidar:
|
chokidar:
|
||||||
specifier: ^3.5.3
|
specifier: ^3.5.3
|
||||||
version: 3.5.3
|
version: 3.5.3
|
||||||
|
electron-store:
|
||||||
|
specifier: ^8.1.0
|
||||||
|
version: 8.1.0
|
||||||
electron-updater:
|
electron-updater:
|
||||||
specifier: ^6.1.1
|
specifier: ^6.1.1
|
||||||
version: 6.1.4
|
version: 6.1.4
|
||||||
@@ -1224,8 +1227,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@it-incubator/mdx-components@0.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(esbuild@0.19.3)(react-dom@18.2.0)(react@18.2.0):
|
/@it-incubator/mdx-components@0.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(esbuild@0.19.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-S8L7MtXkgPgTCclmMxgeHUxk/6a3W6cbzZX9Q4mXfvnBaSVh2SJrVqq9YdxQQkEK+0q2Vu04BoTLyruD5ewunw==}
|
resolution: {integrity: sha512-GEqGlg/5u9Tu1xahabRMEKv2g/FxMEhWhMmdIYclJDCqTJfguUkmIWeYjKgJVgJFqxeuJQNpzd6D850RuviEJQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=18.0.2'
|
react: '>=18.0.2'
|
||||||
react-dom: '>=18.0.2'
|
react-dom: '>=18.0.2'
|
||||||
@@ -1269,8 +1272,8 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@it-incubator/ui-kit@0.2.17(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react-toastify@9.1.3)(react@18.2.0):
|
/@it-incubator/ui-kit@0.2.18(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react-toastify@9.1.3)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-x3V3dqh2HY/wJZJ8hhO0c+5C0hiUxz/dhdkWoaDrqqaiAUGb+laj7uzh8SFJ7noWwXT4uFWX57cPnlzvdUqieA==}
|
resolution: {integrity: sha512-xmDIEKCfkiBFVg+k1k5065Cw08/9fL39LkdgzyJ7nSbYENVW4gLw1m57jOuh0PvzzVOk1qTkF869Z9rgbPfm6Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=18.0.2'
|
react: '>=18.0.2'
|
||||||
react-dom: '>=18.0.2'
|
react-dom: '>=18.0.2'
|
||||||
@@ -2943,6 +2946,17 @@ packages:
|
|||||||
indent-string: 4.0.0
|
indent-string: 4.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/ajv-formats@2.1.1(ajv@8.12.0):
|
||||||
|
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||||
|
peerDependencies:
|
||||||
|
ajv: ^8.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ajv:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
ajv: 8.12.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ajv-keywords@3.5.2(ajv@6.12.6):
|
/ajv-keywords@3.5.2(ajv@6.12.6):
|
||||||
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
|
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2967,7 +2981,6 @@ packages:
|
|||||||
json-schema-traverse: 1.0.0
|
json-schema-traverse: 1.0.0
|
||||||
require-from-string: 2.0.2
|
require-from-string: 2.0.2
|
||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/ansi-escapes@4.3.2:
|
/ansi-escapes@4.3.2:
|
||||||
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
||||||
@@ -3187,6 +3200,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|
||||||
|
/atomically@1.7.0:
|
||||||
|
resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==}
|
||||||
|
engines: {node: '>=10.12.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/attr-accept@2.2.2:
|
/attr-accept@2.2.2:
|
||||||
resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==}
|
resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -3658,6 +3676,22 @@ packages:
|
|||||||
typedarray: 0.0.6
|
typedarray: 0.0.6
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/conf@10.2.0:
|
||||||
|
resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
ajv: 8.12.0
|
||||||
|
ajv-formats: 2.1.1(ajv@8.12.0)
|
||||||
|
atomically: 1.7.0
|
||||||
|
debounce-fn: 4.0.0
|
||||||
|
dot-prop: 6.0.1
|
||||||
|
env-paths: 2.2.1
|
||||||
|
json-schema-typed: 7.0.3
|
||||||
|
onetime: 5.1.2
|
||||||
|
pkg-up: 3.1.0
|
||||||
|
semver: 7.5.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/config-file-ts@0.2.4:
|
/config-file-ts@0.2.4:
|
||||||
resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==}
|
resolution: {integrity: sha512-cKSW0BfrSaAUnxpgvpXPLaaW/umg4bqg4k3GO1JqlRfpx+d5W0GDXznCMkWotJQek5Mmz1MJVChQnz3IVaeMZQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3766,6 +3800,13 @@ packages:
|
|||||||
'@babel/runtime': 7.22.15
|
'@babel/runtime': 7.22.15
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/debounce-fn@4.0.0:
|
||||||
|
resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
mimic-fn: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/debounce@1.2.1:
|
/debounce@1.2.1:
|
||||||
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -3983,6 +4024,13 @@ packages:
|
|||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dot-prop@6.0.1:
|
||||||
|
resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
is-obj: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/dotenv-expand@5.1.0:
|
/dotenv-expand@5.1.0:
|
||||||
resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==}
|
resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -4034,6 +4082,13 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/electron-store@8.1.0:
|
||||||
|
resolution: {integrity: sha512-2clHg/juMjOH0GT9cQ6qtmIvK183B39ZXR0bUoPwKwYHJsEF3quqyDzMFUAu+0OP8ijmN2CbPRAelhNbWUbzwA==}
|
||||||
|
dependencies:
|
||||||
|
conf: 10.2.0
|
||||||
|
type-fest: 2.19.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/electron-to-chromium@1.4.523:
|
/electron-to-chromium@1.4.523:
|
||||||
resolution: {integrity: sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg==}
|
resolution: {integrity: sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg==}
|
||||||
|
|
||||||
@@ -4723,7 +4778,6 @@ packages:
|
|||||||
|
|
||||||
/fast-deep-equal@3.1.3:
|
/fast-deep-equal@3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fast-diff@1.3.0:
|
/fast-diff@1.3.0:
|
||||||
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
|
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
|
||||||
@@ -4815,6 +4869,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-wRkO8crYqjaTvCnqEfQGuV8LOp4JO0Ctjn6qROGPcradK+6jQ7giLMGLnKlNxQm6dEdYD3/TBABQ7Xi/5ZhWcg==}
|
resolution: {integrity: sha512-wRkO8crYqjaTvCnqEfQGuV8LOp4JO0Ctjn6qROGPcradK+6jQ7giLMGLnKlNxQm6dEdYD3/TBABQ7Xi/5ZhWcg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/find-up@3.0.0:
|
||||||
|
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
locate-path: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/find-up@4.1.0:
|
/find-up@4.1.0:
|
||||||
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -5770,6 +5831,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
|
/is-obj@2.0.0:
|
||||||
|
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-obj@3.0.0:
|
/is-obj@3.0.0:
|
||||||
resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==}
|
resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -5970,7 +6036,10 @@ packages:
|
|||||||
|
|
||||||
/json-schema-traverse@1.0.0:
|
/json-schema-traverse@1.0.0:
|
||||||
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
|
||||||
dev: true
|
|
||||||
|
/json-schema-typed@7.0.3:
|
||||||
|
resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/json-stable-stringify-without-jsonify@1.0.1:
|
/json-stable-stringify-without-jsonify@1.0.1:
|
||||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||||
@@ -6064,6 +6133,14 @@ packages:
|
|||||||
/lines-and-columns@1.2.4:
|
/lines-and-columns@1.2.4:
|
||||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||||
|
|
||||||
|
/locate-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
p-locate: 3.0.0
|
||||||
|
path-exists: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/locate-path@5.0.0:
|
/locate-path@5.0.0:
|
||||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -7090,6 +7167,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
/mimic-fn@3.1.0:
|
||||||
|
resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/mimic-fn@4.0.0:
|
/mimic-fn@4.0.0:
|
||||||
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -7365,6 +7447,13 @@ packages:
|
|||||||
yocto-queue: 0.1.0
|
yocto-queue: 0.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/p-locate@3.0.0:
|
||||||
|
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
p-limit: 2.3.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-locate@4.1.0:
|
/p-locate@4.1.0:
|
||||||
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -7440,6 +7529,11 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/path-exists@3.0.0:
|
||||||
|
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/path-exists@4.0.0:
|
/path-exists@4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -7495,6 +7589,13 @@ packages:
|
|||||||
find-up: 4.1.0
|
find-up: 4.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/pkg-up@3.1.0:
|
||||||
|
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
find-up: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/plist@3.1.0:
|
/plist@3.1.0:
|
||||||
resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
|
resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
|
||||||
engines: {node: '>=10.4.0'}
|
engines: {node: '>=10.4.0'}
|
||||||
@@ -7642,7 +7743,6 @@ packages:
|
|||||||
/punycode@2.3.0:
|
/punycode@2.3.0:
|
||||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/querystring@0.2.0:
|
/querystring@0.2.0:
|
||||||
resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
|
resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
|
||||||
@@ -8046,7 +8146,6 @@ packages:
|
|||||||
/require-from-string@2.0.2:
|
/require-from-string@2.0.2:
|
||||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/resolve-alpn@1.2.1:
|
/resolve-alpn@1.2.1:
|
||||||
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||||
@@ -8898,6 +8997,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
|
resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
/type-fest@2.19.0:
|
||||||
|
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
||||||
|
engines: {node: '>=12.20'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/typed-array-buffer@1.0.0:
|
/typed-array-buffer@1.0.0:
|
||||||
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
|
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -9110,7 +9214,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.0
|
punycode: 2.3.0
|
||||||
dev: true
|
|
||||||
|
|
||||||
/url@0.10.3:
|
/url@0.10.3:
|
||||||
resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==}
|
resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export const CODE_BLOCK_FILENAME_REGEX = /filename="([^"]+)"/
|
|
||||||
@@ -3,11 +3,14 @@ import path from 'node:path'
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
import { electronApp, is, optimizer } from '@electron-toolkit/utils'
|
||||||
import { bundleMdx } from '@it-incubator/md-bundler'
|
import { bundleMdx, generateToc } from '@it-incubator/md-bundler'
|
||||||
import { BrowserWindow, app, ipcMain, shell } from 'electron'
|
import { BrowserWindow, app, ipcMain, shell } from 'electron'
|
||||||
|
import Store from 'electron-store'
|
||||||
|
|
||||||
import icon from '../../resources/icon.png?asset'
|
import icon from '../../resources/icon.png?asset'
|
||||||
|
|
||||||
const chokidar = require('chokidar')
|
const chokidar = require('chokidar')
|
||||||
|
const store = new Store()
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null
|
let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
@@ -35,6 +38,11 @@ function createWindow(): void {
|
|||||||
return { action: 'deny' }
|
return { action: 'deny' }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mainWindow.webContents.on('will-navigate', (event, url) => {
|
||||||
|
event.preventDefault()
|
||||||
|
shell.openExternal(url)
|
||||||
|
})
|
||||||
|
|
||||||
// HMR for renderer base on electron-vite cli.
|
// HMR for renderer base on electron-vite cli.
|
||||||
// Load the remote URL for development or the local html file for production.
|
// Load the remote URL for development or the local html file for production.
|
||||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||||
@@ -79,8 +87,10 @@ app.on('window-all-closed', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let watcher: any = null
|
let watcher: any = null
|
||||||
|
let currentContent: any = null
|
||||||
|
|
||||||
function setupWatcher(filePath: string) {
|
function setupWatcher(filePath: string) {
|
||||||
|
store.set('lastFilePath', filePath)
|
||||||
// Close the existing watcher if it exists
|
// Close the existing watcher if it exists
|
||||||
if (watcher) {
|
if (watcher) {
|
||||||
watcher.close()
|
watcher.close()
|
||||||
@@ -101,13 +111,18 @@ function setupWatcher(filePath: string) {
|
|||||||
// 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 = await generateToc(content, {})
|
||||||
|
const newContent = { ...bundled, fileName: path, toc }
|
||||||
|
|
||||||
mainWindow.webContents.send('file-changed', { ...bundled, fileName: path })
|
currentContent = newContent
|
||||||
|
|
||||||
|
mainWindow.webContents.send('current-content', newContent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await shell.openPath(path)
|
// await shell.openPath(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bundleAndSend(filePath)
|
||||||
// Add your event listeners
|
// Add your event listeners
|
||||||
watcher
|
watcher
|
||||||
.on('add', async (path: string) => {
|
.on('add', async (path: string) => {
|
||||||
@@ -119,15 +134,147 @@ function setupWatcher(filePath: string) {
|
|||||||
.on('unlink', (path: string) => console.warn(`File ${path} has been removed`))
|
.on('unlink', (path: string) => console.warn(`File ${path} has been removed`))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareAndSendDir(dir: string) {
|
||||||
|
const files = fs.readdirSync(dir)
|
||||||
|
const dirName = path.basename(dir)
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
children: getFilesRecursive(
|
||||||
|
dir,
|
||||||
|
['.md', '.mdx'],
|
||||||
|
['node_modules', 'README.md'],
|
||||||
|
true,
|
||||||
|
dir + '/'
|
||||||
|
),
|
||||||
|
name: dirName,
|
||||||
|
path: dir,
|
||||||
|
type: FsEntryType.Directory,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// Send the list of files to the renderer process
|
||||||
|
mainWindow?.webContents.send('directory-contents', { data, dir, dirName, files })
|
||||||
|
store.set('lastOpenDir', dir)
|
||||||
|
}
|
||||||
|
|
||||||
ipcMain.on('dropped-file', (event, arg) => {
|
ipcMain.on('dropped-file', (event, arg) => {
|
||||||
console.warn('Dropped File(s):', arg)
|
console.warn('Dropped File(s):', arg)
|
||||||
event.returnValue = `Received ${arg.length} paths.` // Synchronous reply
|
event.returnValue = `Received ${arg.length} paths.`
|
||||||
|
if (!mainWindow) {
|
||||||
// Assuming the user only dropped one file, update the watcher to watch that file.
|
throw new Error('mainWindow is not defined')
|
||||||
|
}
|
||||||
if (arg.length > 0) {
|
if (arg.length > 0) {
|
||||||
setupWatcher(arg[0])
|
const pathToCheck = arg[0]
|
||||||
|
|
||||||
|
if (fs.statSync(pathToCheck).isDirectory()) {
|
||||||
|
// If it's a directory, get the list of files
|
||||||
|
prepareAndSendDir(pathToCheck)
|
||||||
|
} else {
|
||||||
|
setupWatcher(pathToCheck)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.on('get-current-content', event => {
|
||||||
|
event.reply('current-content', currentContent)
|
||||||
|
})
|
||||||
|
ipcMain.on('get-current-dir', () => {
|
||||||
|
const lastOpenDir = store.get('lastOpenDir') as string | undefined
|
||||||
|
|
||||||
|
if (!lastOpenDir) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prepareAndSendDir(lastOpenDir)
|
||||||
|
})
|
||||||
|
ipcMain.on('open-file', (_event, filePath) => {
|
||||||
|
setupWatcher(filePath)
|
||||||
|
})
|
||||||
|
const lastFilePath = store.get('lastFilePath') as string | undefined
|
||||||
|
const lastOpenDir = store.get('lastOpenDir') as string | undefined
|
||||||
|
|
||||||
|
if (lastOpenDir) {
|
||||||
|
prepareAndSendDir(lastOpenDir)
|
||||||
|
}
|
||||||
|
|
||||||
// Initially setup watcher for 'hello.md'
|
// Initially setup watcher for 'hello.md'
|
||||||
setupWatcher(path.resolve(__dirname, '../../../hello.md'))
|
setupWatcher(lastFilePath ?? path.resolve(__dirname, '../../../hello.md'))
|
||||||
|
|
||||||
|
function getFilesRecursive(
|
||||||
|
directory: string,
|
||||||
|
allowedExtensions: string[] = [],
|
||||||
|
ignoredPaths: string[] = [],
|
||||||
|
includeParent = true,
|
||||||
|
prefix = ''
|
||||||
|
): FileOrDirectory[] {
|
||||||
|
const fileList: FileOrDirectory[] = []
|
||||||
|
|
||||||
|
const filesAndDirs = fs.readdirSync(directory)
|
||||||
|
|
||||||
|
for (const fileOrDir of filesAndDirs) {
|
||||||
|
const absolutePath = path.join(directory, fileOrDir)
|
||||||
|
const relativePath = path.join(prefix, fileOrDir)
|
||||||
|
|
||||||
|
// Skip dotfiles and dot directories
|
||||||
|
if (fileOrDir.startsWith('.')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip ignored files and directories
|
||||||
|
if (ignoredPaths.some(ignoredPath => absolutePath.includes(ignoredPath))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fs.statSync(absolutePath).isDirectory()) {
|
||||||
|
const nestedFiles = getFilesRecursive(
|
||||||
|
absolutePath,
|
||||||
|
allowedExtensions,
|
||||||
|
ignoredPaths,
|
||||||
|
includeParent,
|
||||||
|
relativePath + '/'
|
||||||
|
)
|
||||||
|
|
||||||
|
fileList.push({
|
||||||
|
children: nestedFiles,
|
||||||
|
name: fileOrDir,
|
||||||
|
path: relativePath,
|
||||||
|
type: FsEntryType.Directory,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const extension = path.extname(fileOrDir).toLowerCase()
|
||||||
|
|
||||||
|
// Check the file has an allowed extension
|
||||||
|
if (
|
||||||
|
allowedExtensions.length === 0 ||
|
||||||
|
allowedExtensions.map(e => e.toLowerCase()).includes(extension)
|
||||||
|
) {
|
||||||
|
fileList.push({
|
||||||
|
name: fileOrDir,
|
||||||
|
path: relativePath,
|
||||||
|
type: FsEntryType.File,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileList
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FsEntryType {
|
||||||
|
Directory = 'directory',
|
||||||
|
File = 'file',
|
||||||
|
}
|
||||||
|
|
||||||
|
type File = {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
type: FsEntryType.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type Directory = {
|
||||||
|
children: Array<FileOrDirectory>
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
type: FsEntryType.Directory
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileOrDirectory = Directory | File
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
// @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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export type BundledMdx = {
|
|
||||||
code: string
|
|
||||||
frontmatter: Record<string, any>
|
|
||||||
}
|
|
||||||
@@ -1,65 +1,16 @@
|
|||||||
import { ComponentProps, ReactElement, useEffect, useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import { IpcRendererListener } from '@electron-toolkit/preload'
|
import { View } from './components/view/view'
|
||||||
import * as components from '@it-incubator/mdx-components'
|
import { Layout } from './layout'
|
||||||
import { ImagePreview, Typography } from '@it-incubator/ui-kit'
|
|
||||||
import { getMDXComponent } from 'mdx-bundler/client'
|
|
||||||
|
|
||||||
import s from './view.module.scss'
|
|
||||||
|
|
||||||
import { Pre } from './components/pre'
|
|
||||||
function App() {
|
function App() {
|
||||||
const [code, setCode] = useState<string>('')
|
|
||||||
const [fileName, setFileName] = useState<string>('')
|
const [fileName, setFileName] = useState<string>('')
|
||||||
const [srcPreview, setSrcPreview] = useState<string>('')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const listener: IpcRendererListener = (_event, content) => {
|
|
||||||
setCode(content.code)
|
|
||||||
setFileName(content.fileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
window.electron.ipcRenderer.on('file-changed', listener)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.electron.ipcRenderer.removeAllListeners('file-changed')
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!code) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const Component = getMDXComponent(code, { components: components })
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Layout fileName={fileName}>
|
||||||
<Typography.H1>{fileName}</Typography.H1>
|
<View setFileName={setFileName} />
|
||||||
<article className={s.root}>
|
</Layout>
|
||||||
<ImagePreview onClose={() => setSrcPreview('')} open={!!srcPreview} src={srcPreview} />
|
|
||||||
<Component
|
|
||||||
components={{
|
|
||||||
code: Code,
|
|
||||||
img: props => (
|
|
||||||
<img
|
|
||||||
{...props}
|
|
||||||
onClick={() => setSrcPreview(props.src || '')}
|
|
||||||
style={{ cursor: 'pointer' }}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
pre: Pre,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
||||||
const Code = ({ children, ...props }: ComponentProps<'code'>): ReactElement => {
|
|
||||||
return (
|
|
||||||
<code className={s.inline} dir={'ltr'} {...props}>
|
|
||||||
{children}
|
|
||||||
</code>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,206 +1,13 @@
|
|||||||
* {
|
|
||||||
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 {
|
:root {
|
||||||
--shiki-color-text: oklch(37.53% 0 0);
|
--header-height: 60px;
|
||||||
--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 {
|
#root,
|
||||||
--primary-hue: 204deg;
|
html,
|
||||||
--shiki-color-background: hsl(var(--primary-hue) 100% 77% / 10%);
|
body {
|
||||||
--shiki-color-text: oklch(86.07% 0 0);
|
height: 100%;
|
||||||
--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 {
|
code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
.container {
|
||||||
|
> [class*='tree'] {
|
||||||
|
margin-top: 0;
|
||||||
|
> [class*='container'] {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileCustomLabel {
|
||||||
|
all: unset;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 1px solid var(--mdx-color-outline-focus);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/renderer/src/components/file-selector/file-selector.tsx
Normal file
79
src/renderer/src/components/file-selector/file-selector.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { FileTree } from '@it-incubator/ui-kit'
|
||||||
|
|
||||||
|
import s from './file-selector.module.scss'
|
||||||
|
|
||||||
|
export enum EntryType {
|
||||||
|
Directory = 'directory',
|
||||||
|
File = 'file',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FileOrDirectory = {
|
||||||
|
children?: FileOrDirectory[]
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
type: EntryType
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: FileOrDirectory[]
|
||||||
|
selectedMdx: string
|
||||||
|
setSelectedMdx: (s: string) => void
|
||||||
|
}
|
||||||
|
export const MdxFileSelector = ({ data, selectedMdx, setSelectedMdx }: Props) => {
|
||||||
|
return (
|
||||||
|
<div className={s.container}>
|
||||||
|
<FileTree>
|
||||||
|
{data?.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<RenderItem
|
||||||
|
isFirst={index === 0 && !selectedMdx}
|
||||||
|
item={item}
|
||||||
|
key={item.path}
|
||||||
|
onFileClick={setSelectedMdx}
|
||||||
|
selectedItemPath={selectedMdx}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</FileTree>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldOpenFolder = (dirPath: string, filePath: string): boolean => {
|
||||||
|
return filePath.startsWith(dirPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenderItemProps = {
|
||||||
|
isFirst?: boolean
|
||||||
|
item: FileOrDirectory
|
||||||
|
onFileClick: (path: string) => void
|
||||||
|
selectedItemPath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const RenderItem = ({ isFirst, item, onFileClick, selectedItemPath }: RenderItemProps) => {
|
||||||
|
const isOpen = selectedItemPath ? shouldOpenFolder(item.path, selectedItemPath) : isFirst
|
||||||
|
|
||||||
|
if (item.type === EntryType.Directory) {
|
||||||
|
return (
|
||||||
|
<FileTree.Folder defaultOpen={isOpen} name={item.name}>
|
||||||
|
{item.children &&
|
||||||
|
item.children.map(childItem => (
|
||||||
|
<RenderItem
|
||||||
|
item={childItem}
|
||||||
|
key={childItem.path}
|
||||||
|
onFileClick={onFileClick}
|
||||||
|
selectedItemPath={selectedItemPath}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</FileTree.Folder>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileSelected = () => {
|
||||||
|
onFileClick(item.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSelected = selectedItemPath === item.path
|
||||||
|
|
||||||
|
return <FileTree.File active={isSelected} name={item.name} onClick={handleFileSelected} />
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import type { ComponentProps, ReactElement } from 'react'
|
|
||||||
|
|
||||||
export function CheckIcon(props: ComponentProps<'svg'>): ReactElement {
|
|
||||||
return (
|
|
||||||
<svg fill={'currentColor'} height={'1em'} viewBox={'0 0 20 20'} width={'1em'} {...props}>
|
|
||||||
<path
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
fillRule={'evenodd'}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
.root {
|
|
||||||
pointer-events: none;
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
all: unset;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import { ComponentProps, ReactElement, useCallback, useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { clsx } from 'clsx'
|
|
||||||
|
|
||||||
import s from './copy-to-clipboard.module.scss'
|
|
||||||
|
|
||||||
import { CheckIcon } from './check'
|
|
||||||
import { CopyIcon } from './copy'
|
|
||||||
|
|
||||||
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 className={s.button} onClick={handleClick} tabIndex={0} title={'Copy code'} {...props}>
|
|
||||||
<IconToUse className={clsx('nextra-copy-icon', s.root)} />
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import type { ComponentProps, ReactElement } from 'react'
|
|
||||||
|
|
||||||
export function CopyIcon(props: ComponentProps<'svg'>): ReactElement {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
fill={'none'}
|
|
||||||
height={'24'}
|
|
||||||
stroke={'currentColor'}
|
|
||||||
viewBox={'0 0 24 24'}
|
|
||||||
width={'24'}
|
|
||||||
xmlns={'http://www.w3.org/2000/svg'}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<rect
|
|
||||||
height={'13'}
|
|
||||||
rx={'2'}
|
|
||||||
strokeLinecap={'round'}
|
|
||||||
strokeLinejoin={'round'}
|
|
||||||
strokeWidth={'2'}
|
|
||||||
width={'13'}
|
|
||||||
x={'9'}
|
|
||||||
y={'9'}
|
|
||||||
/>
|
|
||||||
<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'
|
|
||||||
}
|
|
||||||
strokeLinecap={'round'}
|
|
||||||
strokeLinejoin={'round'}
|
|
||||||
strokeWidth={'2'}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './pre'
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
.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;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { ComponentProps, ReactElement, useRef } from 'react'
|
|
||||||
|
|
||||||
import { Scrollbar } from '@it-incubator/ui-kit'
|
|
||||||
import { clsx } from 'clsx'
|
|
||||||
|
|
||||||
import styles from './pre.module.scss'
|
|
||||||
|
|
||||||
import { CopyToClipboard } from './copy-to-clipboard'
|
|
||||||
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/components/toc/index.ts
Normal file
1
src/renderer/src/components/toc/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './toc'
|
||||||
23
src/renderer/src/components/toc/toc-node.module.scss
Normal file
23
src/renderer/src/components/toc/toc-node.module.scss
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
.list {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
all: unset;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
line-height: var(--line-height-m);
|
||||||
|
|
||||||
|
&[data-depth='1'] {
|
||||||
|
font-weight: var(--font-weight-semi-bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-depth='2'] {
|
||||||
|
font-weight: var(--font-weight-regular);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--color-accent-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/renderer/src/components/toc/toc-node.tsx
Normal file
89
src/renderer/src/components/toc/toc-node.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { MouseEvent } from 'react'
|
||||||
|
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
import s from './toc-node.module.scss'
|
||||||
|
|
||||||
|
import { NodeData } from './toc-node.types'
|
||||||
|
|
||||||
|
interface NodeProps {
|
||||||
|
currentHeading?: string
|
||||||
|
data: NodeData
|
||||||
|
depth: number
|
||||||
|
onLinkClick: (e: MouseEvent<HTMLAnchorElement>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TocNode({ currentHeading, data, depth, onLinkClick }: NodeProps) {
|
||||||
|
if (!data) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data.type) {
|
||||||
|
case 'list':
|
||||||
|
return (
|
||||||
|
<ul className={s.list}>
|
||||||
|
{data.children.map((child, index) => (
|
||||||
|
<TocNode
|
||||||
|
currentHeading={currentHeading}
|
||||||
|
data={child}
|
||||||
|
depth={depth}
|
||||||
|
key={index}
|
||||||
|
onLinkClick={onLinkClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
case 'listItem':
|
||||||
|
return (
|
||||||
|
<li className={s.listItem}>
|
||||||
|
{data.children.map((child, index) => (
|
||||||
|
<TocNode
|
||||||
|
currentHeading={currentHeading}
|
||||||
|
data={child}
|
||||||
|
depth={depth + 1}
|
||||||
|
key={index}
|
||||||
|
onLinkClick={onLinkClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
case 'paragraph':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data.children.map((child, index) => (
|
||||||
|
<TocNode
|
||||||
|
currentHeading={currentHeading}
|
||||||
|
data={child}
|
||||||
|
depth={depth}
|
||||||
|
key={index}
|
||||||
|
onLinkClick={onLinkClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case 'link':
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className={clsx(s.link, data.url === `#${currentHeading}` && s.active)}
|
||||||
|
data-depth={depth}
|
||||||
|
href={data.url}
|
||||||
|
onClick={onLinkClick}
|
||||||
|
title={data.title ?? undefined}
|
||||||
|
>
|
||||||
|
{data.children.map((child, index) => (
|
||||||
|
<TocNode
|
||||||
|
currentHeading={currentHeading}
|
||||||
|
data={child}
|
||||||
|
depth={depth}
|
||||||
|
key={index}
|
||||||
|
onLinkClick={onLinkClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
case 'text':
|
||||||
|
return <>{data.value}</>
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/renderer/src/components/toc/toc-node.types.ts
Normal file
31
src/renderer/src/components/toc/toc-node.types.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export type TextNode = {
|
||||||
|
type: 'text'
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LinkNode = {
|
||||||
|
children: Array<TextNode>
|
||||||
|
title: null | string
|
||||||
|
type: 'link'
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ParagraphNode = {
|
||||||
|
children: Array<LinkNode>
|
||||||
|
type: 'paragraph'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ListItemNode = {
|
||||||
|
children: Array<ListNode | ParagraphNode>
|
||||||
|
spread: boolean
|
||||||
|
type: 'listItem'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ListNode = {
|
||||||
|
children: Array<ListItemNode>
|
||||||
|
ordered: boolean
|
||||||
|
spread: boolean
|
||||||
|
type: 'list'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NodeData = LinkNode | ListItemNode | ListNode | ParagraphNode | TextNode
|
||||||
8
src/renderer/src/components/toc/toc.module.scss
Normal file
8
src/renderer/src/components/toc/toc.module.scss
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.toc {
|
||||||
|
position: sticky;
|
||||||
|
top: 19px;
|
||||||
|
|
||||||
|
> ul {
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/renderer/src/components/toc/toc.tsx
Normal file
73
src/renderer/src/components/toc/toc.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { MouseEvent, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
import { Typography } from '@it-incubator/ui-kit'
|
||||||
|
|
||||||
|
import s from './toc.module.scss'
|
||||||
|
|
||||||
|
import { TocNode } from './toc-node'
|
||||||
|
import { NodeData } from './toc-node.types'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
tocMap: NodeData
|
||||||
|
}
|
||||||
|
export const TableOfContents = ({ tocMap }: Props) => {
|
||||||
|
const [currentHeading, setCurrentHeading] = useState('')
|
||||||
|
const headingsObserverRef = useRef<IntersectionObserver | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const setCurrent: IntersectionObserverCallback = entries => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setCurrentHeading(entry.target.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const observerOptions: IntersectionObserverInit = {
|
||||||
|
// Negative top margin accounts for `scroll-margin`.
|
||||||
|
// Negative bottom margin means heading needs to be towards top of viewport to trigger intersection.
|
||||||
|
rootMargin: '-60px 0% -66%',
|
||||||
|
threshold: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!headingsObserverRef.current) {
|
||||||
|
headingsObserverRef.current = new IntersectionObserver(setCurrent, observerOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
const headingsObserver = headingsObserverRef.current
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelectorAll('article :is(h1,h2,h3)').forEach(h => headingsObserver.observe(h))
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
headingsObserver.disconnect()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (headingsObserverRef.current) {
|
||||||
|
const headingsObserver = headingsObserverRef.current
|
||||||
|
|
||||||
|
// Disconnect and reconnect the observer to refresh it
|
||||||
|
headingsObserver.disconnect()
|
||||||
|
setTimeout(() => {
|
||||||
|
document.querySelectorAll('article :is(h1,h2,h3)').forEach(h => headingsObserver.observe(h))
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}, [tocMap])
|
||||||
|
|
||||||
|
const onLinkClick = (e: MouseEvent<HTMLAnchorElement>) => {
|
||||||
|
setCurrentHeading(e.currentTarget.getAttribute('href')!.replace('#', ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className={s.toc}>
|
||||||
|
<Typography.Subtitle2 mb={'13px'} ml={'12px'} mt={'19px'}>
|
||||||
|
Содержание:
|
||||||
|
</Typography.Subtitle2>
|
||||||
|
<TocNode currentHeading={currentHeading} data={tocMap} depth={0} onLinkClick={onLinkClick} />
|
||||||
|
</aside>
|
||||||
|
)
|
||||||
|
}
|
||||||
35
src/renderer/src/components/view/view.module.scss
Normal file
35
src/renderer/src/components/view/view.module.scss
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
81
src/renderer/src/components/view/view.tsx
Normal file
81
src/renderer/src/components/view/view.tsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { IpcRendererListener } from '@electron-toolkit/preload'
|
||||||
|
import { MdxComponent, Prose } from '@it-incubator/mdx-components'
|
||||||
|
import { ImagePreview } from '@it-incubator/ui-kit'
|
||||||
|
|
||||||
|
import s from './view.module.scss'
|
||||||
|
|
||||||
|
import { FileOrDirectory, MdxFileSelector } from '../file-selector/file-selector'
|
||||||
|
import { TableOfContents } from '../toc'
|
||||||
|
|
||||||
|
export const View = ({ setFileName }) => {
|
||||||
|
const [selectedFile, setSelectedFile] = useState<string>('')
|
||||||
|
const [code, setCode] = useState<string>('')
|
||||||
|
const [toc, setToc] = useState<any>({})
|
||||||
|
const [srcPreview, setSrcPreview] = useState<string>('')
|
||||||
|
|
||||||
|
const [directoryContents, setDirectoryContents] = useState<{
|
||||||
|
data: FileOrDirectory[]
|
||||||
|
dir: string
|
||||||
|
dirName: string
|
||||||
|
files: string[]
|
||||||
|
} | null>(null)
|
||||||
|
|
||||||
|
const handleFileClick = (filePath: string) => {
|
||||||
|
setSelectedFile(filePath)
|
||||||
|
window.electron.ipcRenderer.send('open-file', filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const contentListener: IpcRendererListener = (_event, content) => {
|
||||||
|
setCode(content.code)
|
||||||
|
setFileName(content.fileName)
|
||||||
|
setSelectedFile(content.fileName)
|
||||||
|
setToc(content.toc)
|
||||||
|
}
|
||||||
|
|
||||||
|
const directoryContentsListener: IpcRendererListener = (_event, content) => {
|
||||||
|
setDirectoryContents(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.electron.ipcRenderer.on('current-content', contentListener)
|
||||||
|
window.electron.ipcRenderer.on('directory-contents', directoryContentsListener)
|
||||||
|
|
||||||
|
window.electron.ipcRenderer.send('get-current-dir')
|
||||||
|
window.electron.ipcRenderer.send('get-current-content')
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.electron.ipcRenderer.removeAllListeners('file-changed')
|
||||||
|
window.electron.ipcRenderer.removeAllListeners('current-content')
|
||||||
|
window.electron.ipcRenderer.removeAllListeners('directory-contents')
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={s.page}>
|
||||||
|
<ImagePreview onClose={() => setSrcPreview('')} open={!!srcPreview} src={srcPreview} />
|
||||||
|
<div className={s.container}>
|
||||||
|
<div>
|
||||||
|
{directoryContents && (
|
||||||
|
<MdxFileSelector
|
||||||
|
data={directoryContents.data}
|
||||||
|
selectedMdx={selectedFile}
|
||||||
|
setSelectedMdx={handleFileClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Prose as={'article'} className={s.root}>
|
||||||
|
<MdxComponent code={code} onImageClick={setSrcPreview} />
|
||||||
|
</Prose>
|
||||||
|
<div>
|
||||||
|
<TableOfContents tocMap={toc?.map} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
1
src/renderer/src/layout/index.ts
Normal file
1
src/renderer/src/layout/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './layout'
|
||||||
36
src/renderer/src/layout/layout.module.scss
Normal file
36
src/renderer/src/layout/layout.module.scss
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.scrollbar {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
scroll-padding-top: calc(1.5rem + var(--header-height));
|
||||||
|
|
||||||
|
position: initial !important;
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
padding-top: var(--header-height);
|
||||||
|
|
||||||
|
> div[class*='scrollbar'] {
|
||||||
|
margin-top: var(--header-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 768px) {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
max-width: 1186px;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
@media screen and (width <= 1300px) {
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 768px) {
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/renderer/src/layout/layout.tsx
Normal file
30
src/renderer/src/layout/layout.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { ReactNode, useState } from 'react'
|
||||||
|
|
||||||
|
import { Button, Header, Scrollbar, Typography } from '@it-incubator/ui-kit'
|
||||||
|
|
||||||
|
import s from './layout.module.scss'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode
|
||||||
|
fileName: string
|
||||||
|
}
|
||||||
|
export const Layout = ({ children, fileName }: Props) => {
|
||||||
|
const [isDark, setIsDark] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const handleThemeChanged = () => {
|
||||||
|
setIsDark(!isDark)
|
||||||
|
document.body.classList.toggle('dark-mode', !isDark)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header>
|
||||||
|
<Typography.H1>{fileName}</Typography.H1>
|
||||||
|
<Button onClick={() => handleThemeChanged()}>Toggle theme</Button>
|
||||||
|
</Header>
|
||||||
|
<Scrollbar className={s.scrollbar}>
|
||||||
|
<main className={s.main}>{children}</main>
|
||||||
|
</Scrollbar>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const View = ({ code }: { code: string }) => {
|
|
||||||
return <div>View</div>
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user