Add support for rendering pdf, video, image and other binary files (#151)

This commit is contained in:
Tan Nhu 2023-06-19 22:36:01 +00:00 committed by Harness
parent deb9cf473a
commit 2ba2d48f43
16 changed files with 1023 additions and 55 deletions

View File

@ -88,6 +88,7 @@
"react-keywords": "^0.0.5",
"react-markdown": "^8.0.3",
"react-monaco-editor": "^0.52.0",
"react-pdf": "^7.1.2",
"react-resize-detector": "^7.1.2",
"react-router-dom": "^5.2.0",
"react-split-pane": "^0.1.92",

View File

@ -37,7 +37,8 @@ const App: React.FC<AppProps> = React.memo(function App({
(): Partial<RequestInit> => buildResfulReactRequestOptions(hooks?.useGetToken?.() || token),
[token, hooks]
)
const queryParams = useMemo(() => (!standalone ? { routingId: space.split('/').shift() } : {}), [space, standalone])
const routingId = useMemo(() => (standalone ? '' : space.split('/').shift() || ''), [standalone, space])
const queryParams = useMemo(() => (!standalone ? { routingId } : {}), [standalone, routingId])
useEffect(() => {
languageLoader(lang).then(setStrings)
@ -62,6 +63,7 @@ const App: React.FC<AppProps> = React.memo(function App({
<AppContextProvider
value={{
standalone,
routingId,
space,
routes,
lang,

View File

@ -25,7 +25,8 @@ const AppContext = React.createContext<AppContextProps>({
routes,
hooks: {},
currentUser: defaultCurrentUser,
currentUserProfileURL: ''
currentUserProfileURL: '',
routingId: ''
})
export const AppContextProvider: React.FC<{ value: AppProps }> = React.memo(function AppContextProvider({

View File

@ -24,6 +24,9 @@ export interface AppProps {
/** Flag to tell if App is mounted as a standalone app */
standalone: boolean
/** Harness routingId */
routingId: string
/** App children. When provided, children is a remote view which will be mounted under App contexts */
children?: React.ReactNode

View File

@ -11,6 +11,6 @@ import './bootstrap.scss'
window.STRIP_CODE_PREFIX = true
ReactDOM.render(
<App standalone routes={routes} hooks={{}} currentUser={defaultCurrentUser} currentUserProfileURL="" />,
<App standalone routes={routes} hooks={{}} currentUser={defaultCurrentUser} currentUserProfileURL="" routingId="" />,
document.getElementById('react-root')
)

View File

@ -7,7 +7,7 @@ import cx from 'classnames'
import type { TypesCommit } from 'services/code'
import { CommitActions } from 'components/CommitActions/CommitActions'
import { useAppContext } from 'AppContext'
import { formatDate } from 'utils/Utils'
import { formatBytes, formatDate } from 'utils/Utils'
import type { GitInfoProps } from 'utils/GitUtils'
import { PipeSeparator } from 'components/PipeSeparator/PipeSeparator'
import css from './LatestCommit.module.scss'
@ -15,6 +15,7 @@ import css from './LatestCommit.module.scss'
interface LatestCommitProps extends Pick<GitInfoProps, 'repoMetadata'> {
latestCommit?: TypesCommit
standaloneStyle?: boolean
size?: number
}
export function LatestCommitForFolder({ repoMetadata, latestCommit, standaloneStyle }: LatestCommitProps) {
@ -46,7 +47,7 @@ export function LatestCommitForFolder({ repoMetadata, latestCommit, standaloneSt
)
}
export function LatestCommitForFile({ repoMetadata, latestCommit, standaloneStyle }: LatestCommitProps) {
export function LatestCommitForFile({ repoMetadata, latestCommit, standaloneStyle, size }: LatestCommitProps) {
const { routes } = useAppContext()
const commitURL = routes.toCODECommits({
repoPath: repoMetadata.path as string,
@ -72,6 +73,14 @@ export function LatestCommitForFile({ repoMetadata, latestCommit, standaloneStyl
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_400}>
{formatDate(latestCommit?.author?.when as string)}
</Text>
{size && size > 0 && (
<>
<PipeSeparator height={9} />
<Text font={{ variation: FontVariation.SMALL }} color={Color.GREY_400}>
{formatBytes(size)}
</Text>
</>
)}
<FlexExpander />
<CommitActions sha={latestCommit?.sha as string} href={commitURL} />

View File

@ -43,6 +43,7 @@ export interface StringsMap {
checkRuns: string
checkSuites: string
checks: string
clickHereToDownload: string
clone: string
cloneHTTPS: string
cloneText: string
@ -131,6 +132,7 @@ export interface StringsMap {
failedToDeleteBranch: string
failedToDeleteWebhook: string
fileDeleted: string
fileTooLarge: string
files: string
filesChanged: string
findATag: string
@ -192,6 +194,7 @@ export interface StringsMap {
open: string
optional: string
optionalExtendedDescription: string
overview: string
pageLoading: string
pageNotFound: string
password: string

View File

@ -0,0 +1,51 @@
import { useCallback, useEffect, useState } from 'react'
import { useToaster } from '@harness/uicore'
import { useGet } from 'restful-react'
import { getErrorMessage } from 'utils/Utils'
import type { GitInfoProps } from 'utils/GitUtils'
interface UseDownloadRawFileParams extends Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath'> {
filename: string
}
export function useDownloadRawFile() {
const { error, response, refetch } = useGet({
path: '',
lazy: true
})
const [name, setName] = useState('')
const { showError } = useToaster()
const callback = useCallback(async () => {
if (response) {
const imageBlog = await response.blob()
const imageURL = URL.createObjectURL(imageBlog)
const anchor = document.createElement('a')
anchor.href = imageURL
anchor.download = name
document.body.appendChild(anchor)
anchor.click()
document.body.removeChild(anchor)
URL.revokeObjectURL(imageURL)
}
}, [name, response])
useEffect(() => {
if (error) {
showError(getErrorMessage(error))
} else if (response) {
callback()
}
}, [error, showError, response, callback])
return useCallback(
({ repoMetadata, resourcePath, gitRef, filename = 'download' }: UseDownloadRawFileParams) => {
const rawURL = `/api/v1/repos/${repoMetadata?.path}/+/raw/${resourcePath}`
setName(filename)
refetch({ path: rawURL, queryParams: { git_ref: gitRef } })
},
[refetch]
)
}

View File

@ -419,6 +419,9 @@ tagger: Tagger
confirmDelete: Confirm delete
tagEmpty: Here is no Tag. Try to
newTag: New Tag
overview: Overview
fileTooLarge: File is too large to open. {download}
clickHereToDownload: Click here to download.
viewFile: View the file at this point in the history
viewRepo: View the repository at this point in the history
hideCommitHistory: Hide Rename History for {{file}}

View File

@ -223,7 +223,7 @@ export default function Compare() {
tabList={[
{
id: 'general',
title: <TabTitleWithCount icon={CodeIcon.Chat} title={getString('general')} count={0} />,
title: <TabTitleWithCount icon={CodeIcon.Chat} title={getString('overview')} count={0} />,
panel: (
<Container className={css.generalTab}>
<Layout.Vertical spacing="small" padding="xxlarge">

View File

@ -78,6 +78,7 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
)
const isClosed = pullRequestMetadata.state === PullRequestState.CLOSED
const isOpen = pullRequestMetadata.state === PullRequestState.OPEN
const isConflict = pullRequestMetadata.merge_check_status === MergeCheckStatus.CONFLICT
const unchecked = useMemo(
() => pullRequestMetadata.merge_check_status === MergeCheckStatus.UNCHECKED && !isClosed,
[pullRequestMetadata, isClosed]
@ -287,7 +288,7 @@ export const PullRequestActionsBox: React.FC<PullRequestActionsBoxProps> = ({
})}>
<SplitButton
text={mergeOption.title}
disabled={loading || unchecked}
disabled={loading || unchecked || (isConflict && mergeOption.method !== 'close')}
className={cx({
[css.secondaryButton]: mergeOption.method === 'close' || mergeable === false
})}

View File

@ -1,4 +1,4 @@
import React, { useMemo } from 'react'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useGet } from 'restful-react'
import {
ButtonSize,
@ -7,12 +7,15 @@ import {
Container,
FlexExpander,
Heading,
Icon,
Layout,
StringSubstitute,
Tabs,
Utils
} from '@harness/uicore'
import { Render } from 'react-jsx-match'
import { useHistory } from 'react-router-dom'
import { Document, Page, pdfjs } from 'react-pdf'
import { Render, Match, Truthy, Falsy, Case, Else } from 'react-jsx-match'
import { Link, useHistory } from 'react-router-dom'
import { SourceCodeViewer } from 'components/SourceCodeViewer/SourceCodeViewer'
import type { OpenapiContentInfo, RepoFileContent, TypesCommit } from 'services/code'
import {
@ -32,6 +35,8 @@ import { OptionsMenuButton } from 'components/OptionsMenuButton/OptionsMenuButto
import { PlainButton } from 'components/PlainButton/PlainButton'
import { CommitsView } from 'components/CommitsView/CommitsView'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { FileCategory, useFileContentViewerDecision } from 'utils/FileUtils'
import { useDownloadRawFile } from 'hooks/useDownloadRawFile'
import { usePageIndex } from 'hooks/usePageIndex'
import { Readme } from '../FolderContent/Readme'
import { GitBlame } from './GitBlame'
@ -47,6 +52,9 @@ export function FileContent({
}: Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath' | 'resourceContent' | 'commitRef'>) {
const { routes } = useAppContext()
const { getString } = useStrings()
const downloadFile = useDownloadRawFile()
const { category, isFileTooLarge, isViewable, filename, extension, size, base64Data, rawURL } =
useFileContentViewerDecision({ repoMetadata, gitRef, resourcePath, resourceContent })
const history = useHistory()
const [activeTab, setActiveTab] = React.useState<string>(FileSection.CONTENT)
@ -83,7 +91,7 @@ export function FileContent({
const { standalone } = useAppContext()
const { hooks } = useAppContext()
const space = useGetSpaceParam()
const isATextFile = category === FileCategory.TEXT
const permPushResult = hooks?.usePermissionTranslate?.(
{
resource: {
@ -106,6 +114,19 @@ export function FileContent({
}
return { disabled: isRefATag(gitRef) || false, tooltip: undefined }
}, [permPushResult, gitRef]) // eslint-disable-line react-hooks/exhaustive-deps
const [pdfWidth, setPdfWidth] = useState<number>(700)
const ref = useRef<HTMLDivElement>(null)
const [numPages, setNumPages] = useState<number>()
useEffect(() => {
if (ref.current) {
const width = Math.min(Math.max(ref.current.clientWidth - 100, 700), 1800)
if (pdfWidth !== width) {
setPdfWidth(width)
}
}
}, [pdfWidth, ref.current?.clientWidth])
const [page] = usePageIndex()
const { data: commits } = useGet<{ commits: TypesCommit[]; rename_details: RenameDetails[] }>({
@ -120,7 +141,7 @@ export function FileContent({
})
return (
<Container className={css.tabsContainer}>
<Container className={css.tabsContainer} ref={ref}>
<Tabs
id="fileTabs"
selectedTabId={activeTab}
@ -138,6 +159,7 @@ export function FileContent({
repoMetadata={repoMetadata}
latestCommit={resourceContent.latest_commit}
standaloneStyle
size={size}
/>
<Container className={css.container} background={Color.WHITE}>
<Layout.Horizontal padding="small" className={css.heading}>
@ -155,7 +177,7 @@ export function FileContent({
icon="code-edit"
tooltipProps={{ isDark: true }}
tooltip={permsFinal.tooltip}
disabled={permsFinal.disabled}
disabled={permsFinal.disabled || !isATextFile}
onClick={() => {
history.push(
routes.toCODEFileEdit({
@ -177,24 +199,14 @@ export function FileContent({
hasIcon: true,
iconName: 'arrow-right',
text: getString('viewRaw'),
onClick: () => {
window.open(
`/code/api/v1/repos/${
repoMetadata?.path
}/+/raw/${resourcePath}?${`git_ref=${gitRef}`}`,
'_blank'
)
}
onClick: () => window.open(rawURL, '_blank') // TODO: This is still not working due to token is not stored in cookies
},
'-',
{
hasIcon: true,
iconName: 'cloud-download',
text: getString('download'),
download: resourceContent?.name || 'download',
href: `/code/api/v1/repos/${
repoMetadata?.path
}/+/raw/${resourcePath}?${`git_ref=${gitRef}`}`
onClick: () => downloadFile({ repoMetadata, resourcePath, gitRef, filename })
},
{
hasIcon: true,
@ -219,18 +231,154 @@ export function FileContent({
<Render when={(resourceContent?.content as RepoFileContent)?.data}>
<Container className={css.content}>
<Render when={!markdownInfo}>
<SourceCodeViewer language={filenameToLanguage(resourceContent?.name)} source={content} />
</Render>
<Render when={markdownInfo}>
<Readme
metadata={repoMetadata}
readmeInfo={markdownInfo as OpenapiContentInfo}
contentOnly
maxWidth="calc(100vw - 346px)"
gitRef={gitRef}
/>
</Render>
<Match expr={isViewable}>
<Falsy>
<Center>
<Link
to={rawURL} // TODO: Link component generates wrong copy link
onClick={e => {
Utils.stopEvent(e)
downloadFile({ repoMetadata, resourcePath, gitRef, filename })
}}>
<Layout.Horizontal spacing="small">
<Icon name="cloud-download" size={16} />
<span>{getString('download')}</span>
</Layout.Horizontal>
</Link>
</Center>
</Falsy>
<Truthy>
<Match expr={isFileTooLarge}>
<Truthy>
<Center>
<Match expr={category}>
<Case val={FileCategory.PDF}>
<Document
file={rawURL}
options={{
// TODO: Configure this to use a local worker/webpack loader
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,
cMapPacked: true
}}
onLoadSuccess={({ numPages: nextNumPages }) => setNumPages(nextNumPages)}>
{Array.from(new Array(numPages), (_el, index) => (
<Page
loading=""
width={pdfWidth}
key={`page_${index + 1}`}
pageNumber={index + 1}
renderTextLayer={false}
renderAnnotationLayer={false}
/>
))}
</Document>
</Case>
<Case val={FileCategory.AUDIO}>
<audio controls>
<source src={rawURL} />
</audio>
</Case>
<Case val={FileCategory.VIDEO}>
<video controls height={500}>
<source src={rawURL} />
</video>
</Case>
<Else>
<StringSubstitute
str={getString('fileTooLarge')}
vars={{
download: (
<Link
to={rawURL} // TODO: Link component generates wrong copy link
onClick={e => {
Utils.stopEvent(e)
downloadFile({ repoMetadata, resourcePath, gitRef, filename })
}}>
<Layout.Horizontal spacing="small" padding={{ left: 'small' }}>
<Icon name="cloud-download" size={16} />
<span>{getString('clickHereToDownload')}</span>
</Layout.Horizontal>
</Link>
)
}}
/>
</Else>
</Match>
</Center>
</Truthy>
<Falsy>
<Match expr={markdownInfo}>
<Truthy>
<Readme
metadata={repoMetadata}
readmeInfo={markdownInfo as OpenapiContentInfo}
contentOnly
maxWidth="calc(100vw - 346px)"
gitRef={gitRef}
/>
</Truthy>
<Falsy>
<Center>
<Match expr={category}>
<Case val={FileCategory.SVG}>
<img
src={`data:image/svg+xml;base64,${base64Data}`}
alt={filename}
style={{ maxWidth: '100%', maxHeight: '100%' }}
/>
</Case>
<Case val={FileCategory.IMAGE}>
<img
src={`data:image/${extension};base64,${base64Data}`}
alt={filename}
style={{ maxWidth: '100%', maxHeight: '100%' }}
/>
</Case>
<Case val={FileCategory.PDF}>
<Document
file={`data:application/pdf;base64,${base64Data}`}
options={{
// TODO: Configure this to use a local worker/webpack loader
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjs.version}/cmaps/`,
cMapPacked: true
}}
onLoadSuccess={({ numPages: nextNumPages }) => setNumPages(nextNumPages)}>
{Array.from(new Array(numPages), (_el, index) => (
<Page
loading=""
width={pdfWidth}
key={`page_${index + 1}`}
pageNumber={index + 1}
renderTextLayer={false}
renderAnnotationLayer={false}
/>
))}
</Document>
</Case>
<Case val={FileCategory.AUDIO}>
<audio controls>
<source src={`data:audio/${extension};base64,${base64Data}`} />
</audio>
</Case>
<Case val={FileCategory.VIDEO}>
<video controls height={500}>
<source src={`data:video/${extension};base64,${base64Data}`} />
</video>
</Case>
<Case val={FileCategory.TEXT}>
<SourceCodeViewer
language={filenameToLanguage(filename)}
source={decodeGitContent(base64Data)}
/>
</Case>
</Match>
</Center>
</Falsy>
</Match>
</Falsy>
</Match>
</Truthy>
</Match>
</Container>
</Render>
</Container>
@ -286,3 +434,9 @@ export function FileContent({
</Container>
)
}
const Center: React.FC = ({ children }) => (
<Container flex={{ align: 'center-center' }} style={{ width: '100%', height: '100%' }} padding={{ right: 'large' }}>
{children}
</Container>
)

460
web/src/utils/FileUtils.ts Normal file
View File

@ -0,0 +1,460 @@
import { useEffect, useMemo } from 'react'
import { pdfjs } from 'react-pdf'
import { useAppContext } from 'AppContext'
import type { RepoFileContent } from 'services/code'
import type { GitInfoProps } from './GitUtils'
// TODO: Configure this to use a local worker/webpack loader
// Maybe use pdfjs directly: https://github.com/mozilla/pdf.js/blob/master/examples/webpack/webpack.config.js
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`
type UseFileViewerDecisionProps = Pick<GitInfoProps, 'repoMetadata' | 'gitRef' | 'resourcePath' | 'resourceContent'>
interface UseFileViewerDecisionResult {
category: FileCategory
isFileTooLarge: boolean
isViewable: string | boolean
filename: string
extension: string
size: number
base64Data: string
rawURL: string
}
export function useFileContentViewerDecision({
repoMetadata,
gitRef,
resourcePath,
resourceContent
}: UseFileViewerDecisionProps): UseFileViewerDecisionResult {
const { routingId } = useAppContext()
const metadata = useMemo(() => {
const filename = resourceContent.name as string
const extension = filename?.split('.').pop() || ''
const isMarkdown = extension.toLowerCase() === 'md'
const isPdf = extension.toLowerCase() === 'pdf'
const isSVG = extension.toLowerCase() === 'svg'
const isImage = ImageExtensions.includes(extension.toLowerCase())
const isAudio = AudioExtensions.includes(extension.toLowerCase())
const isVideo = VideoExtensions.includes(extension.toLowerCase())
const isText =
SpecialTextFiles.find(name => name.toLowerCase() === filename?.toLowerCase()) ||
TextExtensions.includes(extension.toLowerCase())
const category = isMarkdown
? FileCategory.MARKDOWN
: isSVG
? FileCategory.SVG
: isPdf
? FileCategory.PDF
: isImage
? FileCategory.IMAGE
: isAudio
? FileCategory.AUDIO
: isVideo
? FileCategory.VIDEO
: isText
? FileCategory.TEXT
: FileCategory.OTHER
const isViewable = isPdf || isSVG || isImage || isAudio || isVideo || isText
const resourceData = resourceContent?.content as RepoFileContent
const isFileTooLarge = resourceData?.size !== resourceData?.data_size
const rawURL = `/code/api/v1/repos/${repoMetadata?.path}/+/raw/${resourcePath}?routingId=${routingId}&git_ref=${gitRef}`
// TODO: TEST ONLY
// const rawURL = `https://localhost:8181/rawfile/${filename}`
return {
category,
isFileTooLarge,
isViewable,
filename,
extension,
size: resourceData?.size || 0,
// base64 data returned from content API. This snapshot can be truncated by backend
base64Data: resourceData?.data || '',
rawURL
}
}, []) // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
switch (metadata.category) {
case FileCategory.SVG:
case FileCategory.PDF:
case FileCategory.IMAGE:
case FileCategory.AUDIO:
case FileCategory.VIDEO:
case FileCategory.TEXT:
break
default:
break
}
}, []) // eslint-disable-line react-hooks/exhaustive-deps
return metadata
}
export const MAX_VIEWABLE_FILE_SIZE = 100 * 1024 * 1024 // 100 MB
export enum FileCategory {
MARKDOWN = 'MARKDOWN',
SVG = 'SVG',
PDF = 'PDF',
IMAGE = 'IMAGE',
AUDIO = 'AUDIO',
VIDEO = 'VIDEO',
TEXT = 'TEXT',
OTHER = 'OTHER'
}
// Parts are copied from https://github.com/sindresorhus/text-extensions
// MIT License
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
const TextExtensions = [
'ada',
'adb',
'ads',
'applescript',
'as',
'asc',
'ascii',
'ascx',
'asm',
'asmx',
'asp',
'aspx',
'atom',
'au3',
'awk',
'bas',
'bash',
'bashrc',
'bat',
'bbcolors',
'bcp',
'bdsgroup',
'bdsproj',
'bib',
'bowerrc',
'c',
'cbl',
'cc',
'cfc',
'cfg',
'cfm',
'cfml',
'cgi',
'cjs',
'clj',
'cljs',
'cls',
'cmake',
'cmd',
'cnf',
'cob',
'code-snippets',
'coffee',
'coffeekup',
'conf',
'cp',
'cpp',
'cpt',
'cpy',
'crt',
'cs',
'csh',
'cson',
'csproj',
'csr',
'css',
'csslintrc',
'csv',
'ctl',
'curlrc',
'cxx',
'd',
'dart',
'dfm',
'diff',
'dof',
'dpk',
'dpr',
'dproj',
'dtd',
'eco',
'editorconfig',
'ejs',
'el',
'elm',
'emacs',
'eml',
'ent',
'erb',
'erl',
'eslintignore',
'eslintrc',
'ex',
'exs',
'f',
'f03',
'f77',
'f90',
'f95',
'fish',
'for',
'fpp',
'frm',
'fs',
'fsproj',
'fsx',
'ftn',
'gemrc',
'gemspec',
'gitattributes',
'gitconfig',
'gitignore',
'gitkeep',
'gitmodules',
'go',
'gpp',
'gradle',
'graphql',
'groovy',
'groupproj',
'grunit',
'gtmpl',
'gvimrc',
'h',
'haml',
'hbs',
'hgignore',
'hh',
'hpp',
'hrl',
'hs',
'hta',
'htaccess',
'htc',
'htm',
'html',
'htpasswd',
'hxx',
'iced',
'iml',
'inc',
'inf',
'info',
'ini',
'ino',
'int',
'irbrc',
'itcl',
'itermcolors',
'itk',
'jade',
'java',
'jhtm',
'jhtml',
'js',
'jscsrc',
'jshintignore',
'jshintrc',
'json',
'json5',
'jsonld',
'jsp',
'jspx',
'jsx',
'ksh',
'less',
'lhs',
'lisp',
'log',
'ls',
'lsp',
'lua',
'm',
'm4',
'mak',
'map',
'markdown',
'master',
'md',
'mdown',
'mdwn',
'mdx',
'metadata',
'mht',
'mhtml',
'mjs',
'mk',
'mkd',
'mkdn',
'mkdown',
'ml',
'mli',
'mm',
'mxml',
'nfm',
'nfo',
'noon',
'npmignore',
'npmrc',
'nuspec',
'nvmrc',
'ops',
'pas',
'pasm',
'patch',
'pbxproj',
'pch',
'pem',
'pg',
'php',
'php3',
'php4',
'php5',
'phpt',
'phtml',
'pir',
'pl',
'pm',
'pmc',
'pod',
'pot',
'prettierrc',
'properties',
'props',
'pt',
'pug',
'purs',
'py',
'pyx',
'r',
'rake',
'rb',
'rbw',
'rc',
'rdoc',
'rdoc_options',
'resx',
'rexx',
'rhtml',
'rjs',
'rlib',
'ron',
'rs',
'rss',
'rst',
'rtf',
'rvmrc',
'rxml',
's',
'sass',
'scala',
'scm',
'scss',
'seestyle',
'sh',
'shtml',
'sln',
'sls',
'spec',
'sql',
'sqlite',
'sqlproj',
'srt',
'ss',
'sss',
'st',
'strings',
'sty',
'styl',
'stylus',
'sub',
'sublime-build',
'sublime-commands',
'sublime-completions',
'sublime-keymap',
'sublime-macro',
'sublime-menu',
'sublime-project',
'sublime-settings',
'sublime-workspace',
'sv',
'svc',
'svg',
'swift',
't',
'tcl',
'tcsh',
'terminal',
'tex',
'text',
'textile',
'tg',
'tk',
'tmLanguage',
'tmpl',
'tmTheme',
'tpl',
'ts',
'tsv',
'tsx',
'tt',
'tt2',
'ttml',
'twig',
'txt',
'v',
'vb',
'vbproj',
'vbs',
'vcproj',
'vcxproj',
'vh',
'vhd',
'vhdl',
'vim',
'viminfo',
'vimrc',
'vm',
'vue',
'webapp',
'webmanifest',
'wsc',
'x-php',
'xaml',
'xht',
'xhtml',
'xml',
'xs',
'xsd',
'xsl',
'xslt',
'y',
'yaml',
'yml',
'zsh',
'zshrc',
'ics'
]
const SpecialTextFiles = [
'Dockerfile',
'.gitignore',
'yarn.lock',
'README',
'LICENSE',
'CHANGELOG',
'Makefile',
'Procfile'
]
const ImageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'ico', 'bmp']
const VideoExtensions = ['ogg', 'mp4', 'webm', 'mpeg']
const AudioExtensions = ['mp3', 'wav']

View File

@ -149,4 +149,17 @@ export const diffRefsToRefs = (diffRefs: string) => {
}
}
export const decodeGitContent = (content = '') => decodeURIComponent(escape(window.atob(content)))
export const decodeGitContent = (content = '') => {
try {
// Decode base64 content for text file
return decodeURIComponent(escape(window.atob(content)))
} catch (_exception) {
try {
// Return original base64 content for binary file
return content
} catch (exception) {
console.error(exception) // eslint-disable-line no-console
}
}
return ''
}

View File

@ -40,8 +40,8 @@ export function generateAlphaNumericHash(length: number) {
export const dayAgoInMS = 86400000
export const getErrorMessage = (error: Unknown): string =>
get(error, 'data.error', get(error, 'data.message', get(error, 'message', error)))
export const getErrorMessage = (error: Unknown): string | undefined =>
error ? get(error, 'data.error', get(error, 'data.message', get(error, 'message', error))) : undefined
export interface PageBrowserProps {
page: string
@ -254,5 +254,25 @@ export const voidFn = (f: Function) => () => {
export enum MergeCheckStatus {
MERGEABLE = 'mergeable',
UNCHECKED = 'unchecked'
UNCHECKED = 'unchecked',
CONFLICT = 'conflict'
}
/**
* Convert number of bytes into human readable format
*
* @param integer bytes Number of bytes to convert
* @param integer precision Number of digits after the decimal separator
* @return string
* @link https://stackoverflow.com/a/18650828/1114931
*/
export function formatBytes(bytes: number, decimals = 2) {
if (!+bytes) return '0 Bytes'
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
}

View File

@ -1144,6 +1144,21 @@
"@lezer/highlight" "^1.0.0"
"@lezer/lr" "^1.0.0"
"@mapbox/node-pre-gyp@^1.0.0":
version "1.0.10"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c"
integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==
dependencies:
detect-libc "^2.0.0"
https-proxy-agent "^5.0.0"
make-dir "^3.1.0"
node-fetch "^2.6.7"
nopt "^5.0.0"
npmlog "^5.0.1"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.11"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@ -2761,6 +2776,11 @@ append-transform@^2.0.0:
dependencies:
default-require-extensions "^3.0.0"
"aproba@^1.0.3 || ^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@ -2771,6 +2791,14 @@ archy@^1.0.0:
resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40"
integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==
are-we-there-yet@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
dependencies:
delegates "^1.0.0"
readable-stream "^3.6.0"
arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
@ -3533,6 +3561,15 @@ canonicalize@^1.0.1:
resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.8.tgz#24d1f1a00ed202faafd9bf8e63352cd4450c6df1"
integrity sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==
canvas@^2.11.2:
version "2.11.2"
resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860"
integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.0"
nan "^2.17.0"
simple-get "^3.0.3"
capture-exit@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
@ -3654,6 +3691,11 @@ chownr@^1.1.1:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
chownr@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
chrome-launcher@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.13.4.tgz#4c7d81333c98282899c4e38256da23e00ed32f73"
@ -3832,7 +3874,7 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
clsx@^1.1.1:
clsx@^1.1.1, clsx@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
@ -3889,6 +3931,11 @@ color-name@^1.1.4, color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-support@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
colorette@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
@ -4055,6 +4102,11 @@ console-browserify@^1.1.0:
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
console-control-strings@^1.0.0, console-control-strings@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@ -4459,6 +4511,13 @@ decompress-response@^3.3.0:
dependencies:
mimic-response "^1.0.0"
decompress-response@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
dependencies:
mimic-response "^2.0.0"
deep-equal@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@ -4580,6 +4639,11 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@ -4618,6 +4682,11 @@ details-element-polyfill@^2.4.0:
resolved "https://registry.yarnpkg.com/details-element-polyfill/-/details-element-polyfill-2.4.0.tgz#e0622adef7902662faf27b4ab8acba5dc4e3a6e6"
integrity sha512-jnZ/m0+b1gz3EcooitqL7oDEkKHEro659dt8bWB/T/HjiILucoQhHvvi5MEOAIFJXxxO+rIYJ/t3qCgfUOSU5g==
detect-libc@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
detect-newline@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
@ -5942,6 +6011,13 @@ fs-extra@^9.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
dependencies:
minipass "^3.0.0"
fs-monkey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
@ -6000,6 +6076,21 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
gauge@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
dependencies:
aproba "^1.0.3 || ^2.0.0"
color-support "^1.1.2"
console-control-strings "^1.0.0"
has-unicode "^2.0.1"
object-assign "^4.1.1"
signal-exit "^3.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
wide-align "^1.1.2"
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@ -6335,6 +6426,11 @@ has-tostringtag@^1.0.0:
dependencies:
has-symbols "^1.0.2"
has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
has-value@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@ -8642,6 +8738,11 @@ lz-string@^1.4.4, lz-string@^1.5.0:
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
make-cancellable-promise@^1.0.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/make-cancellable-promise/-/make-cancellable-promise-1.3.1.tgz#3bd89704c75afe6251cdd6a82baca1fcfbd2c792"
integrity sha512-DWOzWdO3xhY5ESjVR+wVFy03rpt0ZccS4bunccNwngoX6rllKlMZm6S9ZnJ5nMuDDweqDMjtaO0g6tZeh+cCUA==
make-dir@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
@ -8657,7 +8758,7 @@ make-dir@^2.0.0:
pify "^4.0.1"
semver "^5.6.0"
make-dir@^3.0.0, make-dir@^3.0.2:
make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@ -8669,6 +8770,11 @@ make-error@1.x, make-error@^1.1.1:
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
make-event-props@^1.5.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/make-event-props/-/make-event-props-1.6.1.tgz#1d587017c3f1f3b42719b775af93d5253656ccdd"
integrity sha512-JhvWq/iz1BvlmnPvLJjXv+xnMPJZuychrDC68V+yCGQJn5chcA8rLGKo5EP1XwIKVrigSXKLmbeXAGkf36wdCQ==
makeerror@1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
@ -8955,6 +9061,13 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
merge-refs@^1.1.3:
version "1.2.1"
resolved "https://registry.yarnpkg.com/merge-refs/-/merge-refs-1.2.1.tgz#abddc800375395a4a4eb5c45ebf2a52557fdbe34"
integrity sha512-pRPz39HQz2xzHdXAGvtJ9S8aEpNgpUjzb5yPC3ytozodmsHg+9nqgRs7/YOmn9fM/TLzntAC8AdGTidKxOq9TQ==
dependencies:
"@types/react" "*"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@ -9312,6 +9425,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
mimic-response@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
@ -9355,6 +9473,26 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
minipass@^3.0.0:
version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
dependencies:
minipass "^3.0.0"
yallist "^4.0.0"
mississippi@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
@ -9384,7 +9522,7 @@ mkdirp@0.3.0:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==
mkdirp@1.x, mkdirp@^1.0.4:
mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
@ -9510,7 +9648,7 @@ mute-stream@0.0.8:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
nan@^2.12.1:
nan@^2.12.1, nan@^2.17.0:
version "2.17.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
@ -9582,6 +9720,13 @@ node-fetch-h2@^2.3.0:
dependencies:
http2-client "^1.2.5"
node-fetch@^2.6.7:
version "2.6.11"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25"
integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==
dependencies:
whatwg-url "^5.0.0"
node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@ -9672,6 +9817,13 @@ nopt@1.0.10:
dependencies:
abbrev "1"
nopt@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
dependencies:
abbrev "1"
normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@ -9738,6 +9890,16 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
npmlog@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
dependencies:
are-we-there-yet "^2.0.0"
console-control-strings "^1.1.0"
gauge "^3.0.0"
set-blocking "^2.0.0"
nth-check@^2.0.0, nth-check@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
@ -10372,6 +10534,11 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
path2d-polyfill@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz#24c554a738f42700d6961992bf5f1049672f2391"
integrity sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==
pbkdf2@^3.0.3:
version "3.1.2"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
@ -10383,6 +10550,16 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
pdfjs-dist@3.6.172:
version "3.6.172"
resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-3.6.172.tgz#f9efdfc5e850e1fecfc70b7f6f45c5dc990d8096"
integrity sha512-bfOhCg+S9DXh/ImWhWYTOiq3aVMFSCvzGiBzsIJtdMC71kVWDBw7UXr32xh0y56qc5wMVylIeqV3hBaRsu+e+w==
dependencies:
path2d-polyfill "^2.0.1"
web-streams-polyfill "^3.2.1"
optionalDependencies:
canvas "^2.11.2"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@ -10974,6 +11151,20 @@ react-monaco-editor@*, react-monaco-editor@^0.52.0:
dependencies:
prop-types "^15.8.1"
react-pdf@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/react-pdf/-/react-pdf-7.1.2.tgz#c6979cff9ac09c3e5ab7ea9e0182f79a499768e5"
integrity sha512-hmTUKh3WVYDJlP8XvebGN8HH0Gk/tXh9WgNAtvdHn79FHL78UEPSbVj3veHHGqmMa2hz1wJCItLUqGVP68Qsjw==
dependencies:
clsx "^1.2.1"
make-cancellable-promise "^1.0.0"
make-event-props "^1.5.0"
merge-refs "^1.1.3"
pdfjs-dist "3.6.172"
prop-types "^15.6.2"
tiny-invariant "^1.0.0"
tiny-warning "^1.0.0"
react-popper@^1.3.7:
version "1.3.11"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.11.tgz#a2cc3f0a67b75b66cfa62d2c409f9dd1fcc71ffd"
@ -12039,6 +12230,20 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
simple-get@^3.0.3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
dependencies:
decompress-response "^4.2.0"
once "^1.3.1"
simple-concat "^1.0.0"
sirv@^1.0.7:
version "1.0.19"
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
@ -12386,15 +12591,7 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
string-width@^2.0.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -12403,6 +12600,14 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^2.0.0, string-width@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string.prototype.matchall@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3"
@ -12655,6 +12860,18 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
tar@^6.1.11:
version "6.1.15"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69"
integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
minipass "^5.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
term-size@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
@ -12774,7 +12991,7 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
tiny-invariant@^1.0.2:
tiny-invariant@^1.0.0, tiny-invariant@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
@ -12888,6 +13105,11 @@ tr46@^2.1.0:
dependencies:
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
trim-lines@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338"
@ -13617,6 +13839,16 @@ web-namespaces@^2.0.0:
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692"
integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==
web-streams-polyfill@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
webidl-conversions@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
@ -13838,6 +14070,14 @@ whatwg-mimetype@^2.3.0:
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
whatwg-url@^8.0.0, whatwg-url@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
@ -13899,6 +14139,13 @@ which@^2.0.1, which@^2.0.2:
dependencies:
isexe "^2.0.0"
wide-align@^1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
widest-line@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc"