From 1ab9b580316d67a18749bd830cd721f8665d5f01 Mon Sep 17 00:00:00 2001 From: Philip Molares Date: Wed, 30 Sep 2020 23:50:32 +0200 Subject: [PATCH] Extract clean title from first heading (#616) * removed first-header-extractor get first heading from markdown rende * don't remove editor or renderer just hide them this way both are always up to date and can be shown very fast * extracted image alt attribute into first title. too * added tests as suggested by @mrdrogdrog --- cypress/integration/documentTitle.spec.ts | 18 +++++++++++ src/components/editor/editor.tsx | 2 +- src/components/editor/splitter/splitter.tsx | 16 ++++------ .../full-markdown-renderer.tsx | 32 +++++++++++++------ .../first-header-extractor.ts | 26 --------------- 5 files changed, 47 insertions(+), 47 deletions(-) delete mode 100644 src/components/markdown-renderer/markdown-it-plugins/first-header-extractor.ts diff --git a/cypress/integration/documentTitle.spec.ts b/cypress/integration/documentTitle.spec.ts index 7d6173b5c..f031387dd 100644 --- a/cypress/integration/documentTitle.spec.ts +++ b/cypress/integration/documentTitle.spec.ts @@ -63,5 +63,23 @@ describe('Document Title', () => { .type(`# ${title} [link](https://codimd.org)`) cy.title().should('eq', `${title} link - HedgeDoc @ ${branding.name}`) }) + + it('markdown syntax removed first', () => { + cy.get('.CodeMirror textarea') + .type(`# ${title} 1*2*3 4*5**`) + cy.title().should('eq', `${title} 123 4*5** - HedgeDoc @ ${branding.name}`) + }) + + it('markdown syntax removed second', () => { + cy.get('.CodeMirror textarea') + .type(`# ${title} **1 2*`) + cy.title().should('eq', `${title} *1 2 - HedgeDoc @ ${branding.name}`) + }) + + it('markdown syntax removed third', () => { + cy.get('.CodeMirror textarea') + .type(`# ${title} _asd_`) + cy.title().should('eq', `${title} asd - HedgeDoc @ ${branding.name}`) + }) }) }) diff --git a/src/components/editor/editor.tsx b/src/components/editor/editor.tsx index 4f8269b1f..e856144aa 100644 --- a/src/components/editor/editor.tsx +++ b/src/components/editor/editor.tsx @@ -54,7 +54,7 @@ export const Editor: React.FC = () => { } else if (noteMetadata.current?.opengraph && noteMetadata.current?.opengraph.get('title') && noteMetadata.current?.opengraph.get('title') !== '') { setDocumentTitle(noteMetadata.current.opengraph.get('title') ?? untitledNote) } else { - setDocumentTitle(firstHeading.current ?? untitledNote) + setDocumentTitle((firstHeading.current ?? untitledNote).trim()) } }, [untitledNote]) diff --git a/src/components/editor/splitter/splitter.tsx b/src/components/editor/splitter/splitter.tsx index ec37e2074..cda52e8be 100644 --- a/src/components/editor/splitter/splitter.tsx +++ b/src/components/editor/splitter/splitter.tsx @@ -43,21 +43,17 @@ export const Splitter: React.FC = ({ containerClassName, left, ri } }} > - -
- {left} -
-
+
+ {left} +
setDoResizing(true)}/>
- -
- {right} -
-
+
+ {right} +
) } diff --git a/src/components/markdown-renderer/full-markdown-renderer.tsx b/src/components/markdown-renderer/full-markdown-renderer.tsx index a14e59e28..bd97d756d 100644 --- a/src/components/markdown-renderer/full-markdown-renderer.tsx +++ b/src/components/markdown-renderer/full-markdown-renderer.tsx @@ -8,7 +8,7 @@ import mathJax from 'markdown-it-mathjax' import plantuml from 'markdown-it-plantuml' import markdownItRegex from 'markdown-it-regex' import toc from 'markdown-it-toc-done-right' -import React, { useCallback, useMemo, useRef, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Alert } from 'react-bootstrap' import { Trans } from 'react-i18next' import { useSelector } from 'react-redux' @@ -20,7 +20,6 @@ import { slugify } from '../editor/table-of-contents/table-of-contents' import { RawYAMLMetadata, YAMLMetaData } from '../editor/yaml-metadata/yaml-metadata' import { BasicMarkdownRenderer } from './basic-markdown-renderer' import { createRenderContainer, validAlertLevels } from './markdown-it-plugins/alert-container' -import { firstHeaderExtractor } from './markdown-it-plugins/first-header-extractor' import { highlightedCode } from './markdown-it-plugins/highlighted-code' import { LineMarkers, lineNumberMarker } from './markdown-it-plugins/line-number-marker' import { plantumlError } from './markdown-it-plugins/plantuml-error' @@ -119,15 +118,28 @@ export const FullMarkdownRenderer: React.FC() usePostTocAstOnChange(tocAst, onTocChange) - const configureMarkdownIt = useCallback((md: MarkdownIt): void => { - if (onFirstHeadingChange) { - md.use(firstHeaderExtractor(), { - firstHeaderFound: (firstHeader: string | undefined) => { - firstHeadingRef.current = firstHeader - } - }) + const extractInnerText = useCallback((node: ChildNode) => { + let innerText = '' + if (node.childNodes && node.childNodes.length > 0) { + node.childNodes.forEach((child) => { innerText += extractInnerText(child) }) + } else if (node.nodeName === 'IMG') { + innerText += (node as HTMLImageElement).getAttribute('alt') + } else { + innerText += node.textContent } + return innerText + }, []) + useEffect(() => { + if (onFirstHeadingChange && documentElement.current) { + const firstHeading = documentElement.current.getElementsByTagName('h1').item(0) + if (firstHeading) { + onFirstHeadingChange(extractInnerText(firstHeading)) + } + } + }, [content, extractInnerText, onFirstHeadingChange]) + + const configureMarkdownIt = useCallback((md: MarkdownIt): void => { if (onMetaDataChange) { md.use(frontmatter, (rawMeta: string) => { try { @@ -210,7 +222,7 @@ export const FullMarkdownRenderer: React.FC { rawMetaRef.current = undefined diff --git a/src/components/markdown-renderer/markdown-it-plugins/first-header-extractor.ts b/src/components/markdown-renderer/markdown-it-plugins/first-header-extractor.ts deleted file mode 100644 index 96d183f87..000000000 --- a/src/components/markdown-renderer/markdown-it-plugins/first-header-extractor.ts +++ /dev/null @@ -1,26 +0,0 @@ -import MarkdownIt from 'markdown-it/lib' - -export interface FirstHeaderExtractorOptions { - firstHeaderFound: (firstHeader: string|undefined) => void -} - -export const firstHeaderExtractor: () => MarkdownIt.PluginWithOptions = () => { - return (md, options) => { - if (!options?.firstHeaderFound) { - return - } - md.core.ruler.after('normalize', 'extract first L1 heading', (state) => { - const lines = state.src.split('\n') - const linkAltTextRegex = /!?\[([^\]]*)]\([^)]*\)/ - for (const line of lines) { - if (line.startsWith('# ')) { - const headerLine = line.replace('# ', '').replace(linkAltTextRegex, '$1') - options.firstHeaderFound(headerLine) - return true - } - } - options.firstHeaderFound(undefined) - return true - }) - } -}