mirror of
https://github.com/marktext/marktext.git
synced 2025-05-06 04:42:02 +08:00
feat: add update to footnote
This commit is contained in:
parent
37b96c8833
commit
fc89d04acc
BIN
src/muya/lib/assets/pngicon/footnote/2.png
Normal file
BIN
src/muya/lib/assets/pngicon/footnote/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -180,6 +180,56 @@ figure[data-role="HTML"].ag-active .ag-html-preview {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"] {
|
||||||
|
position: relative;
|
||||||
|
background: var(--footnoteBgColor);
|
||||||
|
padding: .5em 1em;
|
||||||
|
font-size: .8em;
|
||||||
|
opacity: .8;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"].ag-active::before {
|
||||||
|
content: attr(data-role);
|
||||||
|
text-transform: lowercase;
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 10px;
|
||||||
|
color: var(--editorColor30);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"] pre {
|
||||||
|
font-size: .8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"] .ag-footnote-input {
|
||||||
|
padding: 0 1em;
|
||||||
|
min-width: 80px;
|
||||||
|
position: absolute;
|
||||||
|
top: -23px;
|
||||||
|
left: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--editorColor);
|
||||||
|
background: transparent;
|
||||||
|
z-index: 1;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"].ag-active .ag-footnote-input {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"] .ag-footnote-input::before {
|
||||||
|
content: '[^';
|
||||||
|
}
|
||||||
|
|
||||||
|
figure[data-role="FOOTNOTE"] .ag-footnote-input::after {
|
||||||
|
content: ']:';
|
||||||
|
}
|
||||||
|
|
||||||
.ag-highlight {
|
.ag-highlight {
|
||||||
animation-name: highlight;
|
animation-name: highlight;
|
||||||
animation-duration: .25s;
|
animation-duration: .25s;
|
||||||
|
43
src/muya/lib/contentState/footnoteCtrl.js
Normal file
43
src/muya/lib/contentState/footnoteCtrl.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* eslint-disable no-useless-escape */
|
||||||
|
const FOOTNOTE_REG = /^\[\^([^\^\[\]\s]+?)(?<!\\)\]: /
|
||||||
|
/* eslint-enable no-useless-escape */
|
||||||
|
const footnoteCtrl = ContentState => {
|
||||||
|
ContentState.prototype.updateFootnote = function (block, line) {
|
||||||
|
const { start, end } = this.cursor
|
||||||
|
const { text } = line
|
||||||
|
const match = FOOTNOTE_REG.exec(text)
|
||||||
|
const footnoteIdentifer = match[1]
|
||||||
|
const sectionWrapper = this.createBlock('figure', {
|
||||||
|
functionType: 'footnote'
|
||||||
|
})
|
||||||
|
const footnoteInput = this.createBlock('span', {
|
||||||
|
text: footnoteIdentifer,
|
||||||
|
functionType: 'footnoteInput'
|
||||||
|
})
|
||||||
|
const pBlock = this.createBlockP(text.substring(match[0].length))
|
||||||
|
this.appendChild(sectionWrapper, footnoteInput)
|
||||||
|
this.appendChild(sectionWrapper, pBlock)
|
||||||
|
this.insertBefore(sectionWrapper, block)
|
||||||
|
this.removeBlock(block)
|
||||||
|
|
||||||
|
const { key } = pBlock.children[0]
|
||||||
|
this.cursor = {
|
||||||
|
start: {
|
||||||
|
key,
|
||||||
|
offset: Math.max(0, start.offset - footnoteIdentifer.length)
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
key,
|
||||||
|
offset: Math.max(0, end.offset - footnoteIdentifer.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isCollapse()) {
|
||||||
|
this.checkInlineUpdate(pBlock.children[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.partialRender()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default footnoteCtrl
|
@ -28,6 +28,7 @@ import emojiCtrl from './emojiCtrl'
|
|||||||
import imageCtrl from './imageCtrl'
|
import imageCtrl from './imageCtrl'
|
||||||
import linkCtrl from './linkCtrl'
|
import linkCtrl from './linkCtrl'
|
||||||
import dragDropCtrl from './dragDropCtrl'
|
import dragDropCtrl from './dragDropCtrl'
|
||||||
|
import footnoteCtrl from './footnoteCtrl'
|
||||||
import importMarkdown from '../utils/importMarkdown'
|
import importMarkdown from '../utils/importMarkdown'
|
||||||
import Cursor from '../selection/cursor'
|
import Cursor from '../selection/cursor'
|
||||||
import escapeCharactersMap, { escapeCharacters } from '../parser/escapeCharacter'
|
import escapeCharactersMap, { escapeCharacters } from '../parser/escapeCharacter'
|
||||||
@ -58,6 +59,7 @@ const prototypes = [
|
|||||||
imageCtrl,
|
imageCtrl,
|
||||||
linkCtrl,
|
linkCtrl,
|
||||||
dragDropCtrl,
|
dragDropCtrl,
|
||||||
|
footnoteCtrl,
|
||||||
importMarkdown
|
importMarkdown
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ const INLINE_UPDATE_FRAGMENTS = [
|
|||||||
'^(?:[\\s\\S]+?)\\n {0,3}(\\={3,}|\\-{3,})(?= {1,}|$)', // Setext headings **match from beginning**
|
'^(?:[\\s\\S]+?)\\n {0,3}(\\={3,}|\\-{3,})(?= {1,}|$)', // Setext headings **match from beginning**
|
||||||
'(?:^|\n) {0,3}(>).+', // Block quote
|
'(?:^|\n) {0,3}(>).+', // Block quote
|
||||||
'^( {4,})', // Indent code **match from beginning**
|
'^( {4,})', // Indent code **match from beginning**
|
||||||
|
'^(\\[\\^[^\\^\\[\\]\\s]+?(?<!\\\\)\\]: )', // Footnote **match from beginning**
|
||||||
'(?:^|\n) {0,3}((?:\\* *\\* *\\*|- *- *-|_ *_ *_)[ \\*\\-\\_]*)$' // Thematic break
|
'(?:^|\n) {0,3}((?:\\* *\\* *\\*|- *- *-|_ *_ *_)[ \\*\\-\\_]*)$' // Thematic break
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ const updateCtrl = ContentState => {
|
|||||||
if (/figure/.test(block.type)) {
|
if (/figure/.test(block.type)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (/cellContent|codeContent|languageInput/.test(block.functionType)) {
|
if (/cellContent|codeContent|languageInput|footnoteInput/.test(block.functionType)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,8 +90,9 @@ const updateCtrl = ContentState => {
|
|||||||
const listItem = this.getParent(block)
|
const listItem = this.getParent(block)
|
||||||
const [
|
const [
|
||||||
match, bullet, tasklist, order, atxHeader,
|
match, bullet, tasklist, order, atxHeader,
|
||||||
setextHeader, blockquote, indentCode, hr
|
setextHeader, blockquote, indentCode, footnote, hr
|
||||||
] = text.match(INLINE_UPDATE_REG) || []
|
] = text.match(INLINE_UPDATE_REG) || []
|
||||||
|
const { footnote: isSupportFootnote } = this.muya.options
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case (!!hr && new Set(hr.split('').filter(i => /\S/.test(i))).size === 1):
|
case (!!hr && new Set(hr.split('').filter(i => /\S/.test(i))).size === 1):
|
||||||
@ -118,6 +120,9 @@ const updateCtrl = ContentState => {
|
|||||||
case !!indentCode:
|
case !!indentCode:
|
||||||
return this.updateIndentCode(block, line)
|
return this.updateIndentCode(block, line)
|
||||||
|
|
||||||
|
case !!footnote && block.type === 'p' && !block.parent && isSupportFootnote:
|
||||||
|
return this.updateFootnote(block, line)
|
||||||
|
|
||||||
case !match:
|
case !match:
|
||||||
default:
|
default:
|
||||||
return this.updateToParagraph(block, line)
|
return this.updateToParagraph(block, line)
|
||||||
|
@ -21,6 +21,7 @@ import flowchartIcon from '../../../assets/pngicon/flowchart/2.png'
|
|||||||
import sequenceIcon from '../../../assets/pngicon/sequence/2.png'
|
import sequenceIcon from '../../../assets/pngicon/sequence/2.png'
|
||||||
import mermaidIcon from '../../../assets/pngicon/mermaid/2.png'
|
import mermaidIcon from '../../../assets/pngicon/mermaid/2.png'
|
||||||
import vegaIcon from '../../../assets/pngicon/chart/2.png'
|
import vegaIcon from '../../../assets/pngicon/chart/2.png'
|
||||||
|
import footnoteIcon from '../../../assets/pngicon/footnote/2.png'
|
||||||
|
|
||||||
const FUNCTION_TYPE_HASH = {
|
const FUNCTION_TYPE_HASH = {
|
||||||
mermaid: mermaidIcon,
|
mermaid: mermaidIcon,
|
||||||
@ -32,7 +33,8 @@ const FUNCTION_TYPE_HASH = {
|
|||||||
multiplemath: mathblockIcon,
|
multiplemath: mathblockIcon,
|
||||||
fencecode: codeIcon,
|
fencecode: codeIcon,
|
||||||
indentcode: codeIcon,
|
indentcode: codeIcon,
|
||||||
frontmatter: frontMatterIcon
|
frontmatter: frontMatterIcon,
|
||||||
|
footnote: footnoteIcon
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function renderIcon (block) {
|
export default function renderIcon (block) {
|
||||||
|
@ -42,6 +42,6 @@ export const inlineExtensionRules = {
|
|||||||
// This is not the best regexp, because it not support `2^2\\^`.
|
// This is not the best regexp, because it not support `2^2\\^`.
|
||||||
superscript: /^(\^)((?:[^\^\s]|(?<=\\)\1|(?<=\\) )+?)(?<!\\)\1(?!\1)/,
|
superscript: /^(\^)((?:[^\^\s]|(?<=\\)\1|(?<=\\) )+?)(?<!\\)\1(?!\1)/,
|
||||||
subscript: /^(~)((?:[^~\s]|(?<=\\)\1|(?<=\\) )+?)(?<!\\)\1(?!\1)/,
|
subscript: /^(~)((?:[^~\s]|(?<=\\)\1|(?<=\\) )+?)(?<!\\)\1(?!\1)/,
|
||||||
footnote_identifier: /^(\[\^)([^\^\[\]\s])+?(?<!\\)\]/
|
footnote_identifier: /^(\[\^)([^\^\[\]\s]+?)(?<!\\)\]/
|
||||||
}
|
}
|
||||||
/* eslint-enable no-useless-escape */
|
/* eslint-enable no-useless-escape */
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
--iconColor: #6B737B;
|
--iconColor: #6B737B;
|
||||||
--codeBgColor: #d8d8d869;
|
--codeBgColor: #d8d8d869;
|
||||||
--codeBlockBgColor: rgba(0, 0, 0, 0.03);
|
--codeBlockBgColor: rgba(0, 0, 0, 0.03);
|
||||||
|
--footnoteBgColor: rgba(0, 0, 0, .03);
|
||||||
--inputBgColor: rgba(0, 0, 0, .06);
|
--inputBgColor: rgba(0, 0, 0, .06);
|
||||||
--focusColor: var(--themeColor);
|
--focusColor: var(--themeColor);
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
--iconColor: rgba(255, 255, 255, .56);
|
--iconColor: rgba(255, 255, 255, .56);
|
||||||
--codeBgColor: #424344;
|
--codeBgColor: #424344;
|
||||||
--codeBlockBgColor: #424344;
|
--codeBlockBgColor: #424344;
|
||||||
|
--footnoteBgColor: rgba(66, 67, 68, .3);
|
||||||
--inputBgColor: #2f3336;
|
--inputBgColor: #2f3336;
|
||||||
|
|
||||||
--focusColor: var(--themeColor);
|
--focusColor: var(--themeColor);
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
--iconColor: rgba(150, 150, 150, .8);
|
--iconColor: rgba(150, 150, 150, .8);
|
||||||
--codeBgColor: #d8d8d869;
|
--codeBgColor: #d8d8d869;
|
||||||
--codeBlockBgColor: rgba(104, 134, 170, .05);
|
--codeBlockBgColor: rgba(104, 134, 170, .05);
|
||||||
|
--footnoteBgColor: rgba(0, 0, 0, .03);
|
||||||
--inputBgColor: rgba(0, 0, 0, .06);
|
--inputBgColor: rgba(0, 0, 0, .06);
|
||||||
--focusColor: var(--themeColor);
|
--focusColor: var(--themeColor);
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
--iconColor: rgba(255, 255, 255, .56);
|
--iconColor: rgba(255, 255, 255, .56);
|
||||||
--codeBgColor: #d8d8d869;
|
--codeBgColor: #d8d8d869;
|
||||||
--codeBlockBgColor: #3f454c;
|
--codeBlockBgColor: #3f454c;
|
||||||
|
--footnoteBgColor: rgba(66, 67, 68, .5);
|
||||||
--inputBgColor: rgba(0, 0, 0, .1);
|
--inputBgColor: rgba(0, 0, 0, .1);
|
||||||
--focusColor: var(--themeColor);
|
--focusColor: var(--themeColor);
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
--iconColor: rgba(255, 255, 255, .56);
|
--iconColor: rgba(255, 255, 255, .56);
|
||||||
--codeBgColor: #3a3f4b;
|
--codeBgColor: #3a3f4b;
|
||||||
--codeBlockBgColor: #3a3f4b;
|
--codeBlockBgColor: #3a3f4b;
|
||||||
|
--footnoteBgColor: rgba(66, 67, 68, .5);
|
||||||
--inputBgColor: rgba(0, 0, 0, .1);
|
--inputBgColor: rgba(0, 0, 0, .1);
|
||||||
--focusColor: #568af2;
|
--focusColor: #568af2;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
--iconColor: rgba(101, 101, 101, .8);
|
--iconColor: rgba(101, 101, 101, .8);
|
||||||
--codeBgColor: #d8d8d869;
|
--codeBgColor: #d8d8d869;
|
||||||
--codeBlockBgColor: rgba(12, 139, 186, .05);
|
--codeBlockBgColor: rgba(12, 139, 186, .05);
|
||||||
|
--footnoteBgColor: rgba(0, 0, 0, .03);
|
||||||
--inputBgColor: rgba(0, 0, 0, .06);
|
--inputBgColor: rgba(0, 0, 0, .06);
|
||||||
--focusColor: var(--themeColor);
|
--focusColor: var(--themeColor);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user