Prototyping for diff annotations (#127)

This commit is contained in:
Tan Nhu 2022-12-16 09:57:52 -08:00 committed by GitHub
parent 3b120dd2b3
commit 0d49ef1d69
20 changed files with 901 additions and 210 deletions

View File

@ -47,7 +47,8 @@
"@harness/use-modal": "1.3.0",
"@popperjs/core": "^2.4.2",
"@projectstorm/react-diagrams-core": "^6.6.0",
"@uiw/react-markdown-preview": "^4.1.2",
"@uiw/react-markdown-editor": "^5.10.1",
"@uiw/react-markdown-preview": "4.1.6",
"@urql/exchange-request-policy": "^0.1.3",
"anser": "^2.0.1",
"classnames": "^2.2.6",

View File

@ -11,6 +11,64 @@
.d2h-file-wrapper {
border: 0;
.d2h-diff-tbody {
&,
tr {
position: relative;
}
}
&.side-by-side-file-diff {
.d2h-code-side-linenumber.d2h-info {
pointer-events: none;
}
}
[data-annotated='true'] [data-content-for-line-number],
[data-content-for-line-number=''] {
&,
:hover {
[data-annotation-for-line] {
pointer-events: none;
display: none;
}
}
}
[data-content-for-line-number] {
cursor: default;
[data-annotation-for-line] {
position: absolute;
top: 1px;
left: 60px;
display: flex;
height: 14px;
width: 14px;
font-weight: 600;
background: var(--purple-500);
color: var(--white);
text-align: center;
border-radius: 5px;
align-items: center;
justify-content: center;
cursor: pointer;
visibility: hidden;
}
&:hover [data-annotation-for-line] {
visibility: visible;
transform: scale(1.5);
transition: transform 0.75s;
}
}
&.line-by-line-file-diff {
[data-annotation-for-line] {
left: 102px;
}
}
}
.d2h-file-header {
@ -31,10 +89,6 @@
.d2h-code-side-linenumber {
width: 56px;
}
.d2h-diff-tbody {
position: relative;
}
}
&.collapsed {
@ -98,9 +152,24 @@
border-bottom-right-radius: 5px;
max-width: calc(100vw - 320px);
// &[data-display='none'] {
// visibility: hidden;
// }
}
}
.annotationCell {
box-sizing: border-box;
padding: var(--spacing-medium);
.annotationCellContainer {
border: 1px solid var(--border-color);
border-radius: 5px;
height: 100%;
padding: var(--spacing-medium);
max-width: 800px;
&[data-view-style='side-by-side'] {
width: 600px;
position: sticky;
left: var(--spacing-medium);
}
}
}

View File

@ -8,5 +8,7 @@ declare const styles: {
readonly offscreen: string
readonly fname: string
readonly viewLabel: string
readonly annotationCell: string
readonly annotationCellContainer: string
}
export default styles

View File

