mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 03:11:29 +08:00
Support multiple lines math input (#294)
* change another way to render math * open\save\edit multiple lines math block * rewrite header label style * inline code style update * update dark theme style * update change log * typo error * update webpack to v4 * update license * fix unexpected to delete math preview block * fix cursor error when change mode
This commit is contained in:
parent
1a7a3d5c06
commit
16bb1de82e
@ -51,7 +51,7 @@ function startRenderer () {
|
||||
compiler.plugin('compilation', compilation => {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
cb && cb()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -5,10 +5,10 @@ process.env.BABEL_ENV = 'main'
|
||||
const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
const proMode = process.env.NODE_ENV === 'production'
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
|
||||
let mainConfig = {
|
||||
const mainConfig = {
|
||||
mode: 'development',
|
||||
entry: {
|
||||
main: path.join(__dirname, '../src/main/index.js')
|
||||
},
|
||||
@ -40,8 +40,8 @@ let mainConfig = {
|
||||
]
|
||||
},
|
||||
node: {
|
||||
__dirname: process.env.NODE_ENV !== 'production',
|
||||
__filename: process.env.NODE_ENV !== 'production'
|
||||
__dirname: !proMode,
|
||||
__filename: !proMode
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
@ -60,7 +60,7 @@ let mainConfig = {
|
||||
/**
|
||||
* Adjust mainConfig for development settings
|
||||
*/
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!proMode) {
|
||||
mainConfig.plugins.push(
|
||||
new webpack.DefinePlugin({
|
||||
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
|
||||
@ -71,12 +71,10 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
/**
|
||||
* Adjust mainConfig for production settings
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (proMode) {
|
||||
mainConfig.mode = 'production'
|
||||
mainConfig.plugins.push(
|
||||
new BabiliWebpackPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
})
|
||||
// new BabiliWebpackPlugin()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,14 @@
|
||||
process.env.BABEL_ENV = 'renderer'
|
||||
|
||||
const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
|
||||
const { dependencies } = require('../package.json')
|
||||
const proMode = process.env.NODE_ENV === 'production'
|
||||
/**
|
||||
* List of node_modules to include in webpack bundle
|
||||
*
|
||||
@ -18,9 +18,10 @@ const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
* that provide pure *.vue files that need compiling
|
||||
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
|
||||
*/
|
||||
let whiteListedModules = ['vue']
|
||||
const whiteListedModules = ['vue']
|
||||
|
||||
let rendererConfig = {
|
||||
const rendererConfig = {
|
||||
mode: 'development',
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
entry: {
|
||||
renderer: path.join(__dirname, '../src/renderer/main.js')
|
||||
@ -43,10 +44,10 @@ let rendererConfig = {
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: 'css-loader'
|
||||
})
|
||||
use: [
|
||||
proMode ? MiniCssExtractPlugin.loader : 'style-loader',
|
||||
"css-loader"
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
@ -64,14 +65,7 @@ let rendererConfig = {
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: {
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
extractCSS: process.env.NODE_ENV === 'production',
|
||||
loaders: {
|
||||
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
|
||||
scss: 'vue-style-loader!css-loader!sass-loader'
|
||||
}
|
||||
}
|
||||
loader: 'vue-loader'
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -109,7 +103,6 @@ let rendererConfig = {
|
||||
__filename: process.env.NODE_ENV !== 'production'
|
||||
},
|
||||
plugins: [
|
||||
new ExtractTextPlugin('styles.css'),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
@ -123,7 +116,8 @@ let rendererConfig = {
|
||||
: false
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin()
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new VueLoaderPlugin()
|
||||
],
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
@ -154,11 +148,16 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
/**
|
||||
* Adjust rendererConfig for production settings
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (proMode) {
|
||||
rendererConfig.devtool = ''
|
||||
|
||||
rendererConfig.mode = 'production'
|
||||
rendererConfig.plugins.push(
|
||||
new BabiliWebpackPlugin(),
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: '[name].[hash].css',
|
||||
chunkFilename: '[id].[hash].css'
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.join(__dirname, '../static'),
|
||||
@ -170,9 +169,6 @@ if (process.env.NODE_ENV === 'production') {
|
||||
to: path.join(__dirname, '../dist/electron/codemirror/mode/[name]/[name].js')
|
||||
}
|
||||
]),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
|
@ -5,12 +5,15 @@ process.env.BABEL_ENV = 'web'
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
|
||||
let webConfig = {
|
||||
const proMode = process.env.NODE_ENV === 'production'
|
||||
|
||||
const webConfig = {
|
||||
mode: 'development',
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
entry: {
|
||||
web: path.join(__dirname, '../src/renderer/main.js')
|
||||
@ -30,10 +33,10 @@ let webConfig = {
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: 'css-loader'
|
||||
})
|
||||
use: [
|
||||
proMode ? MiniCssExtractPlugin.loader : 'style-loader',
|
||||
"css-loader"
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
@ -48,14 +51,7 @@ let webConfig = {
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: {
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
extractCSS: true,
|
||||
loaders: {
|
||||
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
|
||||
scss: 'vue-style-loader!css-loader!sass-loader'
|
||||
}
|
||||
}
|
||||
loader: 'vue-loader'
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -81,7 +77,6 @@ let webConfig = {
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new ExtractTextPlugin('styles.css'),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
@ -96,7 +91,8 @@ let webConfig = {
|
||||
'process.env.IS_WEB': 'true'
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin()
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new VueLoaderPlugin()
|
||||
],
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
@ -115,11 +111,17 @@ let webConfig = {
|
||||
/**
|
||||
* Adjust webConfig for production settings
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (proMode) {
|
||||
webConfig.devtool = ''
|
||||
webConfig.mode ='production'
|
||||
|
||||
webConfig.plugins.push(
|
||||
new BabiliWebpackPlugin(),
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: '[name].[hash].css',
|
||||
chunkFilename: '[id].[hash].css'
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.join(__dirname, '../static'),
|
||||
@ -127,9 +129,6 @@ if (process.env.NODE_ENV === 'production') {
|
||||
ignore: ['.*']
|
||||
}
|
||||
]),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
|
3
.github/CHANGELOG.md
vendored
3
.github/CHANGELOG.md
vendored
@ -1,4 +1,4 @@
|
||||
### 0.11.37
|
||||
### 0.11.38
|
||||
|
||||
**:cactus:Feature**
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
- feature: Support `setext` heading but the default heading style is `atx`
|
||||
- feature: User list item marker setting in preference file.
|
||||
- feature: Select text from selected table (cell) only if you press Ctrl+A
|
||||
- feature: Support Multiple lines math #242
|
||||
|
||||
**:butterfly:Optimization**
|
||||
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2018 Jocs
|
||||
Copyright (c) 2017-Present Jocs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,6 +0,0 @@
|
||||
!macro customUnInstall
|
||||
MessageBox MB_YESNO "Do you want to delete user settings?" /SD IDNO IDNO SkipRemoval
|
||||
SetShellVarContext current
|
||||
RMDir /r "$APPDATA\marktext"
|
||||
SkipRemoval:
|
||||
!macroend
|
0
dist/electron/.gitkeep
vendored
0
dist/electron/.gitkeep
vendored
0
dist/web/.gitkeep
vendored
0
dist/web/.gitkeep
vendored
5543
package-lock.json
generated
5543
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
90
package.json
90
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "marktext",
|
||||
"version": "0.10.21",
|
||||
"author": "Jocs <luoran1988@126.com>",
|
||||
"author": "Jocs <ransixi@gmail.com>",
|
||||
"description": "Next generation markdown editor",
|
||||
"license": "MIT",
|
||||
"main": "./dist/electron/main.js",
|
||||
@ -105,61 +105,59 @@
|
||||
"codemirror": "^5.36.0",
|
||||
"css-tree": "^1.0.0-alpha.28",
|
||||
"dompurify": "^1.0.3",
|
||||
"electron-window-state": "^4.1.1",
|
||||
"element-ui": "^2.3.3",
|
||||
"element-ui": "^2.3.9",
|
||||
"file-icons-js": "^1.0.3",
|
||||
"fs-extra": "^5.0.0",
|
||||
"fs-extra": "^6.0.1",
|
||||
"fuzzaldrin": "^2.1.0",
|
||||
"html-tags": "^2.0.0",
|
||||
"katex": "^0.9.0",
|
||||
"katex": "^0.10.0-alpha",
|
||||
"mousetrap": "^1.6.1",
|
||||
"parse5": "^4.0.0",
|
||||
"parse5": "^5.0.0",
|
||||
"snabbdom": "^0.7.1",
|
||||
"snabbdom-to-html": "^5.1.1",
|
||||
"snabbdom-virtualize": "^0.7.0",
|
||||
"turndown": "^4.0.1",
|
||||
"turndown-plugin-gfm": "^1.0.1",
|
||||
"turndown": "^4.0.2",
|
||||
"turndown-plugin-gfm": "^1.0.2",
|
||||
"vue": "^2.5.16",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vuex": "^2.3.1"
|
||||
"vuex": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-plugin-component": "^1.1.0",
|
||||
"babel-eslint": "^8.2.3",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-component": "^1.1.1",
|
||||
"babel-plugin-istanbul": "^4.1.6",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-register": "^6.24.1",
|
||||
"babili-webpack-plugin": "^0.1.2",
|
||||
"cfonts": "^1.2.0",
|
||||
"babel-register": "^6.26.0",
|
||||
"cfonts": "^2.1.2",
|
||||
"chai": "^4.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"cross-env": "^5.0.5",
|
||||
"css-loader": "^0.28.4",
|
||||
"chalk": "^2.4.1",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
"cross-env": "^5.1.6",
|
||||
"css-loader": "^0.28.11",
|
||||
"del": "^3.0.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^2.0.2",
|
||||
"electron-builder": "^20.14.7",
|
||||
"electron-debug": "^1.5.0",
|
||||
"electron-devtools-installer": "^2.2.0",
|
||||
"electron-updater": "^2.21.8",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"electron-updater": "^2.21.10",
|
||||
"electron-window-state": "^4.1.1",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-standard": "^10.2.1",
|
||||
"eslint-friendly-formatter": "^3.0.0",
|
||||
"eslint-loader": "^1.9.0",
|
||||
"eslint-plugin-html": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.10.0",
|
||||
"eslint-plugin-node": "^5.1.1",
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"inject-loader": "^3.0.0",
|
||||
"eslint-config-standard": "^11.0.0",
|
||||
"eslint-friendly-formatter": "^4.0.1",
|
||||
"eslint-loader": "^2.0.0",
|
||||
"eslint-plugin-html": "^4.0.3",
|
||||
"eslint-plugin-import": "^2.12.0",
|
||||
"eslint-plugin-node": "^6.0.1",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
"eslint-plugin-standard": "^3.1.0",
|
||||
"file-loader": "^1.1.11",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"inject-loader": "^4.0.1",
|
||||
"karma": "^1.3.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
@ -168,20 +166,22 @@
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "^0.0.31",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"mini-css-extract-plugin": "^0.4.0",
|
||||
"mocha": "^3.0.2",
|
||||
"multispinner": "^0.2.1",
|
||||
"node-loader": "^0.6.0",
|
||||
"require-dir": "^0.3.0",
|
||||
"spectron": "^3.7.1",
|
||||
"style-loader": "^0.18.2",
|
||||
"url-loader": "^0.5.9",
|
||||
"require-dir": "^1.0.0",
|
||||
"spectron": "^3.8.0",
|
||||
"style-loader": "^0.21.0",
|
||||
"url-loader": "^1.0.1",
|
||||
"vue-html-loader": "^1.2.4",
|
||||
"vue-loader": "^14.2.2",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.4.2",
|
||||
"webpack": "^3.5.2",
|
||||
"webpack-dev-server": "^2.7.1",
|
||||
"webpack-hot-middleware": "^2.22.0",
|
||||
"webpack-merge": "^4.1.0"
|
||||
"vue-loader": "^15.2.0",
|
||||
"vue-style-loader": "^4.1.0",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"webpack": "^4.8.3",
|
||||
"webpack-cli": "^2.1.4",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-hot-middleware": "^2.22.2",
|
||||
"webpack-merge": "^4.1.2"
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ import { generateKeyHash, genUpper2LowerKeyHash, getLongUniqueId } from './utils
|
||||
import htmlTags from 'html-tags'
|
||||
import voidHtmlTags from 'html-tags/void'
|
||||
|
||||
export const UNDO_DEPTH = 100
|
||||
// [0.25, 0.5, 1, 2, 4, 8] <—?—> [256M, 500M/768M, 1G/1000M, 2G, 4G, 8G]
|
||||
export const DEVICE_MEMORY = navigator.deviceMemory // Get the divice memory number
|
||||
// Electron 2.0.2 not support yet! So give a default value 4
|
||||
export const DEVICE_MEMORY = navigator.deviceMemory || 4 // Get the divice memory number(Chrome >= 63)
|
||||
export const UNDO_DEPTH = DEVICE_MEMORY >= 4 ? 100 : 50
|
||||
export const HAS_TEXT_BLOCK_REG = /^(h\d|span|th|td|hr|pre)/i
|
||||
export const VOID_HTML_TAGS = voidHtmlTags
|
||||
export const HTML_TAGS = htmlTags
|
||||
@ -80,6 +81,7 @@ export const CLASS_OR_ID = genUpper2LowerKeyHash([
|
||||
'AG_HTML_ESCAPE',
|
||||
'AG_FRONT_MATTER',
|
||||
'AG_FRONT_MATTER_LINE',
|
||||
'AG_MULTIPLE_MATH_LINE',
|
||||
'AG_CODEMIRROR_BLOCK',
|
||||
'AG_SHOW_PREVIEW',
|
||||
'AG_HTML_PREVIEW',
|
||||
@ -113,7 +115,11 @@ export const CLASS_OR_ID = genUpper2LowerKeyHash([
|
||||
'AG_MATH_TEXT',
|
||||
'AG_MATH_RENDER',
|
||||
'AG_MATH_ERROR',
|
||||
'AG_MATH_EMPTY',
|
||||
'AG_MATH_MARKER',
|
||||
'AG_MATH_PREVIEW',
|
||||
'AG_MULTIPLE_MATH_BLOCK',
|
||||
'AG_MULTIPLE_MATH',
|
||||
'AG_LOOSE_LIST_ITEM',
|
||||
'AG_TIGHT_LIST_ITEM',
|
||||
'AG_HTML_TAG',
|
||||
|
@ -89,7 +89,7 @@ const arrowCtrl = ContentState => {
|
||||
return
|
||||
}
|
||||
|
||||
if (block.type === 'pre' && block.functionType !== 'frontmatter') {
|
||||
if (block.type === 'pre' && /code|html/.test(block.functionType)) {
|
||||
// handle cursor in code block. the case at firstline or lastline.
|
||||
const cm = this.codeBlocks.get(id)
|
||||
const anchorBlock = block.functionType === 'html' ? this.getParent(this.getParent(block)) : block
|
||||
@ -205,8 +205,8 @@ const arrowCtrl = ContentState => {
|
||||
}
|
||||
|
||||
if (
|
||||
(preBlock && preBlock.type === 'pre' && preBlock.functionType !== 'frontmatter' && event.key === EVENT_KEYS.ArrowUp) ||
|
||||
(preBlock && preBlock.type === 'pre' && preBlock.functionType !== 'frontmatter' && event.key === EVENT_KEYS.ArrowLeft && left === 0)
|
||||
(preBlock && preBlock.type === 'pre' && /code|html/.test(preBlock.functionType) && event.key === EVENT_KEYS.ArrowUp) ||
|
||||
(preBlock && preBlock.type === 'pre' && /code|html/.test(preBlock.functionType) && event.key === EVENT_KEYS.ArrowLeft && left === 0)
|
||||
) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@ -222,8 +222,8 @@ const arrowCtrl = ContentState => {
|
||||
|
||||
return this.partialRender()
|
||||
} else if (
|
||||
(nextBlock && nextBlock.type === 'pre' && nextBlock.functionType !== 'frontmatter' && event.key === EVENT_KEYS.ArrowDown) ||
|
||||
(nextBlock && nextBlock.type === 'pre' && nextBlock.functionType !== 'frontmatter' && event.key === EVENT_KEYS.ArrowRight && right === 0)
|
||||
(nextBlock && nextBlock.type === 'pre' && /code|html/.test(nextBlock.functionType) && event.key === EVENT_KEYS.ArrowDown) ||
|
||||
(nextBlock && nextBlock.type === 'pre' && /code|html/.test(nextBlock.functionType) && event.key === EVENT_KEYS.ArrowRight && right === 0)
|
||||
) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
@ -104,6 +104,7 @@ const backspaceCtrl = ContentState => {
|
||||
const { start, end } = selection.getCursorRange()
|
||||
const startBlock = this.getBlock(start.key)
|
||||
const endBlock = this.getBlock(end.key)
|
||||
|
||||
// fix: #67 problem 1
|
||||
if (startBlock.icon) return event.preventDefault()
|
||||
// fix: unexpect remove all editor html. #67 problem 4
|
||||
@ -131,11 +132,11 @@ const backspaceCtrl = ContentState => {
|
||||
if (start.key !== end.key) {
|
||||
event.preventDefault()
|
||||
const { key, offset } = start
|
||||
const startRemainText = startBlock.type === 'pre' && startBlock.functionType !== 'frontmatter'
|
||||
const startRemainText = startBlock.type === 'pre' && /code|html/.test(startBlock.functionType)
|
||||
? startBlock.text.substring(0, offset - 1)
|
||||
: startBlock.text.substring(0, offset)
|
||||
|
||||
const endRemainText = endBlock.type === 'pre' && endBlock.functionType !== 'frontmatter'
|
||||
const endRemainText = endBlock.type === 'pre' && /code|html/.test(endBlock.functionType)
|
||||
? endBlock.text.substring(end.offset - 1)
|
||||
: endBlock.text.substring(end.offset)
|
||||
|
||||
@ -183,7 +184,7 @@ const backspaceCtrl = ContentState => {
|
||||
return tHeadHasContent || tBodyHasContent
|
||||
}
|
||||
|
||||
if (block.type === 'pre' && block.functionType !== 'frontmatter') {
|
||||
if (block.type === 'pre' && /code|html/.test(block.functionType)) {
|
||||
const cm = this.codeBlocks.get(id)
|
||||
// if event.preventDefault(), you can not use backspace in language input.
|
||||
if (isCursorAtBegin(cm) && onlyHaveOneLine(cm)) {
|
||||
@ -204,9 +205,10 @@ const backspaceCtrl = ContentState => {
|
||||
this.partialRender()
|
||||
}
|
||||
} else if (
|
||||
block.type === 'span' && block.functionType === 'frontmatter' &&
|
||||
left === 0 && !preBlock
|
||||
block.type === 'span' && /frontmatter|multiplemath/.test(block.functionType) &&
|
||||
left === 0 && !block.preSibling
|
||||
) {
|
||||
const isMathLine = block.functionType === 'multiplemath'
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
const { key } = block
|
||||
@ -216,6 +218,9 @@ const backspaceCtrl = ContentState => {
|
||||
delete line.functionType
|
||||
this.appendChild(pBlock, line)
|
||||
}
|
||||
if (isMathLine) {
|
||||
parent = this.getParent(parent)
|
||||
}
|
||||
this.insertBefore(pBlock, parent)
|
||||
this.removeBlock(parent)
|
||||
|
||||
@ -326,7 +331,7 @@ const backspaceCtrl = ContentState => {
|
||||
const { text } = block
|
||||
const key = preBlock.key
|
||||
const offset = preBlock.text.length
|
||||
if (preBlock.type === 'pre' && preBlock.functionType !== 'frontmatter') {
|
||||
if (preBlock.type === 'pre' && /code|html/.test(preBlock.functionType)) {
|
||||
const cm = this.codeBlocks.get(key)
|
||||
const value = cm.getValue() + text
|
||||
cm.setValue(value)
|
||||
|
@ -52,7 +52,7 @@ const copyCutCtrl = ContentState => {
|
||||
$(
|
||||
`.${CLASS_OR_ID['AG_REMOVE']}, .${CLASS_OR_ID['AG_TOOL_BAR']},
|
||||
.${CLASS_OR_ID['AG_MATH_RENDER']}, .${CLASS_OR_ID['AG_HTML_PREVIEW']},
|
||||
.${CLASS_OR_ID['AG_COPY_REMOVE']}`
|
||||
.${CLASS_OR_ID['AG_MATH_PREVIEW']}, .${CLASS_OR_ID['AG_COPY_REMOVE']}`
|
||||
).remove()
|
||||
$(`.${CLASS_OR_ID['AG_EMOJI_MARKER']}`).text(':')
|
||||
$(`.${CLASS_OR_ID['AG_NOTEXT_LINK']}`).empty()
|
||||
@ -102,6 +102,18 @@ const copyCutCtrl = ContentState => {
|
||||
})
|
||||
}
|
||||
|
||||
const mathBlock = $(`figure.ag-multiple-math-block`)
|
||||
if (mathBlock.length > 0) {
|
||||
mathBlock.each((i, hb) => {
|
||||
const ele = $(hb)
|
||||
const id = ele.attr('id')
|
||||
const { math } = this.getBlock(id).children[1]
|
||||
const pre = $('<pre class="multiple-math"></pre>')
|
||||
pre.text(math)
|
||||
ele.replaceWith(pre)
|
||||
})
|
||||
}
|
||||
|
||||
event.clipboardData.setData('text/html', $('body').html())
|
||||
event.clipboardData.setData('text/plain', text)
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ const enterCtrl = ContentState => {
|
||||
return
|
||||
}
|
||||
// handle cursor in code block
|
||||
if (block.type === 'pre' && block.functionType !== 'frontmatter') {
|
||||
if (block.type === 'pre' && block.functionType === 'code') {
|
||||
return
|
||||
}
|
||||
|
||||
@ -197,15 +197,13 @@ const enterCtrl = ContentState => {
|
||||
// only cursor in `line block` can create `soft line break` and `hard line break`
|
||||
if (
|
||||
(event.shiftKey && block.type === 'span') ||
|
||||
(block.type === 'span' && block.functionType === 'frontmatter')
|
||||
(block.type === 'span' && /frontmatter|multiplemath/.test(block.functionType))
|
||||
) {
|
||||
const { text } = block
|
||||
const newLineText = text.substring(start.offset)
|
||||
block.text = text.substring(0, start.offset)
|
||||
const newLine = this.createBlock('span', newLineText)
|
||||
if (block.functionType === 'frontmatter') {
|
||||
newLine.functionType = 'frontmatter'
|
||||
}
|
||||
newLine.functionType = block.functionType
|
||||
this.insertAfter(newLine, block)
|
||||
const { key } = newLine
|
||||
const offset = 0
|
||||
@ -374,6 +372,7 @@ const enterCtrl = ContentState => {
|
||||
const blockNeedFocus = this.codeBlockUpdate(preParagraphBlock)
|
||||
let tableNeedFocus = this.tableBlockUpdate(preParagraphBlock)
|
||||
let htmlNeedFocus = this.updateHtmlBlock(preParagraphBlock)
|
||||
let mathNeedFocus = this.updateMathBlock(preParagraphBlock)
|
||||
let cursorBlock
|
||||
|
||||
switch (true) {
|
||||
@ -386,6 +385,9 @@ const enterCtrl = ContentState => {
|
||||
case !!htmlNeedFocus:
|
||||
cursorBlock = htmlNeedFocus
|
||||
break
|
||||
case !!mathNeedFocus:
|
||||
cursorBlock = mathNeedFocus
|
||||
break
|
||||
default:
|
||||
cursorBlock = newBlock
|
||||
break
|
||||
|
@ -8,8 +8,7 @@ const DOMPurify = createDOMPurify(window)
|
||||
|
||||
const htmlBlock = ContentState => {
|
||||
ContentState.prototype.createToolBar = function (tools, toolBarType) {
|
||||
const toolBar = this.createBlock('div')
|
||||
toolBar.editable = false
|
||||
const toolBar = this.createBlock('div', '', false)
|
||||
toolBar.toolBarType = toolBarType
|
||||
const ul = this.createBlock('ul')
|
||||
|
||||
@ -28,8 +27,7 @@ const htmlBlock = ContentState => {
|
||||
ContentState.prototype.createCodeInHtml = function (code, selection) {
|
||||
const codeContainer = this.createBlock('div')
|
||||
codeContainer.functionType = 'html'
|
||||
const preview = this.createBlock('div')
|
||||
preview.editable = false
|
||||
const preview = this.createBlock('div', '', false)
|
||||
preview.htmlContent = DOMPurify.sanitize(escapeInBlockHtml(code), DOMPURIFY_CONFIG)
|
||||
preview.functionType = 'preview'
|
||||
const codePre = this.createBlock('pre')
|
||||
|
@ -51,7 +51,6 @@ class ContentState {
|
||||
this.blocks = [ this.createBlockP() ]
|
||||
this.stateRender = new StateRender(eventCenter)
|
||||
this.codeBlocks = new Map()
|
||||
this.loadMathMap = new Map()
|
||||
this.renderRange = [ null, null ]
|
||||
this.currentCursor = null
|
||||
this.prevCursor = null
|
||||
@ -122,7 +121,7 @@ class ContentState {
|
||||
setCursor () {
|
||||
const { start: { key } } = this.cursor
|
||||
const block = this.getBlock(key)
|
||||
if (block.type === 'pre' && block.functionType !== 'frontmatter') {
|
||||
if (block.type === 'pre' && /code|html/.test(block.functionType)) {
|
||||
const cm = this.codeBlocks.get(key)
|
||||
const { selection } = block
|
||||
if (selection) {
|
||||
@ -156,7 +155,6 @@ class ContentState {
|
||||
this.setNextRenderRange()
|
||||
this.stateRender.render(blocks, cursor, activeBlocks, matches)
|
||||
this.pre2CodeMirror(isRenderCursor)
|
||||
this.renderMath()
|
||||
if (isRenderCursor) this.setCursor()
|
||||
}
|
||||
|
||||
@ -175,7 +173,6 @@ class ContentState {
|
||||
this.setNextRenderRange()
|
||||
this.stateRender.partialRender(needRenderBlocks, cursor, activeBlocks, matches, startKey, endKey)
|
||||
this.pre2CodeMirror(true, [...new Set([cursorOutMostBlock, ...needRenderBlocks])])
|
||||
this.renderMath([...new Set([cursorOutMostBlock, ...needRenderBlocks])])
|
||||
this.setCursor()
|
||||
}
|
||||
|
||||
@ -183,12 +180,13 @@ class ContentState {
|
||||
* A block in Aganippe present a paragraph(block syntax in GFM) or a line in paragraph.
|
||||
* a line block must in a `p block` or `pre block(frontmatter)` and `p block`'s children must be line blocks.
|
||||
*/
|
||||
createBlock (type = 'span', text = '') { // span type means it is a line block.
|
||||
createBlock (type = 'span', text = '', editable = true) { // span type means it is a line block.
|
||||
const key = getUniqueId()
|
||||
return {
|
||||
key,
|
||||
type,
|
||||
text,
|
||||
editable,
|
||||
parent: null,
|
||||
preSibling: null,
|
||||
nextSibling: null,
|
||||
@ -310,7 +308,7 @@ class ContentState {
|
||||
if (children.length) {
|
||||
children.forEach(child => this.removeTextOrBlock(child))
|
||||
}
|
||||
} else {
|
||||
} else if (block.editable) {
|
||||
this.removeBlock(block)
|
||||
}
|
||||
}
|
||||
@ -365,8 +363,8 @@ class ContentState {
|
||||
if (!afterEnd) {
|
||||
const parent = this.getParent(after)
|
||||
if (parent) {
|
||||
const isOnlyChild = this.isOnlyChild(after)
|
||||
this.removeBlocks(before, parent, isOnlyChild, true)
|
||||
const removeAfter = isRemoveAfter && this.isOnlyEditableChild(after)
|
||||
this.removeBlocks(before, parent, removeAfter, true)
|
||||
}
|
||||
}
|
||||
if (isRemoveAfter) {
|
||||
@ -505,6 +503,13 @@ class ContentState {
|
||||
return !block.nextSibling && !block.preSibling
|
||||
}
|
||||
|
||||
isOnlyEditableChild (block) {
|
||||
if (block.editable === false) return false
|
||||
const parent = this.getParent(block)
|
||||
if (!parent) throw new Error('isOnlyEditableChild method only apply for child block')
|
||||
return parent.children.filter(child => child.editable).length === 1
|
||||
}
|
||||
|
||||
getLastChild (block) {
|
||||
if (block) {
|
||||
const len = block.children.length
|
||||
|
@ -1,40 +1,69 @@
|
||||
import katex from 'katex'
|
||||
import { CLASS_OR_ID } from '../config'
|
||||
|
||||
import 'katex/dist/katex.min.css'
|
||||
// import { CLASS_OR_ID } from '../config'
|
||||
const LINE_BREAKS_REG = /\n/
|
||||
|
||||
const mathCtrl = ContentState => {
|
||||
ContentState.prototype.renderMath = function (blocks) {
|
||||
let selector = ''
|
||||
|
||||
if (blocks) {
|
||||
selector = blocks.map(block => `#${block.key} .${CLASS_OR_ID['AG_MATH_RENDER']}`).join(', ')
|
||||
ContentState.prototype.createMathBlock = function (value = '') {
|
||||
const FUNCTION_TYPE = 'multiplemath'
|
||||
const mathBlock = this.createBlock('figure')
|
||||
const textArea = this.createBlock('pre')
|
||||
const mathPreview = this.createBlock('div', '', false)
|
||||
if (typeof value === 'string' && value) {
|
||||
const lines = value.replace(/^\s+/, '').split(LINE_BREAKS_REG).map(line => this.createBlock('span', line))
|
||||
for (const line of lines) {
|
||||
line.functionType = FUNCTION_TYPE
|
||||
this.appendChild(textArea, line)
|
||||
}
|
||||
} else {
|
||||
selector = `.${CLASS_OR_ID['AG_MATH_RENDER']}`
|
||||
const emptyLine = this.createBlock('span')
|
||||
emptyLine.functionType = FUNCTION_TYPE
|
||||
this.appendChild(textArea, emptyLine)
|
||||
}
|
||||
|
||||
const mathEles = document.querySelectorAll(selector)
|
||||
const { loadMathMap } = this
|
||||
for (const math of mathEles) {
|
||||
const content = math.getAttribute('data-math')
|
||||
const type = math.getAttribute('data-type')
|
||||
const displayMode = type === 'display_math'
|
||||
const key = `${content}_${type}`
|
||||
if (loadMathMap.has(key)) {
|
||||
math.innerHTML = loadMathMap.get(key)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const html = katex.renderToString(content, {
|
||||
displayMode
|
||||
})
|
||||
loadMathMap.set(key, html)
|
||||
math.innerHTML = html
|
||||
} catch (err) {
|
||||
math.innerHTML = 'Invalid'
|
||||
math.classList.add(CLASS_OR_ID['AG_MATH_ERROR'])
|
||||
}
|
||||
mathBlock.functionType = textArea.functionType = mathPreview.functionType = FUNCTION_TYPE
|
||||
mathPreview.math = value
|
||||
this.appendChild(mathBlock, textArea)
|
||||
this.appendChild(mathBlock, mathPreview)
|
||||
return mathBlock
|
||||
}
|
||||
|
||||
ContentState.prototype.initMathBlock = function (block) { // p block
|
||||
const FUNCTION_TYPE = 'multiplemath'
|
||||
const textArea = this.createBlock('pre')
|
||||
const emptyLine = this.createBlock('span')
|
||||
textArea.functionType = emptyLine.functionType = FUNCTION_TYPE
|
||||
this.appendChild(textArea, emptyLine)
|
||||
block.type = 'figure'
|
||||
block.functionType = FUNCTION_TYPE
|
||||
block.children = []
|
||||
|
||||
const mathPreview = this.createBlock('div', '', false)
|
||||
mathPreview.math = ''
|
||||
mathPreview.functionType = FUNCTION_TYPE
|
||||
|
||||
this.appendChild(block, textArea)
|
||||
this.appendChild(block, mathPreview)
|
||||
return emptyLine
|
||||
}
|
||||
|
||||
ContentState.prototype.handleMathBlockClick = function (mathFigure) {
|
||||
const { id } = mathFigure
|
||||
const mathBlock = this.getBlock(id)
|
||||
const textAreaBlock = mathBlock.children[0]
|
||||
const firstLine = textAreaBlock.children[0]
|
||||
const { key } = firstLine
|
||||
const offset = 0
|
||||
this.cursor = {
|
||||
start: { key, offset },
|
||||
end: { key, offset }
|
||||
}
|
||||
this.partialRender()
|
||||
}
|
||||
|
||||
ContentState.prototype.updateMathBlock = function (block) {
|
||||
const { type } = block
|
||||
if (type !== 'p') return false
|
||||
const { text } = block.children[0]
|
||||
return text.trim() === '$$' ? this.initMathBlock(block) : false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,6 +318,27 @@ const paragraphCtrl = ContentState => {
|
||||
}
|
||||
}
|
||||
|
||||
ContentState.prototype.insertMathBlock = function () {
|
||||
const { start, end } = selection.getCursorRange()
|
||||
if (start.key !== end.key) return
|
||||
let block = this.getBlock(start.key)
|
||||
if (block.type === 'span') {
|
||||
block = this.getParent(block)
|
||||
}
|
||||
const mathBlock = this.createMathBlock()
|
||||
this.insertAfter(mathBlock, block)
|
||||
if (block.type === 'p' && block.children.length === 1 && !block.children[0].text) {
|
||||
this.removeBlock(block)
|
||||
}
|
||||
const cursorBlock = mathBlock.children[0].children[0]
|
||||
const { key } = cursorBlock
|
||||
const offset = 0
|
||||
this.cursor = {
|
||||
start: { key, offset },
|
||||
end: { key, offset }
|
||||
}
|
||||
}
|
||||
|
||||
ContentState.prototype.updateParagraph = function (paraType) {
|
||||
const { start, end } = selection.getCursorRange()
|
||||
const block = this.getBlock(start.key)
|
||||
@ -346,6 +367,10 @@ const paragraphCtrl = ContentState => {
|
||||
this.handleQuoteMenu()
|
||||
break
|
||||
}
|
||||
case 'mathblock': {
|
||||
this.insertMathBlock()
|
||||
break
|
||||
}
|
||||
case 'heading 1':
|
||||
case 'heading 2':
|
||||
case 'heading 3':
|
||||
|
@ -131,10 +131,15 @@ const pasteCtrl = ContentState => {
|
||||
throw new Error('unknown paste type')
|
||||
}
|
||||
// step 3: set cursor and render
|
||||
const cursorBlock = this.getBlock(key)
|
||||
let cursorBlock = this.getBlock(key)
|
||||
if (!cursorBlock) {
|
||||
key = startBlock.key
|
||||
offset = startBlock.text.length - cacheText.length
|
||||
cursorBlock = startBlock
|
||||
}
|
||||
// TODO @Jocs duplicate with codes in updateCtrl.js
|
||||
if (cursorBlock && cursorBlock.type === 'span' && cursorBlock.functionType === 'multiplemath') {
|
||||
this.updateMathContent(cursorBlock)
|
||||
}
|
||||
this.cursor = {
|
||||
start: {
|
||||
|
@ -284,6 +284,13 @@ const updateCtrl = ContentState => {
|
||||
return null
|
||||
}
|
||||
|
||||
ContentState.prototype.updateMathContent = function (block) {
|
||||
const preBlock = this.getParent(block)
|
||||
const mathPreview = this.getNextSibling(preBlock)
|
||||
const math = preBlock.children.map(line => line.text).join('\n')
|
||||
mathPreview.math = math
|
||||
}
|
||||
|
||||
ContentState.prototype.updateState = function (event) {
|
||||
const { floatBox } = this
|
||||
const { start, end } = selection.getCursorRange()
|
||||
@ -314,7 +321,7 @@ const updateCtrl = ContentState => {
|
||||
this.removeBlocks(startBlock, endBlock)
|
||||
// there still has little bug, when the oldstart block is `pre`, the input value will be ignored.
|
||||
// and act as `backspace`
|
||||
if (startBlock.type === 'pre' && startBlock.functionType !== 'frontmatter') {
|
||||
if (startBlock.type === 'pre' && /code|html/.test(startBlock.functionType)) {
|
||||
event.preventDefault()
|
||||
const startRemainText = startBlock.type === 'pre'
|
||||
? startBlock.text.substring(0, oldStart.offset - 1)
|
||||
@ -379,7 +386,7 @@ const updateCtrl = ContentState => {
|
||||
}
|
||||
}
|
||||
|
||||
if (block && block.type === 'pre' && block.functionType !== 'frontmatter') {
|
||||
if (block && block.type === 'pre' && /code|html/.test(block.functionType)) {
|
||||
if (block.key !== oldKey) {
|
||||
this.cursor = lastCursor = { start, end }
|
||||
if (event.type === 'click' && oldKey) {
|
||||
@ -388,6 +395,7 @@ const updateCtrl = ContentState => {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// auto pair
|
||||
if (block && block.text !== text) {
|
||||
const BRACKET_HASH = {
|
||||
@ -423,13 +431,16 @@ const updateCtrl = ContentState => {
|
||||
block.text = text
|
||||
}
|
||||
|
||||
if (block && block.type === 'span' && block.functionType === 'multiplemath') {
|
||||
this.updateMathContent(block)
|
||||
}
|
||||
|
||||
if (oldKey !== key || oldStart.offset !== start.offset || oldEnd.offset !== end.offset) {
|
||||
needRender = true
|
||||
}
|
||||
this.cursor = lastCursor = { start, end }
|
||||
const checkMarkedUpdate = this.checkNeedRender(block)
|
||||
const inlineUpdatedBlock = this.isCollapse() && block.functionType !== 'frontmatter' && this.checkInlineUpdate(block)
|
||||
|
||||
const inlineUpdatedBlock = this.isCollapse() && !/frontmatter|multiplemath/.test(block.functionType) && this.checkInlineUpdate(block)
|
||||
if (checkMarkedUpdate || inlineUpdatedBlock || needRender) {
|
||||
this.partialRender()
|
||||
}
|
||||
|
@ -10,12 +10,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
h1.ag-active::before,
|
||||
h2.ag-active::before,
|
||||
h3.ag-active::before,
|
||||
h4.ag-active::before,
|
||||
h5.ag-active::before,
|
||||
h6.ag-active::before {
|
||||
#ag-editor-id > h1.ag-active::before,
|
||||
#ag-editor-id > h2.ag-active::before,
|
||||
#ag-editor-id > h3.ag-active::before,
|
||||
#ag-editor-id > h4.ag-active::before,
|
||||
#ag-editor-id > h5.ag-active::before,
|
||||
#ag-editor-id > h6.ag-active::before {
|
||||
content: attr(data-head);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@ -25,12 +25,10 @@ h6.ag-active::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -25px;
|
||||
border: 1px solid #C0C4CC;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: #C0C4CC;
|
||||
transform: scale(.7);
|
||||
font-weight: 100;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.ag-paragraph:empty::after,
|
||||
@ -43,6 +41,10 @@ h6.ag-active::before {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.ag-gray {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* .ag-soft-line-break::after {
|
||||
content: '↓';
|
||||
opacity: .5;
|
||||
@ -58,6 +60,7 @@ h6.ag-active::before {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
figure pre.ag-multiple-math,
|
||||
div.ag-function-html pre.ag-html-block {
|
||||
width: 0;
|
||||
height: 0;
|
||||
@ -67,7 +70,8 @@ div.ag-function-html pre.ag-html-block {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
div.ag-function-html.ag-active pre.ag-html-block {
|
||||
div.ag-function-html.ag-active pre.ag-html-block,
|
||||
figure.ag-active pre.ag-multiple-math {
|
||||
position: static;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
@ -101,37 +105,38 @@ span.ag-html-tag {
|
||||
span.ag-math {
|
||||
position: relative;
|
||||
color: purple;
|
||||
letter-spacing: 0.1em;
|
||||
font-family: monospace;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.ag-math > .ag-math-render {
|
||||
display: inline-block;
|
||||
background: rgb(79, 79, 79);
|
||||
padding: .3em 1em;
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
padding: .5rem;
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
color: #333;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.ag-math > .ag-math-error {
|
||||
color: rgba(242, 134, 94, .7);
|
||||
div.ag-math-empty {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.ag-math > .ag-math-render::before {
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
transform: rotate(45deg) translateX(-50%);
|
||||
background: rgb(79, 79, 79);
|
||||
left: 10px;
|
||||
top: -1px;
|
||||
div.ag-math-error,
|
||||
span.ag-math > .ag-math-render.ag-math-error {
|
||||
color: #e6a23c;
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.ag-math > .ag-math-render .katex-display {
|
||||
@ -151,9 +156,11 @@ span.ag-math {
|
||||
}
|
||||
|
||||
.ag-hide.ag-math > .ag-math-render {
|
||||
padding: 0;
|
||||
top: 0;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@ -164,7 +171,7 @@ span.ag-math {
|
||||
figure {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 25px;
|
||||
margin: 1rem 0;
|
||||
position: relative;
|
||||
}
|
||||
.ag-tool-bar {
|
||||
@ -221,29 +228,18 @@ figure.ag-active .ag-tool-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
figure[data-role=HTML] {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
figure.ag-active[data-role=HTML]::before,
|
||||
pre.ag-active[data-role=YAML]::before {
|
||||
figure.ag-active[data-role=HTML]::before {
|
||||
content: attr(data-role);
|
||||
width: auto;
|
||||
padding: 0 .3rem;
|
||||
letter-spacing: .1rem;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
line-height: 22px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -45px;
|
||||
border: 1px solid #C0C4CC;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
color: #C0C4CC;
|
||||
transform: scale(.7);
|
||||
font-weight: 300;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table {
|
||||
@ -340,12 +336,17 @@ p:not(.ag-active)[data-role="hr"] * {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
pre.ag-multiple-math,
|
||||
pre.ag-front-matter {
|
||||
position: relative;
|
||||
background: #f7f7f7;
|
||||
padding: 1rem;
|
||||
background: #f6f8fa;
|
||||
padding: .5rem;
|
||||
border: 5px;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
pre.ag-front-matter {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
span.ag-front-matter-line:first-of-type:empty::after {
|
||||
@ -353,6 +354,11 @@ span.ag-front-matter-line:first-of-type:empty::after {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
span.ag-multiple-math-line:first-of-type:empty::after {
|
||||
content: 'Input Mathematical Formula...';
|
||||
color: #999;
|
||||
}
|
||||
|
||||
figure,
|
||||
pre.ag-html-block,
|
||||
div.ag-function-html,
|
||||
@ -365,22 +371,70 @@ pre.ag-code-block {
|
||||
|
||||
pre.ag-code-block {
|
||||
margin: 1rem 0;
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
|
||||
pre.ag-active.ag-front-matter::before,
|
||||
pre.ag-active.ag-front-matter::after {
|
||||
content: '---';
|
||||
}
|
||||
|
||||
pre.ag-active.ag-multiple-math::before,
|
||||
pre.ag-active.ag-multiple-math::after {
|
||||
content: '$$';
|
||||
}
|
||||
|
||||
pre.ag-active.ag-code-block::before,
|
||||
pre.ag-active.ag-code-block::after {
|
||||
content: '```';
|
||||
}
|
||||
|
||||
pre.ag-active.ag-front-matter::before,
|
||||
pre.ag-active.ag-front-matter::after,
|
||||
pre.ag-active.ag-code-block::before,
|
||||
pre.ag-active.ag-code-block::after,
|
||||
pre.ag-active.ag-multiple-math::before,
|
||||
pre.ag-active.ag-multiple-math::after {
|
||||
color: #909399;
|
||||
font-family: monospace;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
pre.ag-active.ag-front-matter::before,
|
||||
pre.ag-active.ag-multiple-math::before,
|
||||
pre.ag-active.ag-code-block::before {
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
pre.ag-active.ag-front-matter::after,
|
||||
pre.ag-active.ag-multiple-math::after,
|
||||
pre.ag-active.ag-code-block::after {
|
||||
bottom: -25px;
|
||||
bottom: -23px;
|
||||
}
|
||||
|
||||
span.ag-multiple-math-line {
|
||||
color: purple;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
figure div.ag-math-preview {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
figure.ag-active div.ag-math-preview {
|
||||
position: absolute;
|
||||
top: calc(100% + 8px);
|
||||
left: 50%;
|
||||
width: auto;
|
||||
z-index: 1;
|
||||
transform: translateX(-50%);
|
||||
padding: .5rem;
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
div.ag-html-preview {
|
||||
|
@ -80,6 +80,7 @@ class Aganippe {
|
||||
this.dispatchTableToolBar()
|
||||
this.dispatchCodeBlockClick()
|
||||
this.htmlPreviewClick()
|
||||
this.mathPreviewClick()
|
||||
|
||||
contentState.listenForPathChange()
|
||||
|
||||
@ -429,6 +430,21 @@ class Aganippe {
|
||||
eventCenter.attachDOMEvent(container, 'click', handler)
|
||||
}
|
||||
|
||||
mathPreviewClick () {
|
||||
const { eventCenter, container } = this
|
||||
const handler = event => {
|
||||
const target = event.target
|
||||
const mathFigure = isInElement(target, 'ag-multiple-math-block')
|
||||
if (mathFigure && !mathFigure.classList.contains(CLASS_OR_ID['AG_ACTIVE'])) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
this.contentState.handleMathBlockClick(mathFigure)
|
||||
}
|
||||
}
|
||||
|
||||
eventCenter.attachDOMEvent(container, 'click', handler)
|
||||
}
|
||||
|
||||
listItemCheckBoxClick () {
|
||||
const { container, eventCenter } = this
|
||||
const handler = event => {
|
||||
|
@ -29,7 +29,8 @@ var block = {
|
||||
table: noop,
|
||||
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
|
||||
text: /^[^\n]+/,
|
||||
frontmatter: /^---\n([\s\S]+?)---(?:\n+|$)/
|
||||
frontmatter: /^---\n([\s\S]+?)---(?:\n+|$)/,
|
||||
multiplemath: /^\$\$\n([\s\S]+?)\n\$\$(?:\n+|$)/
|
||||
};
|
||||
|
||||
block.checkbox = /^\[([ x])\] +/;
|
||||
@ -195,16 +196,25 @@ Lexer.prototype.token = function(src, top, bq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// multiple line math
|
||||
if (cap = this.rules.multiplemath.exec(src)) {
|
||||
src = src.substring(cap[0].length)
|
||||
this.tokens.push({
|
||||
type: 'multiplemath',
|
||||
text: cap[1]
|
||||
})
|
||||
}
|
||||
|
||||
// fences (gfm)
|
||||
if (cap = this.rules.fences.exec(src)) {
|
||||
src = src.substring(cap[0].length);
|
||||
src = src.substring(cap[0].length)
|
||||
this.tokens.push({
|
||||
type: 'code',
|
||||
codeBlockStyle: 'fenced',
|
||||
lang: cap[2],
|
||||
text: cap[3]
|
||||
});
|
||||
continue;
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// heading
|
||||
@ -822,6 +832,10 @@ Renderer.prototype.frontmatter = function (text) {
|
||||
return `<pre class="front-matter">\n${text}</pre>\n`
|
||||
}
|
||||
|
||||
Renderer.prototype.multiplemath = function (text) {
|
||||
return `<pre class="multiple-math">\n${text}</pre>\n`
|
||||
}
|
||||
|
||||
Renderer.prototype.code = function (code, lang, escaped, codeBlockStyle) {
|
||||
if (this.options.highlight) {
|
||||
var out = this.options.highlight(code, lang);
|
||||
@ -1073,13 +1087,17 @@ Parser.prototype.tok = function() {
|
||||
return this.renderer.hr()
|
||||
}
|
||||
case 'heading': {
|
||||
return this.renderer.heading(
|
||||
this.inline.output(this.token.text),
|
||||
this.token.depth,
|
||||
this.token.text,
|
||||
this.token.headingStyle
|
||||
)
|
||||
}
|
||||
return this.renderer.heading(
|
||||
this.inline.output(this.token.text),
|
||||
this.token.depth,
|
||||
this.token.text,
|
||||
this.token.headingStyle
|
||||
)
|
||||
}
|
||||
case 'multiplemath': {
|
||||
const { text } = this.token
|
||||
return this.renderer.multiplemath(text)
|
||||
}
|
||||
case 'code':
|
||||
{
|
||||
const { codeBlockStyle, text, lang, escaped } = this.token
|
||||
|
@ -101,7 +101,7 @@ const tokenizerFac = (src, beginRules, inlineRules, pos = 0, top) => {
|
||||
}
|
||||
|
||||
if (beginRules && pos === 0) {
|
||||
const beginR = ['header', 'hr', 'code_fense', 'display_math']
|
||||
const beginR = ['header', 'hr', 'code_fense', 'display_math', 'multiple_math']
|
||||
|
||||
for (const ruleName of beginR) {
|
||||
const to = beginRules[ruleName].exec(src)
|
||||
@ -488,6 +488,7 @@ export const generator = tokens => {
|
||||
result += token.escapeCharacter
|
||||
break
|
||||
case 'tail_header':
|
||||
case 'multiple_math':
|
||||
result += token.marker
|
||||
break
|
||||
case 'hard_line_break':
|
||||
|
@ -9,6 +9,7 @@ class StateRender {
|
||||
constructor (eventCenter) {
|
||||
this.eventCenter = eventCenter
|
||||
this.loadImageMap = new Map()
|
||||
this.loadMathMap = new Map()
|
||||
this.tokenCache = new Map()
|
||||
this.container = null
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ export default function renderContainerBlock (block, cursor, activeBlocks, match
|
||||
if (block.functionType === 'html') { // HTML Block
|
||||
Object.assign(data.dataset, { role: block.functionType.toUpperCase() })
|
||||
}
|
||||
if (block.functionType === 'multiplemath') {
|
||||
selector += `.${CLASS_OR_ID['AG_MULTIPLE_MATH_BLOCK']}`
|
||||
}
|
||||
}
|
||||
// hanle list block
|
||||
if (/ul|ol/.test(block.type) && block.listType) {
|
||||
@ -72,9 +75,11 @@ export default function renderContainerBlock (block, cursor, activeBlocks, match
|
||||
if (block.type === 'ol') {
|
||||
Object.assign(data.attrs, { start: block.start })
|
||||
}
|
||||
if (block.type === 'pre' && block.functionType === 'frontmatter') {
|
||||
Object.assign(data.dataset, { role: 'YAML' })
|
||||
selector += `.${CLASS_OR_ID['AG_FRONT_MATTER']}`
|
||||
if (block.type === 'pre' && /frontmatter|multiplemath/.test(block.functionType)) {
|
||||
const role = block.functionType === 'frontmatter' ? 'YAML' : 'MATH'
|
||||
const className = block.functionType === 'frontmatter' ? CLASS_OR_ID['AG_FRONT_MATTER'] : CLASS_OR_ID['AG_MULTIPLE_MATH']
|
||||
Object.assign(data.dataset, { role })
|
||||
selector += `.${className}`
|
||||
}
|
||||
|
||||
return h(selector, data, block.children.map(child => this.renderBlock(child, cursor, activeBlocks, matches, useCache)))
|
||||
|
@ -1,3 +1,4 @@
|
||||
import katex from 'katex'
|
||||
import { CLASS_OR_ID, DEVICE_MEMORY } from '../../../config'
|
||||
import { tokenizer } from '../../parse'
|
||||
import { snakeToCamel } from '../../../utils'
|
||||
@ -9,13 +10,26 @@ const PRE_BLOCK_HASH = {
|
||||
'frontmatter': `.${CLASS_OR_ID['AG_FRONT_MATTER']}`
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default function renderLeafBlock (block, cursor, activeBlocks, matches, useCache = false) {
|
||||
const { loadMathMap } = this
|
||||
let selector = this.getSelector(block, cursor, activeBlocks)
|
||||
// highlight search key in block
|
||||
const highlights = matches.filter(m => m.key === block.key)
|
||||
const { text, type, headingStyle, align, htmlContent, icon, checked, key, lang, functionType, codeBlockStyle } = block
|
||||
const {
|
||||
text,
|
||||
type,
|
||||
headingStyle,
|
||||
align,
|
||||
htmlContent,
|
||||
icon,
|
||||
checked,
|
||||
key,
|
||||
lang,
|
||||
functionType,
|
||||
codeBlockStyle,
|
||||
math,
|
||||
editable
|
||||
} = block
|
||||
const data = {
|
||||
attrs: {},
|
||||
dataset: {}
|
||||
@ -33,16 +47,41 @@ export default function renderLeafBlock (block, cursor, activeBlocks, matches, u
|
||||
children = tokens.reduce((acc, token) => [...acc, ...this[snakeToCamel(token.type)](h, cursor, block, token)], [])
|
||||
}
|
||||
|
||||
if (editable === false) {
|
||||
Object.assign(data.attrs, {
|
||||
contenteditable: 'false'
|
||||
})
|
||||
}
|
||||
|
||||
if (/th|td/.test(type) && align) {
|
||||
Object.assign(data.attrs, {
|
||||
style: `text-align:${align}`
|
||||
})
|
||||
} else if (type === 'div' && htmlContent !== undefined) {
|
||||
selector += `.${CLASS_OR_ID['AG_HTML_PREVIEW']}`
|
||||
Object.assign(data.attrs, {
|
||||
contenteditable: 'false'
|
||||
})
|
||||
children = htmlToVNode(htmlContent)
|
||||
} else if (type === 'div') {
|
||||
if (typeof htmlContent === 'string') {
|
||||
selector += `.${CLASS_OR_ID['AG_HTML_PREVIEW']}`
|
||||
children = htmlToVNode(htmlContent)
|
||||
} else if (typeof math === 'string') {
|
||||
const key = `${math}_display_math`
|
||||
selector += `.${CLASS_OR_ID['AG_MATH_PREVIEW']}`
|
||||
if (math === '') {
|
||||
children = '< Empty Mathematical Formula >'
|
||||
selector += `.${CLASS_OR_ID['AG_MATH_EMPTY']}`
|
||||
} else if (loadMathMap.has(key)) {
|
||||
children = loadMathMap.get(key)
|
||||
} else {
|
||||
try {
|
||||
const html = katex.renderToString(math, {
|
||||
displayMode: true
|
||||
})
|
||||
children = htmlToVNode(html)
|
||||
loadMathMap.set(key, children)
|
||||
} catch (err) {
|
||||
children = '< Invalid Mathematical Formula >'
|
||||
selector += `.${CLASS_OR_ID['AG_MATH_ERROR']}`
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type === 'svg' && icon) {
|
||||
selector += '.icon'
|
||||
Object.assign(data.attrs, {
|
||||
@ -98,12 +137,17 @@ export default function renderLeafBlock (block, cursor, activeBlocks, matches, u
|
||||
})
|
||||
}
|
||||
|
||||
if (functionType !== 'frontmatter') {
|
||||
if (/code|html/.test(functionType)) {
|
||||
// do not set it to '' (empty string)
|
||||
children = []
|
||||
}
|
||||
} else if (type === 'span' && functionType === 'frontmatter') {
|
||||
selector += `.${CLASS_OR_ID['AG_FRONT_MATTER_LINE']}`
|
||||
} else if (type === 'span' && /frontmatter|multiplemath/.test(functionType)) {
|
||||
if (functionType === 'frontmatter') {
|
||||
selector += `.${CLASS_OR_ID['AG_FRONT_MATTER_LINE']}`
|
||||
}
|
||||
if (functionType === 'multiplemath') {
|
||||
selector += `.${CLASS_OR_ID['AG_MULTIPLE_MATH_LINE']}`
|
||||
}
|
||||
children = text
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
import katex from 'katex'
|
||||
import { CLASS_OR_ID } from '../../../config'
|
||||
import { htmlToVNode } from '../snabbdom'
|
||||
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
export default function displayMath (h, cursor, block, token, outerClass) {
|
||||
const className = this.getClassName(outerClass, block, token, cursor)
|
||||
@ -11,14 +15,34 @@ export default function displayMath (h, cursor, block, token, outerClass) {
|
||||
|
||||
const { content: math, type } = token
|
||||
|
||||
const { loadMathMap } = this
|
||||
|
||||
const displayMode = type === 'display_math'
|
||||
const key = `${math}_${type}`
|
||||
let mathVnode = null
|
||||
let previewSelector = `span.${CLASS_OR_ID['AG_MATH_RENDER']}`
|
||||
if (loadMathMap.has(key)) {
|
||||
mathVnode = loadMathMap.get(key)
|
||||
} else {
|
||||
try {
|
||||
const html = katex.renderToString(math, {
|
||||
displayMode
|
||||
})
|
||||
mathVnode = htmlToVNode(html)
|
||||
loadMathMap.set(key, mathVnode)
|
||||
} catch (err) {
|
||||
mathVnode = '< Invalid Mathematical Formula >'
|
||||
previewSelector += `.${CLASS_OR_ID['AG_MATH_ERROR']}`
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
h(`span.${className}.${CLASS_OR_ID['AG_MATH_MARKER']}`, startMarker),
|
||||
h(`span.${className}.${CLASS_OR_ID['AG_MATH']}`, [
|
||||
h(`span.${CLASS_OR_ID['AG_MATH_TEXT']}`, content),
|
||||
h(`span.${CLASS_OR_ID['AG_MATH_RENDER']}`, {
|
||||
dataset: { math, type },
|
||||
h(previewSelector, {
|
||||
attrs: { contenteditable: 'false' }
|
||||
}, 'Loading')
|
||||
}, mathVnode)
|
||||
]),
|
||||
h(`span.${className}.${CLASS_OR_ID['AG_MATH_MARKER']}`, endMarker)
|
||||
]
|
||||
|
@ -23,6 +23,7 @@ import del from './del'
|
||||
import em from './em'
|
||||
import strong from './strong'
|
||||
import htmlEscape from './htmlEscape'
|
||||
import multipleMath from './multipleMath'
|
||||
|
||||
export default {
|
||||
backlashInToken,
|
||||
@ -49,5 +50,6 @@ export default {
|
||||
del,
|
||||
em,
|
||||
strong,
|
||||
htmlEscape
|
||||
htmlEscape,
|
||||
multipleMath
|
||||
}
|
||||
|
9
src/editor/parser/render/renderInlines/multipleMath.js
Normal file
9
src/editor/parser/render/renderInlines/multipleMath.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { CLASS_OR_ID } from '../../../config'
|
||||
|
||||
export default function multipleMath (h, cursor, block, token, outerClass) {
|
||||
const { start, end } = token.range
|
||||
const content = this.highlight(h, block, start, end, token)
|
||||
return [
|
||||
h(`span.${CLASS_OR_ID['AG_GRAY']}.${CLASS_OR_ID['AG_REMOVE']}`, content)
|
||||
]
|
||||
}
|
@ -5,7 +5,8 @@ export const beginRules = {
|
||||
'hr': /^(\*{3,}$|^\-{3,}$|^\_{3,}$)/,
|
||||
'code_fense': /^(`{3,})([^`]*)$/,
|
||||
'header': /(^\s{0,3}#{1,6}(\s{1,}|$))/,
|
||||
'display_math': /^(\$\$)([^\$]*?[^\$\\])(\\*)\1$/
|
||||
'display_math': /^(\$\$)([^\$]*?[^\$\\])(\\*)\1$/,
|
||||
'multiple_math': /^(\$\$)$/
|
||||
}
|
||||
|
||||
export const inlineRules = {
|
||||
|
@ -55,6 +55,8 @@ class ExportMarkdown {
|
||||
result.push(this.normalizeTable(table, indent))
|
||||
} else if (block.functionType === 'html') {
|
||||
result.push(this.normalizeHTML(block, indent))
|
||||
} else if (block.functionType === 'multiplemath') {
|
||||
result.push(this.normalizeMultipleMath(block, indent))
|
||||
}
|
||||
break
|
||||
|
||||
@ -159,6 +161,16 @@ class ExportMarkdown {
|
||||
return result.join('')
|
||||
}
|
||||
|
||||
normalizeMultipleMath (block, /* figure */ indent) {
|
||||
const result = []
|
||||
result.push('$$\n')
|
||||
for (const line of block.children[0].children) {
|
||||
result.push(`${line.text}\n`)
|
||||
}
|
||||
result.push('$$\n')
|
||||
return result.join('')
|
||||
}
|
||||
|
||||
normalizeCodeBlock (block, indent) {
|
||||
const result = []
|
||||
const textList = block.text.split(LINE_BREAKS)
|
||||
|
@ -65,12 +65,15 @@ const importRegister = ContentState => {
|
||||
return { lang, codeBlockStyle }
|
||||
}
|
||||
|
||||
const isFrontMatter = node => {
|
||||
const getPreFunctionType = node => {
|
||||
let type = 'code'
|
||||
const classAttr = node.attrs.filter(attr => attr.name === 'class')[0]
|
||||
if (classAttr && classAttr.value) {
|
||||
return /front-matter/.test(classAttr.value)
|
||||
const { value } = classAttr
|
||||
if (/front-matter/.test(value)) type = 'frontmatter'
|
||||
if (/multiple-math/.test(value)) type = 'multiplemath'
|
||||
}
|
||||
return false
|
||||
return type
|
||||
}
|
||||
|
||||
const getRowColumnCount = childNodes => {
|
||||
@ -219,17 +222,20 @@ const importRegister = ContentState => {
|
||||
break
|
||||
|
||||
case 'pre':
|
||||
const frontMatter = isFrontMatter(child)
|
||||
if (frontMatter) {
|
||||
const functionType = getPreFunctionType(child)
|
||||
if (functionType === 'frontmatter') {
|
||||
value = child.childNodes[0].value
|
||||
block = this.createBlock('pre')
|
||||
const lines = value.replace(/^\s+/, '').split(LINE_BREAKS_REG).map(line => this.createBlock('span', line))
|
||||
for (const line of lines) {
|
||||
line.functionType = 'frontmatter'
|
||||
line.functionType = functionType
|
||||
this.appendChild(block, line)
|
||||
}
|
||||
block.functionType = 'frontmatter'
|
||||
} else {
|
||||
block.functionType = functionType
|
||||
} else if (functionType === 'multiplemath') {
|
||||
value = child.childNodes[0].value
|
||||
block = this.createMathBlock(value)
|
||||
} else if (functionType === 'code') {
|
||||
const codeNode = child.childNodes[0]
|
||||
const { lang, codeBlockStyle } = getLangAndType(codeNode)
|
||||
value = codeNode.childNodes[0].value
|
||||
@ -343,19 +349,29 @@ const importRegister = ContentState => {
|
||||
// set cursor
|
||||
const travel = blocks => {
|
||||
for (const block of blocks) {
|
||||
const { key, text, children } = block
|
||||
const { key, text, children, editable, type, functionType } = block
|
||||
if (text) {
|
||||
const offset = text.indexOf(CURSOR_DNA)
|
||||
if (offset > -1) {
|
||||
block.text = text.substring(0, offset) + text.substring(offset + CURSOR_DNA.length)
|
||||
this.cursor = {
|
||||
start: { key, offset },
|
||||
end: { key, offset }
|
||||
if (editable) {
|
||||
this.cursor = {
|
||||
start: { key, offset },
|
||||
end: { key, offset }
|
||||
}
|
||||
// handle cursor in Math block, need to remove `CURSOR_DNA` in preview block
|
||||
if (type === 'span' && functionType === 'multiplemath') {
|
||||
const mathPreview = this.getNextSibling(this.getParent(block))
|
||||
const { math } = mathPreview
|
||||
const offset = math.indexOf(CURSOR_DNA)
|
||||
if (offset > -1) {
|
||||
mathPreview.math = math.substring(0, offset) + math.substring(offset + CURSOR_DNA.length)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if (children.length) {
|
||||
} else if (children.length) {
|
||||
travel(children)
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,16 @@ export const usePluginAddRules = turndownService => {
|
||||
}
|
||||
})
|
||||
|
||||
// handle multiple lines math
|
||||
turndownService.addRule('multiplemath', {
|
||||
filter (node, options) {
|
||||
return node.nodeName === 'PRE' && node.classList.contains('multiple-math')
|
||||
},
|
||||
replacement (content, node, options) {
|
||||
return `$$\n${content}\n$$`
|
||||
}
|
||||
})
|
||||
|
||||
// handle `soft line break` and `hard line break`
|
||||
// add `LINE_BREAK` to the end of soft line break and hard line break.
|
||||
turndownService.addRule('lineBreak', {
|
||||
|
@ -68,6 +68,14 @@ const setCheckedMenuItem = affiliation => {
|
||||
return item.id === 'frontMatterMenuItem'
|
||||
} else if (b.functionType === 'code') {
|
||||
return item.id === 'codeFencesMenuItem'
|
||||
} else if (b.functionType === 'html') {
|
||||
return false
|
||||
} else if (b.functionType === 'multiplemath') {
|
||||
return item.id === 'mathBlockMenuItem'
|
||||
}
|
||||
} else if (b.type === 'figure' && b.functionType) {
|
||||
if (b.functionType === 'table') {
|
||||
return item.id === 'tableMenuItem'
|
||||
}
|
||||
} else {
|
||||
return b.type === MENU_ID_MAP[item.id]
|
||||
@ -90,10 +98,15 @@ ipcMain.on('AGANI::selection-change', (e, { start, end, affiliation }) => {
|
||||
setCheckedMenuItem(affiliation)
|
||||
// handle disable
|
||||
setParagraphMenuItemStatus(true)
|
||||
|
||||
if (
|
||||
(/th|td/.test(start.type) && /th|td/.test(end.type)) ||
|
||||
(start.type === 'span' && start.block.functionType === 'frontmatter') ||
|
||||
(end.type === 'span' && end.block.functionType === 'frontmatter')
|
||||
(end.type === 'span' && end.block.functionType === 'frontmatter') ||
|
||||
(start.type === 'span' && start.block.functionType === 'multiplemath') ||
|
||||
(end.type === 'span' && end.block.functionType === 'multiplemath') ||
|
||||
(start.type === 'pre' && start.block.functionType === 'html') ||
|
||||
(end.type === 'pre' && end.block.functionType === 'html')
|
||||
) {
|
||||
setParagraphMenuItemStatus(false)
|
||||
} else if (start.key !== end.key) {
|
||||
|
@ -7,9 +7,6 @@
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// Set environment for development
|
||||
process.env.NODE_ENV = 'development'
|
||||
|
||||
// Install `electron-debug` with `devtron`
|
||||
require('electron-debug')({ showDevTools: false })
|
||||
|
||||
|
@ -93,6 +93,14 @@ export default {
|
||||
click (menuItem, browserWindow) {
|
||||
actions.paragraph(browserWindow, 'blockquote')
|
||||
}
|
||||
}, {
|
||||
id: 'mathBlockMenuItem',
|
||||
label: 'Math Block',
|
||||
type: 'checkbox',
|
||||
accelerator: 'Alt+CmdOrCtrl+M',
|
||||
click (menuItem, browserWindow) {
|
||||
actions.paragraph(browserWindow, 'mathblock')
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
|
@ -67,9 +67,9 @@
|
||||
|
||||
const STANDAR_Y = 320
|
||||
const PARAGRAPH_CMD = [
|
||||
'ul-bullet', 'ul-task', 'ol-order', 'pre', 'blockquote', 'heading 1', 'heading 2', 'heading 3',
|
||||
'heading 4', 'heading 5', 'heading 6', 'upgrade heading', 'degrade heading', 'paragraph', 'hr',
|
||||
'loose-list-item', 'front-matter'
|
||||
'ul-bullet', 'ul-task', 'ol-order', 'pre', 'blockquote', 'mathblock', 'heading 1', 'heading 2',
|
||||
'heading 3', 'heading 4', 'heading 5', 'heading 6', 'upgrade heading', 'degrade heading',
|
||||
'paragraph', 'hr', 'loose-list-item', 'front-matter'
|
||||
]
|
||||
|
||||
export default {
|
||||
|
@ -109,8 +109,8 @@ export const adjustCursor = (cursor, preline, line, nextline) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Need to adjust the cursor when cursor in the first or last line of code block.
|
||||
if (/```[\S]*/.test(line)) {
|
||||
// Need to adjust the cursor when cursor in the first or last line of code/math block.
|
||||
if (/```[\S]*/.test(line) || /^\$\$$/.test(line)) {
|
||||
if (typeof nextline === 'string' && /\S/.test(nextline)) {
|
||||
newCursor.line += 1
|
||||
newCursor.ch = 0
|
||||
|
@ -372,10 +372,30 @@ code {
|
||||
}
|
||||
|
||||
#ag-editor-id pre.ag-html-block {
|
||||
padding: .4rem 1rem;
|
||||
padding: 0 .5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#ag-editor-id pre.ag-front-matter {
|
||||
background: transparent;
|
||||
border-bottom: 1px dashed #efefef;
|
||||
}
|
||||
|
||||
#ag-editor-id pre.ag-multiple-math {
|
||||
background: transparent;
|
||||
border: 1px solid #909399;
|
||||
}
|
||||
|
||||
#ag-editor-id span.ag-math-text,
|
||||
#ag-editor-id pre.ag-multiple-math span.ag-multiple-math-line {
|
||||
color: lightsalmon;
|
||||
}
|
||||
|
||||
#ag-editor-id div.ag-math-preview {
|
||||
background: #303133;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
#ag-editor-id pre.ag-code-block,
|
||||
#ag-editor-id pre.ag-html-block {
|
||||
font-size: 90%;
|
||||
|
@ -48,7 +48,7 @@ body {
|
||||
}
|
||||
|
||||
.ag-gray {
|
||||
color: #E4E7ED;
|
||||
color: #C0C4CC;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@ -317,12 +317,13 @@ tt {
|
||||
|
||||
/* custom add */
|
||||
code {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27,31,35,0.05);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #c7254e;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
color: #24292e;
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
@ -355,12 +356,12 @@ code {
|
||||
|
||||
#ag-editor-id pre.ag-html-block {
|
||||
background: transparent;
|
||||
padding: .4rem 1rem;
|
||||
padding: 0 .5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#ag-editor-id pre.ag-active.ag-html-block {
|
||||
background: #F2F6FC;
|
||||
background: #f6f8fa;
|
||||
}
|
||||
|
||||
p:not(.ag-active)[data-role="hr"]::before {
|
||||
|
Loading…
Reference in New Issue
Block a user