diff --git a/package-lock.json b/package-lock.json index d1d143c..ee50c82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6454,6 +6454,16 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-prismjs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-prismjs/-/babel-plugin-prismjs-2.1.0.tgz", + "integrity": "sha512-ehzSKYfeAz4U78zi/sfwsjDPlq0LvDKxNefcZTJ/iKBu+plsHsLqZhUeGf1+82LAcA35UZGbU6ksEx2Utphc/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prismjs": "^1.18.0" + } + }, "node_modules/babel-plugin-transform-hook-names": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", @@ -11774,6 +11784,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/property-information": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", @@ -13881,6 +13900,20 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite-plugin-prismjs": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/vite-plugin-prismjs/-/vite-plugin-prismjs-0.0.11.tgz", + "integrity": "sha512-20NBQxg/zH+3FTrlU6BQTob720xkuXNYtrx7psAQ4E6pMcRDeLEK77QU9kXURU587+f2To7ASH1JVTGbXVV/vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.15.5", + "babel-plugin-prismjs": "^2.1.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/vite-plugin-svgr": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.2.0.tgz", @@ -14524,7 +14557,8 @@ "@mdx-js/mdx": "^3.0.1", "@mdx-js/preact": "^3.0.1", "classnames": "^2.5.1", - "preact": "^10.20.2" + "preact": "^10.20.2", + "prismjs": "^1.29.0" }, "devDependencies": { "@babel/core": "^7.24.4", @@ -14534,7 +14568,8 @@ "@linaria/core": "^6.1.0", "@linaria/react": "^6.1.0", "@wyw-in-js/babel-preset": "^0.5.1", - "@wyw-in-js/vite": "^0.5.1" + "@wyw-in-js/vite": "^0.5.1", + "vite-plugin-prismjs": "^0.0.11" } }, "packages/unified-latex-to-hast": { diff --git a/packages/processor/src/markdown-to-mdx/hast-transforms/index.ts b/packages/processor/src/markdown-to-mdx/hast-transforms/index.ts index a5ba4dd..6065947 100644 --- a/packages/processor/src/markdown-to-mdx/hast-transforms/index.ts +++ b/packages/processor/src/markdown-to-mdx/hast-transforms/index.ts @@ -3,13 +3,9 @@ import { ProcessorOptions } from '@mdx-js/mdx'; import { ElementContent } from 'hast'; import { PhrasingContent, Root } from 'mdast'; // import { createSvg } from '../utils/icons'; -// import rehypeRaw from 'rehype-raw'; -// import rehypeSlug from 'rehype-slug'; import { PluggableList, unified } from 'unified'; -// import { visit } from 'unist-util-visit'; import { Context } from '../context'; -// import { mathjax } from './mathjax'; import { createWrapper } from './wrapper'; export const processorOptions: ProcessorOptions = { @@ -49,7 +45,6 @@ function createRehypeFragmentPlugins( options: Partial = {}, ): PluggableList { return [ - // mathjax(options), // TODO: // [ // autolinkHeadings, diff --git a/packages/processor/src/markdown-to-mdx/hast-transforms/mathjax.ts b/packages/processor/src/markdown-to-mdx/hast-transforms/mathjax.ts deleted file mode 100644 index 676334a..0000000 --- a/packages/processor/src/markdown-to-mdx/hast-transforms/mathjax.ts +++ /dev/null @@ -1,31 +0,0 @@ -// import mathjaxSvg from 'rehype-mathjax/svg'; -import { Options } from '../options'; -import mathjaxBrowser from 'rehype-mathjax/browser'; -import mathjaxChtml from 'rehype-mathjax/chtml'; -import { Pluggable } from 'unified'; - -const mathjaxOptions = { - chtml: { - // minScale: 0.8, - fontURL: - 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2', - }, - tex: { - inlineMath: [ - ['$', '$'], - // ['\\(', '\\)'], - ], - displayMath: [ - ['$$', '$$'], - // [`\\[`, `\\]`], - ], - }, -}; - -export function mathjax(options: Partial): Pluggable { - if (options.mathsAsTex) { - return [mathjaxBrowser, mathjaxOptions]; - } else { - return [mathjaxChtml, mathjaxOptions]; - } -} diff --git a/packages/processor/src/markdown-to-mdx/index.ts b/packages/processor/src/markdown-to-mdx/index.ts index 8728a2d..bafc256 100644 --- a/packages/processor/src/markdown-to-mdx/index.ts +++ b/packages/processor/src/markdown-to-mdx/index.ts @@ -12,9 +12,9 @@ export { TocHighlightContext, } from './mdx-handlers/toc-highlight/toc-highlight-provider'; -export type { FontName } from './mdx-handlers/mathjax'; +export type { FontName } from './mdx-handlers/math'; -export { MathsProvider, MathsContext } from './mdx-handlers/mathjax'; +export { MathsProvider, MathsContext } from './mdx-handlers/math'; export { sidebarRunOptions } from './sidebar'; diff --git a/packages/processor/src/markdown-to-mdx/mdx-handlers/index.tsx b/packages/processor/src/markdown-to-mdx/mdx-handlers/index.tsx index 2ffadb7..06db178 100644 --- a/packages/processor/src/markdown-to-mdx/mdx-handlers/index.tsx +++ b/packages/processor/src/markdown-to-mdx/mdx-handlers/index.tsx @@ -1,6 +1,6 @@ import { MDXComponents } from 'mdx/types'; -import { MathJax } from './mathjax'; +import { MathJax } from './math'; import { Task } from './task/Task'; import { Section } from './toc-highlight/section'; @@ -18,10 +18,7 @@ export function useMDXComponents(): MDXComponents { // TODO: remove sectionize and use the titles section: Section, code(props) { - if ( - props.class?.includes('math-inline') || - props.class?.includes('math-display') - ) { + if (props.class?.includes('language-math')) { return ; } return ; diff --git a/packages/processor/src/markdown-to-mdx/mdx-handlers/math/index.tsx b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/index.tsx new file mode 100644 index 0000000..b0e316d --- /dev/null +++ b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/index.tsx @@ -0,0 +1,30 @@ +import { useContext } from 'preact/hooks'; + +import { toLaTeX } from './latex'; +import { getMathJax } from './mathjax'; +import { MathsContext } from './maths-provider'; + +export { MathsContext, MathsProvider } from './maths-provider'; + +export type { FontName } from './mathjax'; + +type Props = { + expr: string; +}; + +export function MathJax({ expr }: Props) { + const ctx = useContext(MathsContext); + if (ctx.mathsAsTex) { + return ( + + ); + } else { + const children = getMathJax(expr, ctx.fontName); + return <>{children}; + } +} diff --git a/packages/processor/src/markdown-to-mdx/mdx-handlers/math/latex.ts b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/latex.ts new file mode 100644 index 0000000..4fecb89 --- /dev/null +++ b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/latex.ts @@ -0,0 +1,13 @@ +import Prism from 'prismjs'; + +export function toLaTeX(expr: string) { + const formatted = formatLaTeX(expr); + return Prism.highlight(formatted, Prism.languages.latex, 'latex'); +} + +function formatLaTeX(expr: string) { + return expr + .replace(/\\\\\s?/g, '\\\\\n') + .replace(/\\begin{align(\*?)}/g, '\\begin{align$1}\n') + .replace(/\\end{align(\*?)}/g, '\n\\end{align$1}'); +} diff --git a/packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/litedom.ts b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/litedom.ts similarity index 100% rename from packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/litedom.ts rename to packages/processor/src/markdown-to-mdx/mdx-handlers/math/litedom.ts diff --git a/packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/index.tsx b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/mathjax.ts similarity index 77% rename from packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/index.tsx rename to packages/processor/src/markdown-to-mdx/mdx-handlers/math/mathjax.ts index 905498a..07c5344 100644 --- a/packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/index.tsx +++ b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/mathjax.ts @@ -18,12 +18,8 @@ import { mathjax } from 'mathjax-full/js/mathjax.js'; import { SVG } from 'mathjax-full/js/output/svg.js'; import { MathJaxTermesFont } from 'mathjax-termes-font/js/svg.js'; import 'mathjax-termes-font/js/svg/dynamic/double-struck.js'; -import { useContext } from 'preact/hooks'; import { render } from './litedom'; -import { MathsContext } from './maths-provider'; - -export { MathsContext, MathsProvider } from './maths-provider'; type Document = MathDocument; @@ -61,23 +57,9 @@ const output: Output = { }), }; -export type Options = { - fontName: FontName; -}; - -export function getMathjaxHtml(tex: string, options: Options) { - doc.outputJax = output[options.fontName]; +export function getMathJax(expr: string, fontName: FontName) { + doc.outputJax = output[fontName]; doc.outputJax.setAdaptor(doc.adaptor); - return doc.convert(tex) as LiteElement; -} - -type Props = { - expr: string; -}; - -export function MathJax({ expr }: Props) { - const ctx = useContext(MathsContext); - const node = getMathjaxHtml(expr, ctx); - const children = render(node.children); - return <>{children}; + const node = doc.convert(expr) as LiteElement; + return render(node.children); } diff --git a/packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/maths-provider.tsx b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/maths-provider.tsx similarity index 71% rename from packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/maths-provider.tsx rename to packages/processor/src/markdown-to-mdx/mdx-handlers/math/maths-provider.tsx index dac9a0c..c9e111c 100644 --- a/packages/processor/src/markdown-to-mdx/mdx-handlers/mathjax/maths-provider.tsx +++ b/packages/processor/src/markdown-to-mdx/mdx-handlers/math/maths-provider.tsx @@ -1,18 +1,22 @@ import { ComponentChildren, createContext } from 'preact'; import { useMemo, useState } from 'preact/hooks'; -import { FontName } from '@isos/processor'; +import { FontName } from './mathjax'; const defaultFontName = 'termes'; type Maths = { fontName: FontName; + mathsAsTex: boolean; setFontName: (fontName: FontName) => unknown; + setMathsAsTex: (mathsAsTex: boolean) => unknown; }; export const MathsContext = createContext({ fontName: defaultFontName, + mathsAsTex: false, setFontName: () => {}, + setMathsAsTex: () => {}, }); export function MathsProvider({ @@ -21,13 +25,16 @@ export function MathsProvider({ children: ComponentChildren; }) { const [fontName, setFontName] = useState(defaultFontName); + const [mathsAsTex, setMathsAsTex] = useState(false); const context = useMemo((): Maths => { return { fontName, + mathsAsTex, setFontName, + setMathsAsTex, }; - }, [fontName]); + }, [fontName, mathsAsTex]); return ( diff --git a/packages/runtime/package.json b/packages/runtime/package.json index c5255b7..9e41ead 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -18,7 +18,8 @@ "@mdx-js/mdx": "^3.0.1", "@mdx-js/preact": "^3.0.1", "classnames": "^2.5.1", - "preact": "^10.20.2" + "preact": "^10.20.2", + "prismjs": "^1.29.0" }, "devDependencies": { "@babel/core": "^7.24.4", @@ -28,6 +29,7 @@ "@linaria/core": "^6.1.0", "@linaria/react": "^6.1.0", "@wyw-in-js/babel-preset": "^0.5.1", - "@wyw-in-js/vite": "^0.5.1" + "@wyw-in-js/vite": "^0.5.1", + "vite-plugin-prismjs": "^0.0.11" } } diff --git a/packages/runtime/src/providers/index.tsx b/packages/runtime/src/providers/index.tsx index cc5d652..8580c6e 100644 --- a/packages/runtime/src/providers/index.tsx +++ b/packages/runtime/src/providers/index.tsx @@ -10,29 +10,12 @@ type Props = { children: ComponentChildren; }; -// const mathjaxConfig: MathJax3Config = { -// tex: { -// inlineMath: [ -// ['$', '$'], -// // ['\\(', '\\)'], -// ], -// displayMath: [ -// ['$$', '$$'], -// // [`\\[`, `\\]`], -// ], -// }, -// }; - export function Providers({ children }: Props) { return ( - - {/* */} - {children} - {/* */} - + {children} diff --git a/packages/runtime/src/sidebar/checkbox.tsx b/packages/runtime/src/sidebar/checkbox.tsx index 891af91..364a4b9 100644 --- a/packages/runtime/src/sidebar/checkbox.tsx +++ b/packages/runtime/src/sidebar/checkbox.tsx @@ -28,6 +28,7 @@ const Wrapper = styled.div` display: flex; justify-content: space-between; align-items: middle; + padding-bottom: 0.5em; label { font-size: 0.9em; diff --git a/packages/runtime/src/sidebar/view-options.tsx b/packages/runtime/src/sidebar/view-options.tsx index a21c467..ac03c61 100644 --- a/packages/runtime/src/sidebar/view-options.tsx +++ b/packages/runtime/src/sidebar/view-options.tsx @@ -65,6 +65,11 @@ export function ViewOptions() { maths.setFontName(sansSerif ? 'fira' : 'termes') } /> + maths.setMathsAsTex(mathsAsTex)} + /> ); diff --git a/packages/runtime/src/styles/index.scss b/packages/runtime/src/styles/index.scss index edde1cd..b316b9d 100644 --- a/packages/runtime/src/styles/index.scss +++ b/packages/runtime/src/styles/index.scss @@ -51,6 +51,7 @@ main { @import './sidenote.scss'; @import './scrollbars.scss'; @import './range-input.scss'; +@import './syntax-highlight.scss'; // @import './odl/index.scss'; // @import './latex/index.scss'; diff --git a/packages/runtime/src/styles/syntax-highlight.scss b/packages/runtime/src/styles/syntax-highlight.scss new file mode 100644 index 0000000..2441742 --- /dev/null +++ b/packages/runtime/src/styles/syntax-highlight.scss @@ -0,0 +1,93 @@ +pre:has(code.latex) { + text-wrap-mode: wrap; + margin-bottom: 1.5em; +} + +pre code.latex { + display: block; +} + +code.latex { + font-family: 'Nimbus Mono PS', 'Courier New', monospace; + font-weight: bold; + font-size: 0.8em; + font-style: normal; + display: inline-block; + background: #444; + color: #ccc; + padding: 0.1em 0.5em 0.05em; + border-radius: 0.3em; + position: relative; + bottom: 0.1em; +} + +// https://github.com/PrismJS/prism/blob/master/themes/prism-dark.css + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: hsl(30, 20%, 50%); +} + +.token.punctuation { + opacity: 0.7; +} + +.token.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol { + color: hsl(350, 40%, 70%); +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: hsl(75, 70%, 60%); +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string, +.token.variable { + color: hsl(40, 90%, 60%); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: hsl(350, 40%, 70%); +} + +.token.regex, +.token.important { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +.token.deleted { + color: red; +} diff --git a/packages/runtime/vite.config.ts b/packages/runtime/vite.config.ts index 992422e..9067e5c 100644 --- a/packages/runtime/vite.config.ts +++ b/packages/runtime/vite.config.ts @@ -1,9 +1,11 @@ import preact from '@preact/preset-vite'; import linaria from '@wyw-in-js/vite'; -import { fileURLToPath } from 'node:url'; import { defineConfig } from 'vite'; +import prismjs from 'vite-plugin-prismjs'; import svgr from 'vite-plugin-svgr'; +// import { fileURLToPath } from 'node:url'; + // https://vitejs.dev/config/ export default defineConfig(async () => ({ plugins: [ @@ -16,6 +18,9 @@ export default defineConfig(async () => ({ preact({ prefreshEnabled: false, }), + prismjs({ + languages: ['latex'], + }), // { // name: 'inject', // transformIndexHtml() { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 08d1271..ffa4448 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "isos", - "version": "0.0.34", + "version": "0.0.35", "identifier": "com.isos.app", "build": { "beforeDevCommand": "npm run dev", diff --git a/vite.config.ts b/vite.config.ts index 14d2b1d..2582641 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,6 +2,7 @@ import preact from '@preact/preset-vite'; import linaria from '@wyw-in-js/vite'; import { defineConfig } from 'vite'; +import prismjs from 'vite-plugin-prismjs'; import svgr from 'vite-plugin-svgr'; import { configDefaults } from 'vitest/config'; @@ -23,6 +24,9 @@ export default defineConfig(async () => ({ preact({ prefreshEnabled: false, }), + prismjs({ + languages: ['latex'], + }), ], // 1. prevent vite from obscuring rust errors // clearScreen: false,