@ -12,13 +12,14 @@ import { CodeIcon } from 'utils/GitUtils'
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
import css from './DiffViewer.module.scss'
export enum DiffViewStyle {
export enum ViewStyle {
SPLIT = 'side-by-side',
UNIFIED = 'line-by-line'
}
const DIFF_HEADER_HEIGHT = 36
const LINE_NUMBER_CLASS = 'diff-viewer-line-number'
const INITIAL_COMMENT_HEIGHT = 200
export const DIFF2HTML_CONFIG = {
outputFormat: 'side-by-side',
@ -31,11 +32,12 @@ export const DIFF2HTML_CONFIG = {
renderNothingWhenEmpty: false,
compiledTemplates: {
'generic-line': HoganJsUtils.compile(`
<tr data-line="{{lineNumber}}">
<td class="{{lineClass}} {{type}} ${LINE_NUMBER_CLASS}">
<tr>
<td class="{{lineClass}} {{type}} ${LINE_NUMBER_CLASS}" data-line-number="{{lineNumber}}">
{{{lineNumber}}}
</td>
<td class="{{type}}">
<td class="{{type}}" data-content-for-line-number="{{lineNumber}}">
<div data-annotation-for-line="{{lineNumber}}" tab-index="0" role="button">+</div>
<div class="{{contentClass}}">
{{#prefix}}
<span class="d2h-code-line-prefix">{{{prefix}}}</span>
@ -53,17 +55,70 @@ export const DIFF2HTML_CONFIG = {
</td>
</tr>
`),
'side-by-side-file-diff': HoganJsUtils.compile(`
<div id="{{fileHtmlId}}" class="d2h-file-wrapper side-by-side-file-diff" data-lang="{{file.language}}">
<div class="d2h-file-header">
{{{filePath}}}
</div>
<div class="d2h-files-diff">
<div class="d2h-file-side-diff left">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
{{{diffs.left}}}
</tbody>
</table>
</div>
</div>
<div class="d2h-file-side-diff right">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
{{{diffs.right}}}
</tbody>
</table>
</div>
</div>
</div>
</div>
`),
'line-by-line-file-diff': HoganJsUtils.compile(`
<div id="{{fileHtmlId}}" class="d2h-file-wrapper line-by-line-file-diff" data-lang="{{file.language}}">
<div class="d2h-file-header">
{{{filePath}}}
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
{{{diffs}}}
</tbody>
</table>
</div>
</div>
</div>
`),
'line-by-line-numbers': HoganJsUtils.compile(`
<div class="line-num1 ${LINE_NUMBER_CLASS}">{{oldNumber}}</div>
<div class="line-num2 ${LINE_NUMBER_CLASS}">{{newNumber}}</div>
<div class="line-num1">{{oldNumber}}</div>
<div class="line-num2">{{newNumber}}</div>
`)
}
} as Readonly<Diff2Html.Diff2HtmlConfig>
interface ContentAnnotationInfo {
left: boolean
right: boolean
lineNumber: number
annotatedElement: HTMLTableRowElement | null
width: number
height: number
nthChild: number
}
interface DiffViewerProps {
index: number
diff: DiffFile
viewStyle: DiffViewStyle
viewStyle: ViewStyle
stickyTopPosition?: number
}
@ -85,6 +140,7 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({ index, diff, viewStyle,
const [diffRenderer, setDiffRenderer] = useState<Diff2HtmlUI>()
const { ref: inViewRef, inView } = useInView({ rootMargin: '100px 0px' })
const containerRef = useRef<HTMLDivElement | null>(null)
const [annotations, setAnnotations] = useState<ContentAnnotationInfo[]>([])
const setContainerRef = useCallback(
node => {
containerRef.current = node
@ -171,29 +227,99 @@ export const DiffViewer: React.FC<DiffViewerProps> = ({ index, diff, viewStyle,
} else {
containerClassList.remove(css.collapsed)
if (parseInt(containerStyle.height) != height) {
containerStyle.height = `${height}px`
const annotationHeights = annotations.reduce((total, annotation) => total + annotation.height, 0) || 0
const newHeight = Number(height) + annotationHeights
if (parseInt(containerStyle.height) != newHeight) {
containerStyle.height = `${newHeight}px`
}
}
},
[collapsed, height, stickyTopPosition]
[collapsed, height, stickyTopPosition, annotations]
)
useEffect(function () {
const onClick = (event: MouseEvent) => {
const target = event.target as HTMLDivElement
useEffect(
function clickToAnnotate() {
const onClick = (event: MouseEvent) => {
const annotationInfo: ContentAnnotationInfo = {
left: false,
right: false,
lineNumber: 0,
annotatedElement: null,
height: 0,
width: 0,
nthChild: 1
}
const target = event.target as HTMLDivElement
const annotationButton = target?.closest('[data-annotation-for-line]') as HTMLDivElement
const parentRow = annotationButton?.closest('tr') as HTMLTableRowElement
const isSplitView = viewStyle === ViewStyle.SPLIT
if (target.classList.contains(LINE_NUMBER_CLASS)) {
console.log('line number clicked')
if (annotationButton && parentRow) {
if (isSplitView) {
const leftParent = annotationButton.closest('.d2h-file-side-diff.left')
annotationInfo.left = !!leftParent
annotationInfo.right = !leftParent
annotationInfo.lineNumber = Number(annotationButton.dataset.lineNumber)
} else {
const lineInfoTD = annotationButton.closest('td')?.previousElementSibling
const lineNum1 = lineInfoTD?.querySelector('.line-num1')
const lineNum2 = lineInfoTD?.querySelector('.line-num2')
annotationInfo.left = !!lineNum1?.textContent
annotationInfo.right = !annotationInfo.left
annotationInfo.lineNumber = Number(lineNum1?.textContent || lineNum2?.textContent)
}
const _height = INITIAL_COMMENT_HEIGHT
parentRow.dataset.annotated = 'true' // TODO: set to lookup value instead of true
annotationInfo.annotatedElement = parentRow
annotationInfo.height = _height
const tr = document.createElement('tr')
tr.dataset.isAnnotation = 'true' // TODO: set to lookup value instead of true
tr.innerHTML = `
<td colspan="2" class="${css.annotationCell}" height="${annotationInfo.height}px">
<div class="${css.annotationCellContainer}" data-view-style="${viewStyle}" contentEditable >Enter your comment or review here...</div>
</td>
`
parentRow.after(tr)
let node = parentRow as Element
while (node.previousElementSibling) {
annotationInfo.nthChild++
node = node.previousElementSibling
}
// Add space in opposit pane (split view only)
if (isSplitView) {
const filesDiff = parentRow.closest('.d2h-files-diff') as HTMLElement
const sideDiff = filesDiff?.querySelector(`div.${annotationInfo.left ? 'right' : 'left'}`) as HTMLElement
const sideRow = sideDiff?.querySelector(`tr:nth-child(${annotationInfo.nthChild})`)
const tr2 = document.createElement('tr')
tr2.innerHTML = `<td colspan="2" class="${css.annotationCell}" height="${annotationInfo.height}px"></td>`
sideRow?.after(tr2)
}
console.log(annotationInfo)
setAnnotations([...annotations, annotationInfo])
}
}
console.log({ event, target })
}
const containerDOM = containerRef.current as HTMLDivElement
containerDOM.addEventListener('click', onClick)
return () => {
containerDOM.removeEventListener('click', onClick)
}
}, [])
const containerDOM = containerRef.current as HTMLDivElement
containerDOM.addEventListener('click', onClick)
return () => {
containerDOM.removeEventListener('click', onClick)
}
},
[viewStyle, annotations]
)
return (
<Container

View File

@ -5,46 +5,44 @@
// than 1000 line content.
//
.sourceCodeViewer {
pre {
overflow-x: auto;
}
// These styles conflict with long line in markdown source code
// Disable all for now
.code-highlight {
float: left;
min-width: 100%;
}
// pre {
// overflow-x: auto;
// }
.code-line {
display: block;
padding-left: 16px;
padding-right: 16px;
margin-left: -16px;
margin-right: -16px;
border-left: 4px solid rgba(0, 0, 0, 0); /* Set placeholder for highlight accent border color to transparent */
}
// .code-line {
// display: block;
// padding-left: 16px;
// padding-right: 16px;
// margin-left: -16px;
// margin-right: -16px;
// border-left: 4px solid rgba(0, 0, 0, 0); /* Set placeholder for highlight accent border color to transparent */
// }
.code-line.inserted {
background-color: rgba(16, 185, 129, 0.2); /* Set inserted line (+) color */
}
// .code-line.inserted {
// background-color: rgba(16, 185, 129, 0.2); /* Set inserted line (+) color */
// }
.code-line.deleted {
background-color: rgba(239, 68, 68, 0.2); /* Set deleted line (-) color */
}
// .code-line.deleted {
// background-color: rgba(239, 68, 68, 0.2); /* Set deleted line (-) color */
// }
.highlight-line {
margin-left: -16px;
margin-right: -16px;
background-color: #fff8c4e8; /* Set highlight bg color */
border-left: 4px solid rgb(59, 130, 246); /* Set highlight accent border color */
}
// .highlight-line {
// margin-left: -16px;
// margin-right: -16px;
// background-color: #fff8c4e8; /* Set highlight bg color */
// border-left: 4px solid rgb(59, 130, 246); /* Set highlight accent border color */
// }
.line-number::before {
display: inline-block;
width: 1rem;
text-align: right;
margin-right: 16px;
margin-left: -8px;
color: rgb(156, 163, 175); /* Line number color */
content: attr(line);
}
// .line-number::before {
// display: inline-block;
// width: 1rem;
// text-align: right;
// margin-right: 16px;
// margin-left: -8px;
// color: rgb(156, 163, 175); /* Line number color */
// content: attr(line);
// }
}

View File

@ -16,7 +16,7 @@ export function MarkdownViewer({ source }: MarkdownViewerProps) {
return (
<Container className="sourceCodeViewer">
<Suspense fallback={<Text>{getString('loading')}</Text>}>
<ReactMarkdownPreview source={source} skipHtml={false} />
<ReactMarkdownPreview source={source} skipHtml={true} warpperElement={{ 'data-color-mode': 'light' }} />
</Suspense>
</Container>
)

View File

@ -84,6 +84,7 @@ export interface StringsMap {
failedToDeleteBranch: string
fileDeleted: string
files: string
filesChanged: string
findATag: string
findBranch: string
findOrCreateBranch: string

View File

@ -0,0 +1,15 @@
import { useEffect } from 'react'
export function useEventListener<K extends keyof HTMLElementEventMap>(
type: K,
listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => Unknown,
element: HTMLElement = window as unknown as HTMLElement,
options?: boolean | AddEventListenerOptions
) {
useEffect(() => {
element.addEventListener(type, listener, options)
return () => {
element.removeEventListener(type, listener)
}
}, [element, type, listener, options])
}

View File

@ -183,3 +183,4 @@ rejected: Rejected
yours: Yours
all: All
scrollToTop: Scroll to top
filesChanged: File Changed

View File

@ -18,7 +18,7 @@ import css from './PullRequest.module.scss'
enum PullRequestSection {
CONVERSATION = 'conversation',
COMMITS = 'commits',
DIFFS = 'diffs'
FILES_CHANGED = 'files'
}
export default function PullRequest() {
@ -95,8 +95,8 @@ export default function PullRequest() {
panel: <PullRequestCommits repoMetadata={repoMetadata} pullRequestMetadata={prData} />
},
{
id: PullRequestSection.DIFFS,
title: <TabTitle icon={CodeIcon.File} title={getString('diff')} count={20} />,
id: PullRequestSection.FILES_CHANGED,
title: <TabTitle icon={CodeIcon.File} title={getString('filesChanged')} count={20} />,
panel: <PullRequestDiff repoMetadata={repoMetadata} pullRequestMetadata={prData} />
}
]}

View File

@ -35,9 +35,8 @@
}
.filesMenu {
max-height: 350px;
max-height: 400px;
overflow: auto;
padding: var(--spacing-xsmall) !important;
:global {
.bp3-menu-item:hover {
@ -50,3 +49,7 @@
align-items: center;
}
}
.popover {
border-radius: 8px !important;
}

View File

@ -8,5 +8,6 @@ declare const styles: {
readonly layout: string
readonly filesMenu: string
readonly menuItem: string
readonly popover: string
}
export default styles

View File

@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import {
Container,
FlexExpander,
@ -23,55 +23,41 @@ import type { DiffFile } from 'diff2html/lib/types'
import { useStrings } from 'framework/strings'
import { CodeIcon, GitInfoProps } from 'utils/GitUtils'
import { ButtonRoleProps, formatNumber, waitUntil } from 'utils/Utils'
import { DiffViewer, DIFF2HTML_CONFIG, DiffViewStyle } from 'components/DiffViewer/DiffViewer'
import { DiffViewer, DIFF2HTML_CONFIG, ViewStyle } from 'components/DiffViewer/DiffViewer'
import { useEventListener } from 'hooks/useEventListener'
import { UserPreference, useUserPreference } from 'hooks/useUserPreference'
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
import { PullRequestTabContentWrapper } from '../PullRequestTabContentWrapper'
import diffExample from 'raw-loader!./example2.diff'
import diffExample from 'raw-loader!./example.diff'
import css from './PullRequestDiff.module.scss'
const STICKY_TOP_POSITION = 64
const STICKY_HEADER_HEIGHT = 150
export const PullRequestDiff: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pullRequestMetadata'>> = () => {
const { getString } = useStrings()
const [viewStyle, setViewStyle] = useUserPreference(UserPreference.DIFF_VIEW_STYLE, DiffViewStyle.SPLIT)
const [viewStyle, setViewStyle] = useUserPreference(UserPreference.DIFF_VIEW_STYLE, ViewStyle.SPLIT)
const [diffs, setDiffs] = useState<DiffFile[]>([])
const [stickyInAction, setStickyInAction] = useState(false)
const diffStats = useMemo(() => {
return (diffs || []).reduce(
(obj, diff) => {
obj.addedLines += diff.addedLines
obj.deletedLines += diff.deletedLines
return obj
},
{ addedLines: 0, deletedLines: 0 }
)
}, [diffs])
const [isSticky, setSticky] = useState(false)
const diffStats = useMemo(
() =>
(diffs || []).reduce(
(obj, diff) => {
obj.addedLines += diff.addedLines
obj.deletedLines += diff.deletedLines
return obj
},
{ addedLines: 0, deletedLines: 0 }
),
[diffs]
)
useEffect(() => {
setDiffs(Diff2Html.parse(diffExample, DIFF2HTML_CONFIG))
}, [])
useEffect(() => setDiffs(Diff2Html.parse(diffExample, DIFF2HTML_CONFIG)), [])
useEffect(() => {
const onScroll = () => {
if (window.scrollY >= 150) {
if (!stickyInAction) {
setStickyInAction(true)
}
} else {
if (stickyInAction) {
setStickyInAction(false)
}
}
}
window.addEventListener('scroll', onScroll)
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [stickyInAction])
// console.log({ diffs, viewStyle })
useEventListener(
'scroll',
useCallback(() => setSticky(window.scrollY >= STICKY_HEADER_HEIGHT), [])
)
return (
<PullRequestTabContentWrapper loading={undefined} error={undefined} onRetry={noop} className={css.wrapper}>
@ -87,59 +73,69 @@ export const PullRequestDiff: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pull
variation={ButtonVariation.LINK}
className={css.showLabelLink}
tooltip={
<Menu className={css.filesMenu}>
{diffs?.map((diff, index) => (
<MenuItem
key={index}
className={css.menuItem}
icon={<Icon name={CodeIcon.File} padding={{ right: 'xsmall' }} />}
labelElement={
<Layout.Horizontal spacing="xsmall">
{!!diff.addedLines && (
<Text color={Color.GREEN_600} style={{ fontSize: '12px' }}>
+{diff.addedLines}
</Text>
)}
{!!diff.addedLines && !!diff.deletedLines && <PipeSeparator height={8} />}
{!!diff.deletedLines && (
<Text color={Color.RED_500} style={{ fontSize: '12px' }}>
-{diff.deletedLines}
</Text>
)}
</Layout.Horizontal>
}
text={
diff.isDeleted
? diff.oldName
: diff.isRename
? `${diff.oldName} -> ${diff.newName}`
: diff.newName
}
onClick={() => {
const containerDOM = document.getElementById(`file-diff-container-${index}`)
<Container padding="small" className={css.filesMenu}>
<Menu>
{diffs?.map((diff, index) => (
<MenuItem
key={index}
className={css.menuItem}
icon={<Icon name={CodeIcon.File} padding={{ right: 'xsmall' }} />}
labelElement={
<Layout.Horizontal spacing="xsmall">
{!!diff.addedLines && (
<Text color={Color.GREEN_600} style={{ fontSize: '12px' }}>
+{diff.addedLines}
</Text>
)}
{!!diff.addedLines && !!diff.deletedLines && <PipeSeparator height={8} />}
{!!diff.deletedLines && (
<Text color={Color.RED_500} style={{ fontSize: '12px' }}>
-{diff.deletedLines}
</Text>
)}
</Layout.Horizontal>
}
text={
diff.isDeleted
? diff.oldName
: diff.isRename
? `${diff.oldName} -> ${diff.newName}`
: diff.newName
}
onClick={() => {
// When an item is clicked, do these:
// 1. Scroll into the diff container of the file.
// The diff content might not be rendered yet since it's off-screen
// 2. Wait until its content is rendered
// 3. Adjust scroll position as when diff content is rendered, current
// window scroll position might push diff content up, we need to push
// it down again to make sure first line of content is visible and not
// covered by sticky headers
const containerDOM = document.getElementById(`file-diff-container-${index}`)
if (containerDOM) {
containerDOM.scrollIntoView()
waitUntil(
() => !!containerDOM.querySelector('[data-rendered="true"]'),
() => {
containerDOM.scrollIntoView()
// Fix scrolling position messes up with sticky header
const { y } = containerDOM.getBoundingClientRect()
if (y - STICKY_TOP_POSITION < 1) {
if (STICKY_TOP_POSITION) {
window.scroll({ top: window.scrollY - STICKY_TOP_POSITION })
if (containerDOM) {
containerDOM.scrollIntoView()
waitUntil(
() => !!containerDOM.querySelector('[data-rendered="true"]'),
() => {
containerDOM.scrollIntoView()
if (containerDOM.getBoundingClientRect().y - STICKY_TOP_POSITION < 1) {
if (STICKY_TOP_POSITION) {
window.scroll({ top: window.scrollY - STICKY_TOP_POSITION })
}
}
}
}
)
}
}}
/>
))}
</Menu>
)
}
}}
/>
))}
</Menu>
</Container>
}
tooltipProps={{ interactionKind: 'click', hasBackdrop: true }}>
tooltipProps={{ interactionKind: 'click', hasBackdrop: true, popoverClassName: css.popover }}>
<StringSubstitute str={getString('pr.showLink')} vars={{ count: diffs?.length || 0 }} />
</Button>
),
@ -159,10 +155,10 @@ export const PullRequestDiff: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pull
<BButton
className={cx(
Classes.POPOVER_DISMISS,
viewStyle === DiffViewStyle.SPLIT ? Classes.ACTIVE : ''
viewStyle === ViewStyle.SPLIT ? Classes.ACTIVE : ''
)}
onClick={() => {
setViewStyle(DiffViewStyle.SPLIT)
setViewStyle(ViewStyle.SPLIT)
window.scroll({ top: 0 })
}}>
{getString('pr.split')}
@ -170,10 +166,10 @@ export const PullRequestDiff: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pull
<BButton
className={cx(
Classes.POPOVER_DISMISS,
viewStyle === DiffViewStyle.UNIFIED ? Classes.ACTIVE : ''
viewStyle === ViewStyle.UNIFIED ? Classes.ACTIVE : ''
)}
onClick={() => {
setViewStyle(DiffViewStyle.UNIFIED)
setViewStyle(ViewStyle.UNIFIED)
window.scroll({ top: 0 })
}}>
{getString('pr.unified')}
@ -182,6 +178,7 @@ export const PullRequestDiff: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pull
</Layout.Horizontal>
</Container>
}
tooltipProps={{ interactionKind: 'click' }}
iconProps={{ size: 14, padding: { right: 3 } }}
rightIconProps={{ size: 13, padding: { left: 0 } }}
padding={{ left: 'small' }}
@ -191,7 +188,7 @@ export const PullRequestDiff: React.FC<Pick<GitInfoProps, 'repoMetadata' | 'pull
}}
/>
</Text>
{stickyInAction && (
{isSticky && (
<Layout.Horizontal padding={{ left: 'small' }}>
<PipeSeparator height={10} />
<Button

View File

@ -0,0 +1,9 @@
/* eslint-disable */
// this is an auto-generated file
declare const styles: {
readonly main: string
readonly state: string
readonly metaline: string
readonly time: string
}
export default styles

View File

@ -3,12 +3,13 @@ import { Button, ButtonVariation, Color, Container, FlexExpander, Heading, Layou
import { useHistory } from 'react-router-dom'
import { SourceCodeViewer } from 'components/SourceCodeViewer/SourceCodeViewer'
import type { RepoFileContent } from 'services/code'
import { CodeIcon, GitCommitAction, GitInfoProps, isRefATag } from 'utils/GitUtils'
import { CodeIcon, findMarkdownInfo, GitCommitAction, GitInfoProps, isRefATag } from 'utils/GitUtils'
import { filenameToLanguage } from 'utils/Utils'
import { useAppContext } from 'AppContext'
import { LatestCommitForFile } from 'components/LatestCommit/LatestCommit'
import { CommitModalButton } from 'components/CommitModalButton/CommitModalButton'
import { useStrings } from 'framework/strings'
import { Readme } from '../FolderContent/Readme'
import css from './FileContent.module.scss'
export function FileContent({
@ -24,6 +25,7 @@ export function FileContent({
() => window.atob((resourceContent?.content as RepoFileContent)?.data || ''),
[resourceContent?.content]
)
const markdownInfo = useMemo(() => findMarkdownInfo(resourceContent), [resourceContent])
return (
<Layout.Vertical spacing="small">
@ -83,7 +85,11 @@ export function FileContent({
{(resourceContent?.content as RepoFileContent)?.data && (
<Container className={css.content}>
<SourceCodeViewer language={filenameToLanguage(resourceContent?.name)} source={content} />
{!markdownInfo ? (
<SourceCodeViewer language={filenameToLanguage(resourceContent?.name)} source={content} />
) : (
<Readme metadata={repoMetadata} readmeInfo={markdownInfo} contentOnly maxWidth="calc(100vw - 346px)" />
)}
</Container>
)}
</Container>

View File

@ -1,15 +1,24 @@
.readmeContainer {
margin-top: var(--spacing-xlarge) !important;
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
border-radius: 4px;
&:not(.contentOnly) {
margin-top: var(--spacing-xlarge) !important;
box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16);
border-radius: 4px;
.heading {
border-bottom: 1px solid var(--grey-100);
align-items: center;
padding-left: var(--spacing-large) !important;
.heading {
border-bottom: 1px solid var(--grey-100);
align-items: center;
padding-left: var(--spacing-large) !important;
}
}
&.contentOnly {
.readmeContent {
padding: var(--spacing-large) !important;
}
}
.readmeContent {
max-width: var(--max-width, calc(100vw - 320px));
padding: var(--spacing-xxlarge) !important;
}
}

View File

@ -2,6 +2,7 @@
// this is an auto-generated file
declare const styles: {
readonly readmeContainer: string
readonly contentOnly: string
readonly heading: string
readonly readmeContent: string
}

View File

@ -2,6 +2,7 @@ import React from 'react'
import { Container, Color, Layout, Button, FlexExpander, ButtonVariation, Heading } from '@harness/uicore'
import { useHistory } from 'react-router-dom'
import { useGet } from 'restful-react'
import cx from 'classnames'
import { MarkdownViewer } from 'components/SourceCodeViewer/SourceCodeViewer'
import { useAppContext } from 'AppContext'
import type { OpenapiContentInfo, OpenapiGetContentOutput, RepoFileContent, TypesRepository } from 'services/code'
@ -12,9 +13,11 @@ interface FolderContentProps {
metadata: TypesRepository
gitRef?: string
readmeInfo: OpenapiContentInfo
contentOnly?: boolean
maxWidth?: string
}
export function Readme({ metadata, gitRef, readmeInfo }: FolderContentProps) {
function ReadmeViewer({ metadata, gitRef, readmeInfo, contentOnly, maxWidth }: FolderContentProps) {
const history = useHistory()
const { routes } = useAppContext()
@ -27,24 +30,29 @@ export function Readme({ metadata, gitRef, readmeInfo }: FolderContentProps) {
})
return (
<Container className={css.readmeContainer} background={Color.WHITE}>
<Layout.Horizontal padding="small" className={css.heading}>
<Heading level={5}>{readmeInfo.name}</Heading>
<FlexExpander />
<Button
variation={ButtonVariation.ICON}
icon={CodeIcon.Edit}
onClick={() => {
history.push(
routes.toCODEFileEdit({
repoPath: metadata.path as string,
gitRef: gitRef || (metadata.defaultBranch as string),
resourcePath: readmeInfo.path as string
})
)
}}
/>
</Layout.Horizontal>
<Container
className={cx(css.readmeContainer, contentOnly ? css.contentOnly : '')}
background={Color.WHITE}
style={{ '--max-width': maxWidth } as React.CSSProperties}>
{!contentOnly && (
<Layout.Horizontal padding="small" className={css.heading}>
<Heading level={5}>{readmeInfo.name}</Heading>
<FlexExpander />
<Button
variation={ButtonVariation.ICON}
icon={CodeIcon.Edit}
onClick={() => {
history.push(
routes.toCODEFileEdit({
repoPath: metadata.path as string,
gitRef: gitRef || (metadata.defaultBranch as string),
resourcePath: readmeInfo.path as string
})
)
}}
/>
</Layout.Horizontal>
)}
{/* TODO: Loading and Error handling */}
{(data?.content as RepoFileContent)?.data && (
@ -55,3 +63,5 @@ export function Readme({ metadata, gitRef, readmeInfo }: FolderContentProps) {
</Container>
)
}
export const Readme = React.memo(ReadmeViewer)

View File

@ -116,6 +116,9 @@ export const findReadmeInfo = (content: Nullable<OpenapiGetContentOutput>): Open
entry => entry.type === GitContentType.FILE && /^readme(.md)?$/.test(entry?.name?.toLowerCase() || '')
)
export const findMarkdownInfo = (content: Nullable<OpenapiGetContentOutput>): OpenapiContentInfo | undefined =>
content?.type === GitContentType.FILE && /.md$/.test(content?.name?.toLowerCase() || '') ? content : undefined
export const isRefATag = (gitRef: string) => gitRef.includes(REFS_TAGS_PREFIX)
/**

View File

@ -1140,6 +1140,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.18.6":
version "7.20.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/template@^7.12.7", "@babel/template@^7.18.10", "@babel/template@^7.3.3":
version "7.18.10"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
@ -1273,6 +1280,241 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.3.2":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.4.0.tgz#76ac9a2a411a4cc6e13103014dba5e0fe601da5a"
integrity sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.6.0"
"@lezer/common" "^1.0.0"
"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.1.0":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-6.1.2.tgz#84fb7d170047c3aeb7b0047ace59510bb19208de"
integrity sha512-sO3jdX1s0pam6lIdeSJLMN3DQ6mPEbM4yLvyKkdqtmd/UDwhXA5+AwFJ89rRXm6vTeOXBsE5cAmlos/t7MJdgg==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@codemirror/lang-cpp@^6.0.0":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@codemirror/lang-cpp/-/lang-cpp-6.0.2.tgz#076c98340c3beabde016d7d83e08eebe17254ef9"
integrity sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==
dependencies:
"@codemirror/language" "^6.0.0"
"@lezer/cpp" "^1.0.0"
"@codemirror/lang-css@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-css/-/lang-css-6.0.1.tgz#470fff614e4cfbbe796ec43103420d59c797dd7a"
integrity sha512-rlLq1Dt0WJl+2epLQeAsfqIsx3lGu4HStHCJu95nGGuz2P2fNugbU3dQYafr2VRjM4eMC9HviI6jvS98CNtG5w==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@lezer/css" "^1.0.0"
"@codemirror/lang-html@^6.0.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@codemirror/lang-html/-/lang-html-6.4.0.tgz#55227541426ec4efb13d9da45d7a85cabaa45c73"
integrity sha512-HHged0d9AQ/mpjYLTYDVdtI7235dO0COFNgc5uuiGokgjWx3L/sjMSw5aS/Nk7JG++LhsohG5HMNTCuqAq3Tcg==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/lang-css" "^6.0.0"
"@codemirror/lang-javascript" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.2.2"
"@lezer/common" "^1.0.0"
"@lezer/css" "^1.1.0"
"@lezer/html" "^1.1.0"
"@codemirror/lang-java@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-java/-/lang-java-6.0.1.tgz#03bd06334da7c8feb9dff6db01ac6d85bd2e48bb"
integrity sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==
dependencies:
"@codemirror/language" "^6.0.0"
"@lezer/java" "^1.0.0"
"@codemirror/lang-javascript@^6.0.0":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@codemirror/lang-javascript/-/lang-javascript-6.1.2.tgz#a11812ca1d21301cdeb80e51b4c007edcf55f813"
integrity sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/javascript" "^1.0.0"
"@codemirror/lang-json@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-json/-/lang-json-6.0.1.tgz#0a0be701a5619c4b0f8991f9b5e95fe33f462330"
integrity sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==
dependencies:
"@codemirror/language" "^6.0.0"
"@lezer/json" "^1.0.0"
"@codemirror/lang-markdown@^6.0.0":
version "6.0.5"
resolved "https://registry.yarnpkg.com/@codemirror/lang-markdown/-/lang-markdown-6.0.5.tgz#61393c7e2552528daee6aa4eca63428aa00832bd"
integrity sha512-qH0THRYc2M7pIJoAp6jstXZkv8ZMVhNaBm7Bs4+0SLHhHlwX53txFy98AcPwrfq0Sh8Zi6RAuj9j/GyL8E1MKw==
dependencies:
"@codemirror/lang-html" "^6.0.0"
"@codemirror/language" "^6.3.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/markdown" "^1.0.0"
"@codemirror/lang-php@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-php/-/lang-php-6.0.1.tgz#fa34cc75562178325861a5731f79bd621f57ffaa"
integrity sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==
dependencies:
"@codemirror/lang-html" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/php" "^1.0.0"
"@codemirror/lang-python@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@codemirror/lang-python/-/lang-python-6.1.0.tgz#9e46bd79373664322eff6035ec42a9916c1616bb"
integrity sha512-a/JhyPYn5qz5T8WtAfZCuAZcfClgNVb7UZzdLr76bWUeG7Usd3Un5o8UQOkZ/5Xw+EM5YGHHG+T6ickMYkDcRQ==
dependencies:
"@codemirror/autocomplete" "^6.3.2"
"@codemirror/language" "^6.0.0"
"@lezer/python" "^1.0.0"
"@codemirror/lang-rust@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-rust/-/lang-rust-6.0.1.tgz#d6829fc7baa39a15bcd174a41a9e0a1bf7cf6ba8"
integrity sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==
dependencies:
"@codemirror/language" "^6.0.0"
"@lezer/rust" "^1.0.0"
"@codemirror/lang-sql@^6.0.0":
version "6.3.3"
resolved "https://registry.yarnpkg.com/@codemirror/lang-sql/-/lang-sql-6.3.3.tgz#3a0cad06cd5be5557850035c5f6592ea41dd21c2"
integrity sha512-VNsHju8500fkiDyDU8jZyGQ8M0iXU0SmfeCoCeAYkACcEFlX63BOT8311pICXyw43VYRbS23w54RgSEQmixGjQ==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@codemirror/lang-wast@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-wast/-/lang-wast-6.0.1.tgz#c15bec84548a5e9b0a43fa69fb63631d087d6047"
integrity sha512-sQLsqhRjl2MWG3rxZysX+2XAyed48KhLBHLgq9xcKxIJu3npH/G+BIXW5NM5mHeDUjG0jcGh9BcjP0NfMStuzA==
dependencies:
"@codemirror/language" "^6.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@codemirror/lang-xml@^6.0.0":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@codemirror/lang-xml/-/lang-xml-6.0.1.tgz#ac2dd701d26683163543248b5abc56829ba7fcc6"
integrity sha512-0tvycUTElajCcRKgsszhKjWX+uuOogdu5+enpfqYA+j0gnP8ek7LRxujh2/XMPRdXt/hwOML4slJLE7r2eX3yQ==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/xml" "^1.0.0"
"@codemirror/language-data@^6.1.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@codemirror/language-data/-/language-data-6.1.0.tgz#479eff66289a6453493f7c8213d7b2ceb95c89f6"
integrity sha512-g9V23fuLRI9AEbpM6bDy1oquqgpFlIDHTihUhL21NPmxp+x67ZJbsKk+V71W7/Bj8SCqEO1PtqQA/tDGgt1nfw==
dependencies:
"@codemirror/lang-cpp" "^6.0.0"
"@codemirror/lang-css" "^6.0.0"
"@codemirror/lang-html" "^6.0.0"
"@codemirror/lang-java" "^6.0.0"
"@codemirror/lang-javascript" "^6.0.0"
"@codemirror/lang-json" "^6.0.0"
"@codemirror/lang-markdown" "^6.0.0"
"@codemirror/lang-php" "^6.0.0"
"@codemirror/lang-python" "^6.0.0"
"@codemirror/lang-rust" "^6.0.0"
"@codemirror/lang-sql" "^6.0.0"
"@codemirror/lang-wast" "^6.0.0"
"@codemirror/lang-xml" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/legacy-modes" "^6.1.0"
"@codemirror/language@^6.0.0", "@codemirror/language@^6.3.0":
version "6.3.1"
resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-6.3.1.tgz#1d61f33aa5de9aa74a713ee1f5ce600adc74df6b"
integrity sha512-MK+G1QKaGfSEUg9YEFaBkMBI6j1ge4VMBPZv9fDYotw7w695c42x5Ba1mmwBkesYnzYFBfte6Hh9TDcKa6xORQ==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/common" "^1.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
style-mod "^4.0.0"
"@codemirror/legacy-modes@^6.1.0":
version "6.3.1"
resolved "https://registry.yarnpkg.com/@codemirror/legacy-modes/-/legacy-modes-6.3.1.tgz#77ab3f3db1ce3e47aad7a5baac3a4b12844734a5"
integrity sha512-icXmCs4Mhst2F8mE0TNpmG6l7YTj1uxam3AbZaFaabINH5oWAdg2CfR/PVi+d/rqxJ+TuTnvkKK5GILHrNThtw==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/lint@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-6.1.0.tgz#f006142d3a580fdb8ffc2faa3361b2232c08e079"
integrity sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
crelt "^1.0.5"
"@codemirror/search@^6.0.0":
version "6.2.3"
resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-6.2.3.tgz#fab933fef1b1de8ef40cda275c73d9ac7a1ff40f"
integrity sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==
dependencies:
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
crelt "^1.0.5"
"@codemirror/state@^6.0.0", "@codemirror/state@^6.1.1", "@codemirror/state@^6.1.4":
version "6.1.4"
resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.1.4.tgz#2b654ae233ac4f41ee89ce095509ea35ecdf1031"
integrity sha512-g+3OJuRylV5qsXuuhrc6Cvs1NQluNioepYMM2fhnpYkNk7NgX+j0AFuevKSVKzTDmDyt9+Puju+zPdHNECzCNQ==
"@codemirror/theme-one-dark@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@codemirror/theme-one-dark/-/theme-one-dark-6.1.0.tgz#6f8b3c7fc22e9fec59edd573f4ba9546db42e007"
integrity sha512-AiTHtFRu8+vWT9wWUWDM+cog6ZwgivJogB1Tm/g40NIpLwph7AnmxrSzWfvJN5fBVufsuwBxecQCNmdcR5D7Aw==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@lezer/highlight" "^1.0.0"
"@codemirror/view@^6.0.0", "@codemirror/view@^6.2.2", "@codemirror/view@^6.6.0":
version "6.7.1"
resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.7.1.tgz#370e95d6f001e7f5cadc459807974b4f0a6eb225"
integrity sha512-kYtS+uqYw/q/0ytYxpkqE1JVuK5NsbmBklWYhwLFTKO9gVuTdh/kDEeZPKorbqHcJ+P+ucrhcsS1czVweOpT2g==
dependencies:
"@codemirror/state" "^6.1.4"
style-mod "^4.0.0"
w3c-keyname "^2.2.4"
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@ -2290,6 +2532,114 @@
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
"@lezer/common@^1.0.0":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.0.2.tgz#8fb9b86bdaa2ece57e7d59e5ffbcb37d71815087"
integrity sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==
"@lezer/cpp@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@lezer/cpp/-/cpp-1.0.0.tgz#3293fd88aaf16a6d4f18188602b4d931be8f0915"
integrity sha512-Klk3/AIEKoptmm6cNm7xTulNXjdTKkD+hVOEcz/NeRg8tIestP5hsGHJeFDR/XtyDTxsjoPjKZRIGohht7zbKw==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/css@^1.0.0", "@lezer/css@^1.1.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@lezer/css/-/css-1.1.1.tgz#c36dcb0789317cb80c3740767dd3b85e071ad082"
integrity sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/highlight@^1.0.0":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.1.3.tgz#bf5a36c2ee227f526d74997ac91f7777e29bd25d"
integrity sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/html@^1.1.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@lezer/html/-/html-1.2.0.tgz#1db82c8005be288eb99c568baf66c8730b01877a"
integrity sha512-T6sseEJQPTFayX3h9DINh7KyFBwCjEtrqQRD+7USmtkJh1xHDutsB6KYHKveSd+TbsedIPAcZwar+gkHl1rVSw==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/java@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@lezer/java/-/java-1.0.0.tgz#fe74e062350f7a4268107e7562971bfbad994f49"
integrity sha512-z2EA0JHq2WoiKfQy5uOOd4t17PJtq8guh58gPkSzOnNcQ7DNbkrU+Axak+jL8+Noinwyz2tRNOseQFj+Tg+P0A==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/javascript@^1.0.0":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@lezer/javascript/-/javascript-1.3.2.tgz#bc386a497e5c99d130ec30f0f5f55152e788779d"
integrity sha512-c3QOdmTG2HE4M7wt8/pthzqib194ph3CUdTIyNM1+yH2jE4WClAUGSNEcruOY47auSRsegpqBMOb1zWFovFPUw==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/json@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@lezer/json/-/json-1.0.0.tgz#848ad9c2c3e812518eb02897edd5a7f649e9c160"
integrity sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/lr@^1.0.0":
version "1.2.5"
resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.2.5.tgz#e9088164a711690596f17378665e0554157c9b03"
integrity sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/markdown@^1.0.0":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@lezer/markdown/-/markdown-1.0.2.tgz#8c804a9f6fe1ccca4a20acd2fd9fbe0fae1ae178"
integrity sha512-8CY0OoZ6V5EzPjSPeJ4KLVbtXdLBd8V6sRCooN5kHnO28ytreEGTyrtU/zUwo/XLRzGr/e1g44KlzKi3yWGB5A==
dependencies:
"@lezer/common" "^1.0.0"
"@lezer/highlight" "^1.0.0"
"@lezer/php@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@lezer/php/-/php-1.0.0.tgz#522d2d2d8a4eee6c598060e2a222526953c66adb"
integrity sha512-kFQu/mk/vmjpA+fjQU87d9eimqKJ9PFCa8CZCPFWGEwNnm7Ahpw32N+HYEU/YAQ0XcfmOAnW/YJCEa8WpUOMMw==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/python@^1.0.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@lezer/python/-/python-1.1.1.tgz#6d688071ed93d063a589a7d31df3279b1eba607a"
integrity sha512-ArUGh9kvdaOVu6IkSaYUS9WFQeMAFVWKRuZo6vexnxoeCLnxf0Y9DCFEAMMa7W9SQBGYE55OarSpPqSkdOXSCA==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/rust@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@lezer/rust/-/rust-1.0.0.tgz#939f3e7b0376ebe13f4ac336ed7d59ca2c8adf52"
integrity sha512-IpGAxIjNxYmX9ra6GfQTSPegdCAWNeq23WNmrsMMQI7YNSvKtYxO4TX5rgZUmbhEucWn0KTBMeDEPXg99YKtTA==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@lezer/xml@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@lezer/xml/-/xml-1.0.0.tgz#02817a3d421e7189b50fd31ed17430b2e1c8c0d8"
integrity sha512-73iI9UK8iqSvWtLlOEl/g+50ivwQn8Ge6foHVN66AXUS1RccFnAoc7BYU8b3c8/rP6dfCOGqAGaWLxBzhj60MA==
dependencies:
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@mdx-js/mdx@^1.6.22":
version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
@ -4471,26 +4821,77 @@
"@typescript-eslint/types" "5.40.0"
eslint-visitor-keys "^3.3.0"
"@uiw/codemirror-extensions-basic-setup@4.19.4":
version "4.19.4"
resolved "https://registry.yarnpkg.com/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.19.4.tgz#faa18c671df937c4f370bbbfdf9ebb454e56f4ae"
integrity sha512-iUni1tTpuSO6j1NShEo01eYGtED+9OYKyQ4Y54fBxluhZCT1Z/25sP05Im2rpt/VHE1fReRaC28FEBR5phBAfA==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/commands" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/search" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@uiw/codemirror-extensions-events@^4.12.3":
version "4.19.4"
resolved "https://registry.yarnpkg.com/@uiw/codemirror-extensions-events/-/codemirror-extensions-events-4.19.4.tgz#44b59a267752aaab62e94b06f07cd59e6c93f05e"
integrity sha512-m2aAnR4K+yN8RX/yTKDw2mheNOq9xdVPYiawynroSkKF/6coFQcRa4j6DDDGkL4p0NxXG5vgzYRcCaQGVkQILA==
"@uiw/codemirror-themes@^4.12.3":
version "4.19.4"
resolved "https://registry.yarnpkg.com/@uiw/codemirror-themes/-/codemirror-themes-4.19.4.tgz#78a443d98ec8261134349163efd01e939e4a3f22"
integrity sha512-n2k1GPH9oTeeXY/Xbu5KQiw2rvrM5wkvhFDNRvoX9N/2e3+/lmyloZZ1C1Pk/OpDfOH2hWns8jiH7BKfn+I82A==
dependencies:
"@codemirror/language" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
"@uiw/copy-to-clipboard@~1.0.12":
version "1.0.12"
resolved "https://registry.yarnpkg.com/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.12.tgz#46f563bd6f3007895f95855e5b4bb692c7251933"
integrity sha512-3tt7FVSbjtBCNBhffy7k26rpnEmk8GQj9QkTGZBIfpHU7mG3Buryt69u6xooYM7/gmv7GIqD4QxxIauIp2HHKg==
"@uiw/react-markdown-preview@^4.1.2":
version "4.1.2"
resolved "https://registry.yarnpkg.com/@uiw/react-markdown-preview/-/react-markdown-preview-4.1.2.tgz#41bac8d51c3e5663629afbeb293d20b3486fbd2c"
integrity sha512-6RKQvuMW/r7JRGV1zZHHmylJFUuEyZtxpC8HqlY0R9p3PtH4hGQzu1S8VCQNAr3RG7u8ByWgmicoF6/UD9cZ6w==
"@uiw/react-codemirror@^4.12.3":
version "4.19.4"
resolved "https://registry.yarnpkg.com/@uiw/react-codemirror/-/react-codemirror-4.19.4.tgz#0e5f2534705c24d166b17780496e2c96bf0991d9"
integrity sha512-FGzrxLrY7t+u7ei7BJ3xlhUuRnSVg5N3mnFHXJzGlk6d1faHyoEv/2W2pLl/7BJPK2hL/tTGSieSNM9O1FxWEQ==
dependencies:
"@babel/runtime" "^7.18.6"
"@codemirror/commands" "^6.1.0"
"@codemirror/state" "^6.1.1"
"@codemirror/theme-one-dark" "^6.0.0"
"@uiw/codemirror-extensions-basic-setup" "4.19.4"
codemirror "^6.0.0"
"@uiw/react-markdown-editor@^5.10.1":
version "5.10.1"
resolved "https://registry.yarnpkg.com/@uiw/react-markdown-editor/-/react-markdown-editor-5.10.1.tgz#1ea608294f0523600458441fc8ed89f68b92b0a7"
integrity sha512-0gUTPGnw5LWD6MVLLFNCMpnEgNcPI828scTa4FNfNY+oeCj4E2YQxmnftHciAIaQ66KXK9CoC9Uix0XNEZKnKg==
dependencies:
"@codemirror/lang-markdown" "^6.0.0"
"@codemirror/language-data" "^6.1.0"
"@uiw/codemirror-extensions-events" "^4.12.3"
"@uiw/codemirror-themes" "^4.12.3"
"@uiw/react-codemirror" "^4.12.3"
"@uiw/react-markdown-preview" "^4.0.20"
"@uiw/react-markdown-preview@4.1.6", "@uiw/react-markdown-preview@^4.0.20":
version "4.1.6"
resolved "https://registry.yarnpkg.com/@uiw/react-markdown-preview/-/react-markdown-preview-4.1.6.tgz#2a662706e17a10cdd727b85ac24c74d737b73b18"
integrity sha512-22dGV1HfyDXnObmF4ot4Is+o7QrKvg10Q3SBwDKoyJk3KIRMhCo/HCT+YrCCEtVTQIz2KffCDq2Cx9yvUmE6Ow==
dependencies:
"@babel/runtime" "^7.17.2"
"@uiw/copy-to-clipboard" "~1.0.12"
react-markdown "~8.0.0"
rehype-attr "~2.0.7"
rehype-attr "~2.1.0"
rehype-autolink-headings "~6.1.1"
rehype-ignore "^1.0.1"
rehype-prism-plus "~1.5.0"
rehype-raw "^6.1.1"
rehype-rewrite "~3.0.6"
rehype-slug "~5.0.1"
rehype-slug "~5.1.0"
remark-gfm "~3.0.1"
unist-util-visit "^4.1.0"
@ -6772,6 +7173,19 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==
codemirror@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-6.0.1.tgz#62b91142d45904547ee3e0e0e4c1a79158035a29"
integrity sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==
dependencies:
"@codemirror/autocomplete" "^6.0.0"
"@codemirror/commands" "^6.0.0"
"@codemirror/language" "^6.0.0"
"@codemirror/lint" "^6.0.0"
"@codemirror/search" "^6.0.0"
"@codemirror/state" "^6.0.0"
"@codemirror/view" "^6.0.0"
collapse-white-space@^1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287"
@ -7268,6 +7682,11 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
crelt@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==
cron-validator@^1.2.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/cron-validator/-/cron-validator-1.3.1.tgz#8f2fe430f92140df77f91178ae31fc1e3a48a20e"
@ -9526,11 +9945,16 @@ github-markdown-css@^5.1.0:
resolved "https://registry.yarnpkg.com/github-markdown-css/-/github-markdown-css-5.1.0.tgz#a96281cd90a8969e3c13b9d3ca6a733a523a00a6"
integrity sha512-QLtORwHHtUHhPMHu7i4GKfP6Vx5CWZn+NKQXe+cBhslY1HEt0CTEkP4d/vSROKV0iIJSpl4UtlQ16AD8C6lMug==
github-slugger@^1.0.0, github-slugger@^1.1.1:
github-slugger@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e"
integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==
github-slugger@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a"
integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@ -16292,6 +16716,11 @@ regenerate@^1.4.2:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
@ -16387,10 +16816,10 @@ regjsparser@^0.9.1:
dependencies:
jsesc "~0.5.0"
rehype-attr@~2.0.7:
version "2.0.8"
resolved "https://registry.yarnpkg.com/rehype-attr/-/rehype-attr-2.0.8.tgz#d3d0120d241ae8982e73dc3ae18eae419d3918c7"
integrity sha512-EISuHQNPUN2InK+R1tmiAp3IxvNw4utF3MY6+6QoltAXwSqgvC3RVhfaOVUxyakTb4KJmfii3s0b/a7DlQYZTg==
rehype-attr@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/rehype-attr/-/rehype-attr-2.1.2.tgz#8d2e284ea349936e88e59460cf10081511bc9924"
integrity sha512-Se8EOM4hINnoYciwW+Yu2gB3ZXMzA9zSM7Wzq4G2id+qQkRr8Bpwy9NPrGt3FVZx+sR1opkorBlaO3/CyGfEkA==
dependencies:
unified "~10.1.1"
unist-util-visit "~4.1.0"
@ -16477,13 +16906,13 @@ rehype-sanitize@^5.0.1:
hast-util-sanitize "^4.0.0"
unified "^10.0.0"
rehype-slug@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-5.0.1.tgz#6e732d0c55b3b1e34187e74b7363fb53229e5f52"
integrity sha512-X5v3wV/meuOX9NFcGhJvUpEjIvQl2gDvjg3z40RVprYFt7q3th4qMmYLULiu3gXvbNX1ppx+oaa6JyY1W67pTA==
rehype-slug@~5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-5.1.0.tgz#1f7e69be7ea1a2067bcc4cfe58e74c881d5c047e"
integrity sha512-Gf91dJoXneiorNEnn+Phx97CO7oRMrpi+6r155tTxzGuLtm+QrI4cTwCa9e1rtePdL4i9tSO58PeSS6HWfgsiw==
dependencies:
"@types/hast" "^2.0.0"
github-slugger "^1.1.1"
github-slugger "^2.0.0"
hast-util-has-property "^2.0.0"
hast-util-heading-rank "^2.0.0"
hast-util-to-string "^2.0.0"
@ -18010,6 +18439,11 @@ style-loader@^3.3.0:
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
style-mod@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01"
integrity sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==
style-to-object@0.3.0, style-to-object@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46"
@ -19536,6 +19970,11 @@ w3c-hr-time@^1.0.2:
dependencies:
browser-process-hrtime "^1.0.0"
w3c-keyname@^2.2.4:
version "2.2.6"
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.6.tgz#8412046116bc16c5d73d4e612053ea10a189c85f"
integrity sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==
w3c-xmlserializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a"