mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 11:16:31 -05:00
Use precalculated diff in revision viewer (#2179)
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
f55b590777
commit
649ebe48f1
8 changed files with 530 additions and 89 deletions
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { invertUnifiedPatch } from './invert-unified-patch'
|
||||
import { parsePatch } from 'diff'
|
||||
|
||||
describe('invert unified patch', () => {
|
||||
it('inverts a patch correctly', () => {
|
||||
const parsedPatch = parsePatch(`--- a\t2022-07-03 21:21:07.499933337 +0200
|
||||
+++ b\t2022-07-03 21:22:28.650972217 +0200
|
||||
@@ -1,5 +1,4 @@
|
||||
-a
|
||||
-b
|
||||
c
|
||||
d
|
||||
+d
|
||||
e`)[0]
|
||||
const result = invertUnifiedPatch(parsedPatch)
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"hunks": Array [
|
||||
Object {
|
||||
"linedelimiters": Array [
|
||||
"
|
||||
",
|
||||
"
|
||||
",
|
||||
"
|
||||
",
|
||||
"
|
||||
",
|
||||
"
|
||||
",
|
||||
"
|
||||
",
|
||||
],
|
||||
"lines": Array [
|
||||
"+a",
|
||||
"+b",
|
||||
" c",
|
||||
" d",
|
||||
"-d",
|
||||
" e",
|
||||
],
|
||||
"newLines": 5,
|
||||
"newStart": 1,
|
||||
"oldLines": 4,
|
||||
"oldStart": 1,
|
||||
},
|
||||
],
|
||||
"index": undefined,
|
||||
"newFileName": "a",
|
||||
"newHeader": "2022-07-03 21:21:07.499933337 +0200",
|
||||
"oldFileName": "b",
|
||||
"oldHeader": "2022-07-03 21:22:28.650972217 +0200",
|
||||
}
|
||||
`)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { Hunk, ParsedDiff } from 'diff'
|
||||
|
||||
/**
|
||||
* Inverts a given unified patch.
|
||||
* A patch that e.g. adds a line, will remove it then.
|
||||
*
|
||||
* @param parsedDiff The patch to invert
|
||||
* @return The inverted patch
|
||||
*/
|
||||
export const invertUnifiedPatch = (parsedDiff: ParsedDiff): ParsedDiff => {
|
||||
const { oldFileName, newFileName, oldHeader, newHeader, hunks, index } = parsedDiff
|
||||
|
||||
const newHunks: Hunk[] = hunks.map((hunk) => {
|
||||
const { oldLines, oldStart, newLines, newStart, lines, linedelimiters } = hunk
|
||||
return {
|
||||
oldLines: newLines,
|
||||
oldStart: newStart,
|
||||
newLines: oldLines,
|
||||
newStart: oldStart,
|
||||
linedelimiters: linedelimiters,
|
||||
lines: lines.map((line) => {
|
||||
if (line.startsWith('-')) {
|
||||
return `+${line.slice(1)}`
|
||||
} else if (line.startsWith('+')) {
|
||||
return `-${line.slice(1)}`
|
||||
} else {
|
||||
return line
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
hunks: newHunks,
|
||||
index: index,
|
||||
newFileName: oldFileName,
|
||||
newHeader: oldHeader,
|
||||
oldFileName: newFileName,
|
||||
oldHeader: newHeader
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { DateTime } from 'luxon'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import { ListGroup } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
|
||||
|
@ -20,12 +20,13 @@ import { WaitSpinner } from '../../../common/wait-spinner/wait-spinner'
|
|||
|
||||
export interface RevisionListEntryProps {
|
||||
active: boolean
|
||||
onSelect: (selectedId: number) => void
|
||||
onSelect: () => void
|
||||
revision: RevisionMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an entry in the revision list.
|
||||
*
|
||||
* @param active true if this is the currently selected revision entry.
|
||||
* @param onSelect Callback that is fired when this revision entry is selected.
|
||||
* @param revision The metadata for this revision entry.
|
||||
|
@ -33,10 +34,6 @@ export interface RevisionListEntryProps {
|
|||
export const RevisionListEntry: React.FC<RevisionListEntryProps> = ({ active, onSelect, revision }) => {
|
||||
useTranslation()
|
||||
|
||||
const onSelectRevision = useCallback(() => {
|
||||
onSelect(revision.id)
|
||||
}, [revision, onSelect])
|
||||
|
||||
const revisionCreationTime = useMemo(() => {
|
||||
return DateTime.fromISO(revision.createdAt).toFormat('DDDD T')
|
||||
}, [revision.createdAt])
|
||||
|
@ -55,9 +52,8 @@ export const RevisionListEntry: React.FC<RevisionListEntryProps> = ({ active, on
|
|||
|
||||
return (
|
||||
<ListGroup.Item
|
||||
as='li'
|
||||
active={active}
|
||||
onClick={onSelectRevision}
|
||||
onClick={onSelect}
|
||||
className={`user-select-none ${styles['revision-item']} d-flex flex-column`}>
|
||||
<span>
|
||||
<ForkAwesomeIcon icon={'clock-o'} className='mx-2' />
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react'
|
||||
import { RevisionListEntry } from './revision-list-entry'
|
||||
import { useAsync } from 'react-use'
|
||||
import { getAllRevisions } from '../../../../api/revisions'
|
||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||
import { ListGroup } from 'react-bootstrap'
|
||||
import { AsyncLoadingBoundary } from '../../../common/async-loading-boundary'
|
||||
|
||||
export interface RevisionListProps {
|
||||
selectedRevisionId?: number
|
||||
onRevisionSelect: (selectedRevisionId: number) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of selectable revisions of the current note.
|
||||
*
|
||||
* @param selectedRevisionId The currently selected revision
|
||||
* @param onRevisionSelect Callback that is executed when a list entry is selected
|
||||
*/
|
||||
export const RevisionList: React.FC<RevisionListProps> = ({ selectedRevisionId, onRevisionSelect }) => {
|
||||
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||
|
||||
const {
|
||||
value: revisions,
|
||||
error,
|
||||
loading
|
||||
} = useAsync(() => {
|
||||
return getAllRevisions(noteIdentifier)
|
||||
}, [noteIdentifier])
|
||||
|
||||
const revisionList = useMemo(() => {
|
||||
if (loading || !revisions) {
|
||||
return null
|
||||
}
|
||||
return revisions.map((revisionListEntry) => (
|
||||
<RevisionListEntry
|
||||
active={selectedRevisionId === revisionListEntry.id}
|
||||
onSelect={() => onRevisionSelect(revisionListEntry.id)}
|
||||
revision={revisionListEntry}
|
||||
key={revisionListEntry.id}
|
||||
/>
|
||||
))
|
||||
}, [loading, onRevisionSelect, revisions, selectedRevisionId])
|
||||
|
||||
return (
|
||||
<AsyncLoadingBoundary loading={loading} error={error} componentName={'revision list'}>
|
||||
<ListGroup>{revisionList}</ListGroup>
|
||||
</AsyncLoadingBoundary>
|
||||
)
|
||||
}
|
|
@ -4,52 +4,26 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Col, ListGroup, Modal, Row } from 'react-bootstrap'
|
||||
import React, { useState } from 'react'
|
||||
import { Col, Modal, Row } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { getAllRevisions } from '../../../../api/revisions'
|
||||
import type { ModalVisibilityProps } from '../../../common/modals/common-modal'
|
||||
import { CommonModal } from '../../../common/modals/common-modal'
|
||||
import { RevisionListEntry } from './revision-list-entry'
|
||||
import styles from './revision-modal.module.scss'
|
||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||
import { useAsync } from 'react-use'
|
||||
import { RevisionModalFooter } from './revision-modal-footer'
|
||||
import { RevisionViewer } from './revision-viewer'
|
||||
import { AsyncLoadingBoundary } from '../../../common/async-loading-boundary'
|
||||
import { RevisionList } from './revision-list'
|
||||
|
||||
/**
|
||||
* Modal that shows the available revisions and allows for comparison between them.
|
||||
*
|
||||
* @param show true to show the modal, false otherwise.
|
||||
* @param onHide Callback that is fired when the modal is requested to close.
|
||||
*/
|
||||
export const RevisionModal: React.FC<ModalVisibilityProps> = ({ show, onHide }) => {
|
||||
useTranslation()
|
||||
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||
const [selectedRevisionId, setSelectedRevisionId] = useState<number>()
|
||||
|
||||
const { value, error, loading } = useAsync(() => {
|
||||
return getAllRevisions(noteIdentifier)
|
||||
}, [noteIdentifier])
|
||||
|
||||
const selectRevision = useCallback((revisionId: number) => {
|
||||
setSelectedRevisionId(revisionId)
|
||||
}, [])
|
||||
|
||||
const revisionList = useMemo(() => {
|
||||
if (loading || !value) {
|
||||
return null
|
||||
}
|
||||
return value.map((revisionListEntry) => (
|
||||
<RevisionListEntry
|
||||
active={selectedRevisionId === revisionListEntry.id}
|
||||
onSelect={selectRevision}
|
||||
revision={revisionListEntry}
|
||||
key={revisionListEntry.id}
|
||||
/>
|
||||
))
|
||||
}, [loading, value, selectedRevisionId, selectRevision])
|
||||
|
||||
return (
|
||||
<CommonModal
|
||||
show={show}
|
||||
|
@ -62,12 +36,10 @@ export const RevisionModal: React.FC<ModalVisibilityProps> = ({ show, onHide })
|
|||
<Modal.Body>
|
||||
<Row>
|
||||
<Col lg={4} className={styles['scroll-col']}>
|
||||
<ListGroup as='ul'>{revisionList}</ListGroup>
|
||||
<RevisionList onRevisionSelect={setSelectedRevisionId} selectedRevisionId={selectedRevisionId} />
|
||||
</Col>
|
||||
<Col lg={8} className={styles['scroll-col']}>
|
||||
<AsyncLoadingBoundary loading={loading} componentName={'RevisionModal'} error={error}>
|
||||
<RevisionViewer selectedRevisionId={selectedRevisionId} allRevisions={value} />
|
||||
</AsyncLoadingBoundary>
|
||||
<RevisionViewer selectedRevisionId={selectedRevisionId} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal.Body>
|
||||
|
|
|
@ -3,74 +3,58 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import React from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer'
|
||||
import { useAsync } from 'react-use'
|
||||
import type { RevisionDetails, RevisionMetadata } from '../../../../api/revisions/types'
|
||||
import { getRevision } from '../../../../api/revisions'
|
||||
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||
import { useNoteMarkdownContent } from '../../../../hooks/common/use-note-markdown-content'
|
||||
import { useIsDarkModeActivated } from '../../../../hooks/common/use-is-dark-mode-activated'
|
||||
import type { AsyncState } from 'react-use/lib/useAsyncFn'
|
||||
import { AsyncLoadingBoundary } from '../../../common/async-loading-boundary'
|
||||
import { applyPatch, parsePatch } from 'diff'
|
||||
import { invertUnifiedPatch } from './invert-unified-patch'
|
||||
import { Optional } from '@mrdrogdrog/optional'
|
||||
|
||||
export interface RevisionViewerProps {
|
||||
selectedRevisionId?: number
|
||||
allRevisions?: RevisionMetadata[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the diff viewer for a given revision and its previous one.
|
||||
*
|
||||
* @param selectedRevisionId The id of the currently selected revision.
|
||||
* @param allRevisions List of metadata for all available revisions.
|
||||
*/
|
||||
export const RevisionViewer: React.FC<RevisionViewerProps> = ({ selectedRevisionId, allRevisions }) => {
|
||||
export const RevisionViewer: React.FC<RevisionViewerProps> = ({ selectedRevisionId }) => {
|
||||
const noteIdentifier = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||
const markdownContent = useNoteMarkdownContent()
|
||||
const darkModeEnabled = useIsDarkModeActivated()
|
||||
|
||||
const previousRevisionContent = useAsync(async () => {
|
||||
if (!allRevisions || selectedRevisionId === undefined) {
|
||||
return Promise.reject()
|
||||
const { value, error, loading } = useAsync(async () => {
|
||||
if (selectedRevisionId === undefined) {
|
||||
throw new Error('No revision selected')
|
||||
} else {
|
||||
return await getRevision(noteIdentifier, selectedRevisionId)
|
||||
}
|
||||
const revisionIds = allRevisions.map((revisionMetadata) => revisionMetadata.id)
|
||||
const largestId = Math.max(...revisionIds)
|
||||
if (selectedRevisionId === largestId) {
|
||||
return Promise.resolve(markdownContent)
|
||||
}
|
||||
const nextSmallerId = revisionIds
|
||||
.sort()
|
||||
.reverse()
|
||||
.find((id) => id < selectedRevisionId)
|
||||
if (!nextSmallerId) {
|
||||
return Promise.resolve('')
|
||||
}
|
||||
const revision = await getRevision(noteIdentifier, nextSmallerId)
|
||||
return revision.content
|
||||
}, [selectedRevisionId, allRevisions])
|
||||
|
||||
const selectedRevision = useAsync(() => {
|
||||
if (!allRevisions || selectedRevisionId === undefined) {
|
||||
return Promise.reject()
|
||||
}
|
||||
return getRevision(noteIdentifier, selectedRevisionId)
|
||||
}, [selectedRevisionId, noteIdentifier])
|
||||
|
||||
if (selectedRevisionId === undefined || !allRevisions) {
|
||||
const previousRevisionContent = useMemo(() => {
|
||||
return Optional.ofNullable(value)
|
||||
.flatMap((revision) =>
|
||||
Optional.ofNullable(parsePatch(revision.patch)[0])
|
||||
.map((patch) => invertUnifiedPatch(patch))
|
||||
.map((patch) => applyPatch(revision.content, patch))
|
||||
)
|
||||
.orElse('')
|
||||
}, [value])
|
||||
|
||||
if (selectedRevisionId === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
// TODO Rework the revision viewer to use pre-calculated diffs
|
||||
// see https://github.com/hedgedoc/react-client/issues/1989
|
||||
|
||||
return (
|
||||
<AsyncLoadingBoundary
|
||||
loading={selectedRevision.loading || previousRevisionContent.loading}
|
||||
componentName={'RevisionViewer'}
|
||||
error={selectedRevision.error || previousRevisionContent.error}>
|
||||
<AsyncLoadingBoundary loading={loading} componentName={'RevisionViewer'} error={error}>
|
||||
<ReactDiffViewer
|
||||
oldValue={previousRevisionContent.value ?? ''}
|
||||
newValue={(selectedRevision as AsyncState<RevisionDetails>).value?.content}
|
||||
oldValue={previousRevisionContent ?? ''}
|
||||
newValue={value?.content ?? ''}
|
||||
splitView={false}
|
||||
compareMethod={DiffMethod.WORDS}
|
||||
useDarkTheme={darkModeEnabled}
|
||||
|
|
|
@ -12,13 +12,201 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
respondToMatchingRequest<RevisionDetails>(HttpMethod.GET, req, res, {
|
||||
id: 0,
|
||||
createdAt: '2021-12-21T16:59:42.000Z',
|
||||
patch: '',
|
||||
patch: `Index:
|
||||
===================================================================
|
||||
---
|
||||
+++
|
||||
@@ -0,0 +1,92 @@
|
||||
+---
|
||||
+title: Features
|
||||
+description: Many features, such wow!
|
||||
+robots: noindex
|
||||
+tags: hedgedoc, demo, react
|
||||
+opengraph:
|
||||
+ title: Features
|
||||
+---
|
||||
+# Embedding demo
|
||||
+[TOC]
|
||||
+
|
||||
+## some plain text
|
||||
+
|
||||
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
+
|
||||
+## MathJax
|
||||
+You can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):
|
||||
+
|
||||
+The *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral
|
||||
+
|
||||
+$$
|
||||
+x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.
|
||||
+$$
|
||||
+
|
||||
+$$
|
||||
+\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.
|
||||
+$$
|
||||
+
|
||||
+> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).
|
||||
+
|
||||
+## Blockquote
|
||||
+> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
+> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
+> [color=red] [name=John Doe] [time=2020-06-21 22:50]
|
||||
+
|
||||
+## Slideshare
|
||||
+{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}
|
||||
+
|
||||
+## Gist
|
||||
+https://gist.github.com/schacon/1
|
||||
+
|
||||
+## YouTube
|
||||
+https://www.youtube.com/watch?v=KgMpKsp23yY
|
||||
+
|
||||
+## Vimeo
|
||||
+https://vimeo.com/23237102
|
||||
+
|
||||
+## Asciinema
|
||||
+https://asciinema.org/a/117928
|
||||
+
|
||||
+## PDF
|
||||
+{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}
|
||||
+
|
||||
+## Code highlighting
|
||||
+\`\`\`javascript=
|
||||
+
|
||||
+let a = 1
|
||||
+\`\`\`
|
||||
+
|
||||
+## PlantUML
|
||||
+\`\`\`plantuml
|
||||
+@startuml
|
||||
+participant Alice
|
||||
+participant "The **Famous** Bob" as Bob
|
||||
+
|
||||
+Alice -> Bob : hello --there--
|
||||
+... Some ~~long delay~~ ...
|
||||
+Bob -> Alice : ok
|
||||
+note left
|
||||
+ This is **bold**
|
||||
+ This is //italics//
|
||||
+ This is ""monospaced""
|
||||
+ This is --stroked--
|
||||
+ This is __underlined__
|
||||
+ This is ~~waved~~
|
||||
+end note
|
||||
+
|
||||
+Alice -> Bob : A //well formatted// message
|
||||
+note right of Alice
|
||||
+ This is <back:cadetblue><size:18>displayed</size></back>
|
||||
+ __left of__ Alice.
|
||||
+end note
|
||||
+note left of Bob
|
||||
+ <u:red>This</u> is <color #118888>displayed</color>
|
||||
+ **<color purple>left of</color> <s:red>Alice</strike> Bob**.
|
||||
+end note
|
||||
+note over Alice, Bob
|
||||
+ <w:#FF33FF>This is hosted</w> by <img sourceforge.jpg>
|
||||
+end note
|
||||
+@enduml
|
||||
+\`\`\`
|
||||
+
|
||||
`,
|
||||
edits: [],
|
||||
length: 2782,
|
||||
authorUsernames: [],
|
||||
anonymousAuthorCount: 2,
|
||||
content:
|
||||
'---\ntitle: Features\ndescription: Many features, such wow!\nrobots: noindex\ntags: hedgedoc, demo, react\nopengraph:\n title: Features\n---\n# Embedding demo\n[TOC]\n\n## some plain text\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n## MathJax\nYou can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):\n\nThe *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral\n\n$$\nx = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.\n$$\n\n$$\n\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.\n$$\n\n> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).\n\n## Blockquote\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> [color=red] [name=John Doe] [time=2020-06-21 22:50]\n\n## Slideshare\n{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}\n\n## Gist\nhttps://gist.github.com/schacon/1\n\n## YouTube\nhttps://www.youtube.com/watch?v=KgMpKsp23yY\n\n## Vimeo\nhttps://vimeo.com/23237102\n\n## Asciinema\nhttps://asciinema.org/a/117928\n\n## PDF\n{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}\n\n## Code highlighting\n```javascript=\n\nlet a = 1\n```\n\n## PlantUML\n```plantuml\n@startuml\nparticipant Alice\nparticipant "The **Famous** Bob" as Bob\n\nAlice -> Bob : hello --there--\n... Some ~~long delay~~ ...\nBob -> Alice : ok\nnote left\n This is **bold**\n This is //italics//\n This is ""monospaced""\n This is --stroked--\n This is __underlined__\n This is ~~waved~~\nend note\n\nAlice -> Bob : A //well formatted// message\nnote right of Alice\n This is <back:cadetblue><size:18>displayed</size></back>\n __left of__ Alice.\nend note\nnote left of Bob\n <u:red>This</u> is <color #118888>displayed</color>\n **<color purple>left of</color> <s:red>Alice</strike> Bob**.\nend note\nnote over Alice, Bob\n <w:#FF33FF>This is hosted</w> by <img sourceforge.jpg>\nend note\n@enduml\n```\n\n'
|
||||
content: `---
|
||||
title: Features
|
||||
description: Many features, such wow!
|
||||
robots: noindex
|
||||
tags: hedgedoc, demo, react
|
||||
opengraph:
|
||||
title: Features
|
||||
---
|
||||
# Embedding demo
|
||||
[TOC]
|
||||
|
||||
## some plain text
|
||||
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
|
||||
## MathJax
|
||||
You can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):
|
||||
|
||||
The *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral
|
||||
|
||||
$$
|
||||
x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.
|
||||
$$
|
||||
|
||||
$$
|
||||
\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.
|
||||
$$
|
||||
|
||||
> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).
|
||||
|
||||
## Blockquote
|
||||
> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
> [color=red] [name=John Doe] [time=2020-06-21 22:50]
|
||||
|
||||
## Slideshare
|
||||
{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}
|
||||
|
||||
## Gist
|
||||
https://gist.github.com/schacon/1
|
||||
|
||||
## YouTube
|
||||
https://www.youtube.com/watch?v=KgMpKsp23yY
|
||||
|
||||
## Vimeo
|
||||
https://vimeo.com/23237102
|
||||
|
||||
## Asciinema
|
||||
https://asciinema.org/a/117928
|
||||
|
||||
## PDF
|
||||
{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}
|
||||
|
||||
## Code highlighting
|
||||
\`\`\`javascript=
|
||||
|
||||
let a = 1
|
||||
\`\`\`
|
||||
|
||||
## PlantUML
|
||||
\`\`\`plantuml
|
||||
@startuml
|
||||
participant Alice
|
||||
participant "The **Famous** Bob" as Bob
|
||||
|
||||
Alice -> Bob : hello --there--
|
||||
... Some ~~long delay~~ ...
|
||||
Bob -> Alice : ok
|
||||
note left
|
||||
This is **bold**
|
||||
This is //italics//
|
||||
This is ""monospaced""
|
||||
This is --stroked--
|
||||
This is __underlined__
|
||||
This is ~~waved~~
|
||||
end note
|
||||
|
||||
Alice -> Bob : A //well formatted// message
|
||||
note right of Alice
|
||||
This is <back:cadetblue><size:18>displayed</size></back>
|
||||
__left of__ Alice.
|
||||
end note
|
||||
note left of Bob
|
||||
<u:red>This</u> is <color #118888>displayed</color>
|
||||
**<color purple>left of</color> <s:red>Alice</strike> Bob**.
|
||||
end note
|
||||
note over Alice, Bob
|
||||
<w:#FF33FF>This is hosted</w> by <img sourceforge.jpg>
|
||||
end note
|
||||
@enduml
|
||||
\`\`\`
|
||||
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -11,13 +11,149 @@ const handler = (req: NextApiRequest, res: NextApiResponse): void => {
|
|||
respondToMatchingRequest<RevisionDetails>(HttpMethod.GET, req, res, {
|
||||
id: 1,
|
||||
createdAt: '2021-12-29T17:54:11.000Z',
|
||||
patch: '',
|
||||
patch: `Index:
|
||||
===================================================================
|
||||
---
|
||||
+++
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Features
|
||||
-description: Many features, such wow!
|
||||
+description: Many more features, such wow!
|
||||
robots: noindex
|
||||
tags: hedgedoc, demo, react
|
||||
opengraph:
|
||||
title: Features
|
||||
@@ -10,9 +10,9 @@
|
||||
[TOC]
|
||||
|
||||
## some plain text
|
||||
|
||||
-Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magnus aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetezur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam _et_ justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
|
||||
## MathJax
|
||||
You can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
## Gist
|
||||
https://gist.github.com/schacon/1
|
||||
|
||||
## YouTube
|
||||
-https://www.youtube.com/watch?v=KgMpKsp23yY
|
||||
+https://www.youtube.com/watch?v=zHAIuE5BQWk
|
||||
|
||||
## Vimeo
|
||||
https://vimeo.com/23237102
|
||||
|
||||
@@ -62,9 +62,9 @@
|
||||
@startuml
|
||||
participant Alice
|
||||
participant "The **Famous** Bob" as Bob
|
||||
|
||||
-Alice -> Bob : hello --there--
|
||||
+Alice -> Bob : bye --there--
|
||||
... Some ~~long delay~~ ...
|
||||
Bob -> Alice : ok
|
||||
note left
|
||||
This is **bold**`,
|
||||
edits: [],
|
||||
length: 2788,
|
||||
authorUsernames: [],
|
||||
anonymousAuthorCount: 4,
|
||||
content:
|
||||
'---\ntitle: Features\ndescription: Many more features, such wow!\nrobots: noindex\ntags: hedgedoc, demo, react\nopengraph:\n title: Features\n---\n# Embedding demo\n[TOC]\n\n## some plain text\n\nLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magnus aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetezur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam _et_ justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n## MathJax\nYou can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):\n\nThe *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral\n\n$$\nx = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.\n$$\n\n$$\n\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.\n$$\n\n> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).\n\n## Blockquote\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.\n> [color=red] [name=John Doe] [time=2020-06-21 22:50]\n\n## Slideshare\n{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}\n\n## Gist\nhttps://gist.github.com/schacon/1\n\n## YouTube\nhttps://www.youtube.com/watch?v=zHAIuE5BQWk\n\n## Vimeo\nhttps://vimeo.com/23237102\n\n## Asciinema\nhttps://asciinema.org/a/117928\n\n## PDF\n{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}\n\n## Code highlighting\n```javascript=\n\nlet a = 1\n```\n\n## PlantUML\n```plantuml\n@startuml\nparticipant Alice\nparticipant "The **Famous** Bob" as Bob\n\nAlice -> Bob : bye --there--\n... Some ~~long delay~~ ...\nBob -> Alice : ok\nnote left\n This is **bold**\n This is //italics//\n This is ""monospaced""\n This is --stroked--\n This is __underlined__\n This is ~~waved~~\nend note\n\nAlice -> Bob : A //well formatted// message\nnote right of Alice\n This is <back:cadetblue><size:18>displayed</size></back>\n __left of__ Alice.\nend note\nnote left of Bob\n <u:red>This</u> is <color #118888>displayed</color>\n **<color purple>left of</color> <s:red>Alice</strike> Bob**.\nend note\nnote over Alice, Bob\n <w:#FF33FF>This is hosted</w> by <img sourceforge.jpg>\nend note\n@enduml\n```\n\n'
|
||||
content: `---
|
||||
title: Features
|
||||
description: Many more features, such wow!
|
||||
robots: noindex
|
||||
tags: hedgedoc, demo, react
|
||||
opengraph:
|
||||
title: Features
|
||||
---
|
||||
# Embedding demo
|
||||
[TOC]
|
||||
|
||||
## some plain text
|
||||
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magnus aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetezur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam _et_ justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
|
||||
## MathJax
|
||||
You can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](https://math.stackexchange.com/):
|
||||
|
||||
The *Gamma function* satisfying $\\Gamma(n) = (n-1)!\\quad\\forall n\\in\\mathbb N$ is via the Euler integral
|
||||
|
||||
$$
|
||||
x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.
|
||||
$$
|
||||
|
||||
$$
|
||||
\\Gamma(z) = \\int_0^\\infty t^{z-1}e^{-t}dt\\,.
|
||||
$$
|
||||
|
||||
> More information about **LaTeX** mathematical expressions [here](https://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference).
|
||||
|
||||
## Blockquote
|
||||
> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
> Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
> [color=red] [name=John Doe] [time=2020-06-21 22:50]
|
||||
|
||||
## Slideshare
|
||||
{%slideshare mazlan1/internet-of-things-the-tip-of-an-iceberg %}
|
||||
|
||||
## Gist
|
||||
https://gist.github.com/schacon/1
|
||||
|
||||
## YouTube
|
||||
https://www.youtube.com/watch?v=zHAIuE5BQWk
|
||||
|
||||
## Vimeo
|
||||
https://vimeo.com/23237102
|
||||
|
||||
## Asciinema
|
||||
https://asciinema.org/a/117928
|
||||
|
||||
## PDF
|
||||
{%pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf %}
|
||||
|
||||
## Code highlighting
|
||||
\`\`\`javascript=
|
||||
|
||||
let a = 1
|
||||
\`\`\`
|
||||
|
||||
## PlantUML
|
||||
\`\`\`plantuml
|
||||
@startuml
|
||||
participant Alice
|
||||
participant "The **Famous** Bob" as Bob
|
||||
|
||||
Alice -> Bob : bye --there--
|
||||
... Some ~~long delay~~ ...
|
||||
Bob -> Alice : ok
|
||||
note left
|
||||
This is **bold**
|
||||
This is //italics//
|
||||
This is ""monospaced""
|
||||
This is --stroked--
|
||||
This is __underlined__
|
||||
This is ~~waved~~
|
||||
end note
|
||||
|
||||
Alice -> Bob : A //well formatted// message
|
||||
note right of Alice
|
||||
This is <back:cadetblue><size:18>displayed</size></back>
|
||||
__left of__ Alice.
|
||||
end note
|
||||
note left of Bob
|
||||
<u:red>This</u> is <color #118888>displayed</color>
|
||||
**<color purple>left of</color> <s:red>Alice</strike> Bob**.
|
||||
end note
|
||||
note over Alice, Bob
|
||||
<w:#FF33FF>This is hosted</w> by <img sourceforge.jpg>
|
||||
end note
|
||||
@enduml
|
||||
\`\`\`
|
||||
|
||||
`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue