Merge branch 'develop' into feature/safari

This commit is contained in:
EnixCoda 2021-06-13 12:50:11 +08:00
commit 6706ddd4dc
83 changed files with 3855 additions and 3518 deletions

12
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: enixcoda
patreon: enixcoda
open_collective: enixcoda
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View file

@ -4,6 +4,6 @@ describe(`in GitHub homepage`, () => {
beforeAll(() => page.goto('https://github.com'))
it('should not render Gitako', async () => {
await expectToNotFind('.gitako-side-bar .gitako-position-wrapper')
await expectToNotFind('.gitako-side-bar .gitako-side-bar-body-wrapper')
})
})

View file

@ -4,7 +4,7 @@ describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/pull/71'))
it('should render Gitako', async () => {
await expectToFind('.gitako-side-bar .gitako-position-wrapper')
await expectToFind('.gitako-side-bar .gitako-side-bar-body-wrapper')
})
it('should render file list', async () => {

View file

@ -4,8 +4,6 @@ describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/commits/develop'))
it('should not break go back in history', async () => {
// temporarily skip this test as GitHub has unexpected PJAX behaviors
return
for (let i = 0; i < 3; i++) {
const commitLinks = await page.$$(
`#js-repo-pjax-container .TimelineItem-body ol li > div:nth-child(1) a[href*="/commit/"]`,

View file

@ -1,18 +1,27 @@
import { patientClick, selectFileTreeItem, sleep, waitForLegacyPJAXRedirect } from '../utils'
import {
collapseFloatModeSidebar,
expandFloatModeSidebar,
patientClick,
selectFileTreeItem,
sleep,
waitForLegacyPJAXRedirect,
} from '../utils'
describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/src'))
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/tree/develop/src'))
it('should work with PJAX', async () => {
await sleep(3000)
await patientClick(page, selectFileTreeItem('src/analytics.ts'))
await expandFloatModeSidebar()
await patientClick(selectFileTreeItem('src/analytics.ts'))
await waitForLegacyPJAXRedirect()
await collapseFloatModeSidebar()
await page.click('a[data-selected-links^="repo_issues "]')
await waitForLegacyPJAXRedirect()
await page.click('a[data-tab-item="issues-tab"]')
await waitForLegacyPJAXRedirect()
await page.click('a[data-tab-item="pull-requests-tab"]')
await page.click('a[data-selected-links^="repo_pulls "]')
await waitForLegacyPJAXRedirect()
page.goBack()

View file

@ -1,4 +1,5 @@
import {
expandFloatModeSidebar,
expectToFind,
expectToNotFind,
patientClick,
@ -12,7 +13,9 @@ describe(`in Gitako project page`, () => {
it('should work with PJAX', async () => {
await sleep(3000)
await patientClick(page, selectFileTreeItem('.babelrc'))
await expandFloatModeSidebar()
await patientClick(selectFileTreeItem('.babelrc'))
await waitForPJAXAPIRedirect()
// The selector for file content

View file

@ -1,10 +1,16 @@
import { expectToFind, expectToNotFind, scroll, selectFileTreeItem } from '../utils'
import {
expandFloatModeSidebar,
expectToFind,
expectToNotFind,
scroll,
selectFileTreeItem,
} from '../utils'
describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako'))
it('should render Gitako', async () => {
await expectToFind('.gitako-side-bar .gitako-position-wrapper')
await expectToFind('.gitako-side-bar .gitako-side-bar-body-wrapper')
})
it('should render file list', async () => {
@ -12,6 +18,8 @@ describe(`in Gitako project page`, () => {
})
it('should render while scroll', async () => {
await expandFloatModeSidebar()
const filesEle = await page.waitForSelector('.gitako-side-bar .files')
// node of tsconfig.json should NOT be rendered before scroll down
await expectToNotFind(selectFileTreeItem('tsconfig.json'))

View file

@ -1,5 +1,3 @@
import { Page } from 'puppeteer'
export async function expectToFind(selector: string) {
await expect(page.waitForSelector(selector)).resolves.not.toBeNull()
}
@ -82,7 +80,26 @@ export function selectFileTreeItem(path: string): string {
return `.gitako-side-bar .files a[title="${path}"]`
}
export async function patientClick(page: Page, selector: string) {
export async function patientClick(selector: string) {
await page.waitForSelector(selector)
await page.click(selector)
}
export async function expandFloatModeSidebar() {
const rect = await (await page.$('.gitako-toggle-show-button'))?.evaluate(button => {
const { x, y, width, height } = button.getBoundingClientRect()
// pass required properties to avoid serialization issues
return { x, y, width, height }
})
if (rect) {
await page.mouse.move(rect.x + rect.width / 2, rect.y + rect.height / 2)
await sleep(500)
}
}
export async function collapseFloatModeSidebar() {
await page.mouse.move(600, 600, {
steps: 100,
})
await sleep(500)
}

View file

@ -61,7 +61,7 @@ module.exports = {
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
maxWorkers: 1, // more than 1 workers may make tabs in background and never renders Gitako's file items.
maxWorkers: 8,
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [

View file

@ -0,0 +1,7 @@
const baseConfig = require('./jest.config')
module.exports = {
...baseConfig,
maxWorkers: 1,
testMatch: ['**/__tests__/non-parallel-cases/*.ts?(x)'],
}

View file

@ -1,6 +1,6 @@
{
"name": "gitako",
"version": "2.7.1",
"version": "3.1.0",
"description": "File tree for GitHub, and more than that.",
"repository": "https://github.com/EnixCoda/Gitako",
"author": "EnixCoda",
@ -14,13 +14,15 @@
"postinstall": "rm -rf node_modules/@types/react-native && node scripts/fix-pjax-api",
"build": "VERSION=v$(node scripts/get-version.js) NODE_ENV=production webpack",
"roll": "make release",
"test": "NODE_ENV=test jest"
"test": "yarn run test:parallel && yarn run test:non-parallel",
"test:parallel": "NODE_ENV=test jest",
"test:non-parallel": "NODE_ENV=test jest --config jest.non.parallel.config.js"
},
"dependencies": {
"@primer/components": "^22.0.2",
"@primer/css": "^15.2.0",
"@primer/octicons-react": "^10.0.0",
"@sentry/browser": "^5.12.1",
"@sentry/browser": "^6.3.6",
"@types/history": "^4.7.5",
"@types/ini": "^1.3.30",
"@types/js-base64": "^2.3.1",
@ -35,8 +37,8 @@
"nprogress": "^0.2.0",
"pjax-api": "^3.33.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-use": "^13.8.0",
"react-dom": "^17.0.2",
"react-use": "^15.3.0",
"react-window": "^1.8.5",
"styled-components": "^5.2.0",
"webext-domain-permission-toggle": "^1.0.0",
@ -51,7 +53,7 @@
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@sentry/cli": "^1.51.0",
"@sentry/cli": "^1.64.2",
"@types/firefox-webext-browser": "^70.0.1",
"@types/jest": "^24.0.25",
"@types/node": "^11.10.4",
@ -71,7 +73,7 @@
"raw-loader": "^4.0.0",
"sass": "^1.26.2",
"sass-loader": "^8.0.2",
"typescript": "^4.0.3",
"typescript": "^4.2.4",
"uglifyjs-webpack-plugin": "^2.1.2",
"url-loader": "^1.1.2",
"web-ext": "^4.2.0",

View file

@ -52,6 +52,7 @@ const sentryOptions: Sentry.BrowserOptions = {
}
return breadcrumb
},
autoSessionTracking: false, // this avoids the request when calling `init`
}
Sentry.init(sentryOptions)

View file

@ -1,12 +1,16 @@
import { useConfigs } from 'containers/ConfigsContext'
import { GITHUB_OAUTH } from 'env'
import { platform } from 'platforms'
import { GitHub } from 'platforms/GitHub'
import * as React from 'react'
export function AccessDeniedDescription({ hasToken }: { hasToken: boolean }) {
export function AccessDeniedDescription() {
const configContext = useConfigs()
const hasToken = Boolean(configContext.value.accessToken)
return (
<div className={'description'}>
<h5>Access Denied</h5>
<div className={'description-area'}>
<h2>Access Denied</h2>
{hasToken ? (
<>
<p>

View file

@ -10,13 +10,13 @@ const className = 'clippy-wrapper'
export const ClippyClassName = className
export function Clippy({ codeSnippetElement }: Props) {
const [status, setStatus] = React.useState<'normal' | 'success' | 'fail'>('normal')
const [state, setState] = React.useState<'normal' | 'success' | 'fail'>('normal')
React.useEffect(() => {
const timer = window.setTimeout(() => {
setStatus('normal')
setState('normal')
}, 1000)
return () => window.clearTimeout(timer)
}, [status])
}, [state])
// Temporary fix:
// React moved root node of event delegation since v17
@ -26,7 +26,7 @@ export function Clippy({ codeSnippetElement }: Props) {
const element = elementRef.current
if (element) {
function onClippyClick() {
setStatus(copyElementContent(codeSnippetElement) ? 'success' : 'fail')
setState(copyElementContent(codeSnippetElement) ? 'success' : 'fail')
}
element.addEventListener('click', onClippyClick)
return () => element.removeEventListener('click', onClippyClick)
@ -36,7 +36,7 @@ export function Clippy({ codeSnippetElement }: Props) {
return (
<div className={className}>
<button className="clippy" ref={elementRef}>
<i className={cx('icon', status)} />
<i className={cx('icon', state)} />
</button>
</div>
)

View file

@ -1,4 +1,4 @@
import { Text } from '@primer/components'
import { Label, Text } from '@primer/components'
import { LoadingIndicator } from 'components/LoadingIndicator'
import { Node } from 'components/Node'
import { SearchBar } from 'components/SearchBar'
@ -11,9 +11,12 @@ import * as React from 'react'
import { FixedSizeList, ListChildComponentProps } from 'react-window'
import { cx } from 'utils/cx'
import { focusFileExplorer } from 'utils/DOMHelper'
import { run } from 'utils/general'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useOnLocationChange } from 'utils/hooks/useOnLocationChange'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import { VisibleNodes } from 'utils/VisibleNodesGenerator'
import { SideBarStateContext } from '../containers/SideBarState'
import { Icon } from './Icon'
import { SearchMode, searchModes } from './searchModes'
import { SizeObserver } from './SizeObserver'
@ -27,7 +30,6 @@ type renderNodeContext = {
const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplorer(props) {
const {
state,
visibleNodes,
visibleNodesGenerator,
freeze,
@ -37,16 +39,14 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
onFocusSearchBar,
goTo,
handleKeyDown,
toggleShowSettings,
metaData,
expandTo,
setUpTree,
treeRoot,
defer,
searched,
} = props
const {
value: { accessToken, compressSingletonFolder, searchMode },
value: { accessToken, compressSingletonFolder, searchMode, commentToggle },
} = useConfigs()
const onSearch = React.useCallback(
@ -59,18 +59,19 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
[updateSearchKey, visibleNodesGenerator],
)
const stateContext = useLoadedContext(SideBarStateContext)
const state = stateContext.value
React.useEffect(() => {
if (treeRoot) {
setUpTree({
treeRoot,
metaData,
config: {
compressSingletonFolder,
accessToken,
},
})
}
}, [setUpTree, treeRoot, metaData, compressSingletonFolder, accessToken])
setUpTree({
metaData,
config: {
compressSingletonFolder,
accessToken,
},
stateContext,
})
}, [setUpTree, metaData, compressSingletonFolder, accessToken])
React.useEffect(() => {
if (visibleNodes?.focusedNode) focusFileExplorer()
@ -104,18 +105,26 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
<Icon type="search" />
</button>
) : undefined
const renderFileCommentAmounts = (node: TreeNode): React.ReactNode =>
node.comments !== undefined &&
node.comments > 0 && (
<span className={'node-item-comment'}>
<Icon type={'comment'} /> {node.comments > 9 ? '9+' : node.comments}
</span>
)
const renders: ((node: TreeNode) => React.ReactNode)[] = []
if (commentToggle) renders.push(renderFileCommentAmounts)
if (searchMode === 'fuzzy') renders.push(renderFindInFolderButton)
if (searched) renders.push(renderGoToButton)
return renders.length
? node => renders.map((render, i) => <React.Fragment key={i}>{render(node)}</React.Fragment>)
: undefined
}, [goTo, onSearch, searched, searchMode])
}, [goTo, onSearch, searched, searchMode, commentToggle])
const renderLabelText = React.useCallback(
node => searchModes[searchMode].renderNodeLabelText(node, searchKey),
(node: TreeNode) => searchModes[searchMode].renderNodeLabelText(node, searchKey),
[searchKey, searchMode],
)
@ -131,59 +140,63 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
)
return (
<div
className={cx(`file-explorer`, { freeze })}
tabIndex={-1}
onKeyDown={handleKeyDown}
onClick={freeze ? toggleShowSettings : undefined}
>
{state !== 'done' ? (
<LoadingIndicator
text={
{
pulling: 'Fetching File List...',
rendering: 'Rendering File List...',
}[state]
}
/>
) : (
visibleNodes &&
renderNodeContext && (
<>
<SearchBar value={searchKey} onSearch={onSearch} onFocus={onFocusSearchBar} />
{searched && visibleNodes.nodes.length === 0 && (
<>
<Text marginTop={6} textAlign="center" color="text.gray">
No results found.
</Text>
{defer && (
<Text textAlign="center" color="gray.4" fontSize="12px">
Lazy mode is ON. Search results are limited to loaded folders.
</Text>
)}
</>
)}
<SizeObserver className={'files'}>
{({ width = 0, height = 0 }) => (
<ListView
height={height}
width={width}
renderNodeContext={renderNodeContext}
expandTo={expandTo}
metaData={metaData}
/>
)}
</SizeObserver>
</>
)
)}
<div className={cx(`file-explorer`, { freeze })} tabIndex={-1} onKeyDown={handleKeyDown}>
{run(() => {
switch (state) {
case 'tree-loading':
return <LoadingIndicator text={'Fetching File List...'} />
case 'tree-rendering':
return <LoadingIndicator text={'Rendering File List...'} />
case 'tree-rendered':
return (
visibleNodes &&
renderNodeContext && (
<>
<SearchBar value={searchKey} onSearch={onSearch} onFocus={onFocusSearchBar} />
{searched && visibleNodes.nodes.length === 0 && (
<>
<Text marginTop={6} textAlign="center" color="text.gray">
No results found.
</Text>
{defer && (
<Text textAlign="center" color="gray.4" fontSize="12px">
Lazy mode is ON. Search results are limited to loaded folders.
</Text>
)}
</>
)}
<SizeObserver className={'files'}>
{({ width = 0, height = 0 }) => (
<ListView
height={height}
width={width}
renderNodeContext={renderNodeContext}
expandTo={expandTo}
metaData={metaData}
/>
)}
</SizeObserver>
{defer && (
<Label
title="File tree data is loaded on demand. And search results are limited."
bg="yellow.5"
color="gray.6"
>
Lazy Mode
</Label>
)}
</>
)
)
}
})}
</div>
)
}
RawFileExplorer.defaultProps = {
freeze: false,
state: 'pulling',
searchKey: '',
visibleNodes: null,
}
@ -248,13 +261,15 @@ function ListView({ width, height, metaData, expandTo, renderNodeContext }: List
useOnLocationChange(goToCurrentItem)
useOnPJAXDone(goToCurrentItem)
const { compactFileTree } = useConfigs().value
return (
<FixedSizeList
ref={listRef}
itemKey={(index, { visibleNodes }) => visibleNodes?.nodes[index]?.path}
itemData={renderNodeContext}
itemCount={visibleNodes.nodes.length}
itemSize={37}
itemSize={compactFileTree ? 24 : 37}
height={height}
width={width}
>

View file

@ -1,15 +1,25 @@
import { SideBar } from 'components/SideBar'
import { ConfigsContext, ConfigsContextWrapper } from 'containers/ConfigsContext'
import { ConfigsContextWrapper } from 'containers/ConfigsContext'
import * as React from 'react'
import { ErrorBoundary } from './ErrorBoundary'
import { ErrorBoundary } from '../containers/ErrorBoundary'
import { StateBarErrorContextWrapper } from '../containers/ErrorContext'
import { OAuthWrapper } from '../containers/OAuthWrapper'
import { RepoContextWrapper } from '../containers/RepoContext'
import { StateBarStateContextWrapper } from '../containers/SideBarState'
export function Gitako() {
return (
<ErrorBoundary>
<ConfigsContextWrapper>
<ConfigsContext.Consumer>
{configContext => configContext && <SideBar configContext={configContext} />}
</ConfigsContext.Consumer>
<StateBarStateContextWrapper>
<StateBarErrorContextWrapper>
<OAuthWrapper>
<RepoContextWrapper>
<SideBar />
</RepoContextWrapper>
</OAuthWrapper>
</StateBarErrorContextWrapper>
</StateBarStateContextWrapper>
</ConfigsContextWrapper>
</ErrorBoundary>
)

8
src/components/IIFC.tsx Normal file
View file

@ -0,0 +1,8 @@
import * as React from 'react'
// I tried to install `react-iifc` but that causes TS build errors for unknown reason
// So here the duplicated code is
export function IIFC({ children }: { children(): React.ReactNode }) {
return <>{children()}</>
}

View file

@ -2,6 +2,7 @@ import {
ChevronDownIcon as ChevronDown,
ChevronRightIcon as ChevronRight,
ClockIcon as Clock,
CommentIcon as Comment,
FileCodeIcon as FileCode,
FileIcon as File,
FileMediaIcon as FileMedia,
@ -10,130 +11,76 @@ import {
GearIcon as Gear,
GrabberIcon as Grabber,
HourglassIcon as Hourglass,
Icon as OcticonIcon,
IconProps,
MarkdownIcon as Markdown,
OctofaceIcon as Octoface,
PinIcon as Pin,
ReplyIcon as Reply,
SearchIcon as Search,
TabIcon as Tab,
XIcon as X,
} from '@primer/octicons-react'
import * as React from 'react'
import { cx } from 'utils/cx'
function getSVGIconComponent(
type: string,
): {
IconComponent: OcticonIcon
name: string
} {
switch (type) {
case 'search':
return {
IconComponent: Search,
name: 'Search',
}
case 'loading':
return {
IconComponent: Clock,
name: 'Clock',
}
case 'hourglass':
return {
IconComponent: Hourglass,
name: 'Hourglass',
}
case 'submodule':
return {
IconComponent: Submodule,
name: 'Submodule',
}
case 'grabber':
return {
IconComponent: Grabber,
name: 'Grabber',
}
case 'octoface':
return {
IconComponent: Octoface,
name: 'Octoface',
}
case 'chevron-down':
return {
IconComponent: ChevronDown,
name: 'ChevronDown',
}
case 'x':
return {
IconComponent: X,
name: 'X',
}
case 'gear':
return {
IconComponent: Gear,
name: 'Gear',
}
case 'folder':
return {
IconComponent: ChevronRight,
name: 'ChevronRight',
}
case 'go-to':
return {
IconComponent: Reply,
name: 'Reply',
}
// not supported in octicon v2 yet
// case '.pdf':
// return {
// IconComponent: FilePdf,
// name: 'FilePdf',
// }
case '.zip':
case '.rar':
case '.7z':
return {
IconComponent: FileZip,
name: 'FileZip',
}
case '.md':
return {
IconComponent: Markdown,
name: 'Markdown',
}
case '.png':
case '.jpg':
case '.gif':
case '.bmp':
return {
IconComponent: FileMedia,
name: 'FileMedia',
}
case '.js':
case '.jsx':
case '.ts':
case '.tsx':
case '.es6':
case '.coffee':
case '.css':
case '.less':
case '.scss':
case '.sass':
return {
IconComponent: FileCode,
name: 'FileCode',
}
// TODO: adapt to more file types
// case '': return FileBinary
// case '': return FileSubmodule
// case '': return FileSymlinkDirectory
// case '': return FileSymlinkFile
default:
return {
IconComponent: File,
name: 'File',
}
}
const iconToComponentMap = {
Search,
Clock,
Comment,
Hourglass,
Submodule,
Grabber,
Octoface,
ChevronDown,
X,
Gear,
ChevronRight,
Reply,
FileZip,
Markdown,
FileMedia,
FileCode,
File,
Pin,
Tab,
}
const defaultIcon = 'File'
const typeToIconComponentMap: {
[type: string]: keyof typeof iconToComponentMap
} = {
search: 'Search',
loading: 'Clock',
hourglass: 'Hourglass',
submodule: 'Submodule',
grabber: 'Grabber',
octoface: 'Octoface',
comment: 'Comment',
x: 'X',
pin: 'Pin',
tab: 'Tab',
gear: 'Gear',
folder: 'ChevronRight',
'chevron-down': 'ChevronDown',
'go-to': 'Reply',
'.zip': 'FileZip',
'.rar': 'FileZip',
'.7z': 'FileZip',
'.md': 'Markdown',
'.png': 'FileMedia',
'.jpg': 'FileMedia',
'.gif': 'FileMedia',
'.bmp': 'FileMedia',
'.js': 'FileCode',
'.jsx': 'FileCode',
'.ts': 'FileCode',
'.tsx': 'FileCode',
'.es6': 'FileCode',
'.coffee': 'FileCode',
'.css': 'FileCode',
'.less': 'FileCode',
'.scss': 'FileCode',
'.sass': 'FileCode',
}
type Props = {
@ -151,7 +98,8 @@ export const Icon = React.memo(function Icon({
}: Props) {
let children: React.ReactNode = null
if (!placeholder) {
const { name, IconComponent } = getSVGIconComponent(type)
const name = typeToIconComponentMap[type] || defaultIcon
const IconComponent = iconToComponentMap[name]
children = <IconComponent className={cx('octicon', name)} {...otherProps} />
}
return (

View file

@ -11,14 +11,14 @@ export function MetaBar({ metaData }: Props) {
const { userName, repoName, branchName } = metaData
const { repoUrl, userUrl } = platform.resolveUrlFromMetaData(metaData)
return (
<Flex flexDirection="column" justifyContent="space-between" className={'meta-bar'}>
<>
<Breadcrumb className={'user-and-repo'}>
<Breadcrumb.Item href={userUrl}>{userName}</Breadcrumb.Item>
<Breadcrumb.Item href={repoUrl}>
<Text fontWeight="bolder">{repoName}</Text>
</Breadcrumb.Item>
</Breadcrumb>
<Flex flexWrap="nowrap">
<Flex paddingTop={1} flexWrap="nowrap" alignItems="flex-start">
<div className={'octicon-wrapper'}>
<GitBranchIcon size="small" />
</div>
@ -26,6 +26,6 @@ export function MetaBar({ metaData }: Props) {
{branchName || '...'}
</BranchName>
</Flex>
</Flex>
</>
)
}

View file

@ -2,7 +2,7 @@ import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { cx } from 'utils/cx'
import { OperatingSystems, os } from 'utils/general'
import { getFileIconSrc, getFolderIconSrc } from '../utils/parseIconMapCSV'
import { getFileIconSrc, getFolderIconSrc } from 'utils/parseIconMapCSV'
import { Icon } from './Icon'
function getIconType(node: TreeNode) {
@ -38,6 +38,7 @@ export function Node({
style,
onClick,
}: Props) {
const { compactFileTree: compact } = useConfigs().value
return (
<a
href={node.url}
@ -53,15 +54,15 @@ export function Node({
onClick(event, node)
}}
className={cx(`node-item`, { focused, disabled: node.accessDenied, expanded })}
style={{ ...style, paddingLeft: `${10 + 20 * depth}px` }}
className={cx(`node-item`, { focused, disabled: node.accessDenied, expanded, compact })}
style={{ ...style, paddingLeft: `${10 + (compact ? 10 : 20) * depth}px` }}
title={node.path}
>
<div className={'node-item-label'}>
<NodeItemIcon node={node} open={expanded} loading={loading} />
{renderLabelText(node)}
</div>
{renderActions && <div>{renderActions(node)}</div>}
{renderActions && <div className={'actions'}>{renderActions(node)}</div>}
</a>
)
}

View file

@ -1,14 +1,24 @@
import { Icon } from 'components/Icon'
import * as React from 'react'
import { Size } from './Resizable'
import { Size } from './SideBarBodyWrapper'
type Props = {
size: Size
onResize(size: Size): void
onResetSize?(): void
onResizeStateChange?(state: ResizeState): void
style?: React.CSSProperties
}
export function HorizontalResizeHandler({ onResize, size, style }: Props) {
type ResizeState = 'idle' | 'resizing'
export function HorizontalResizeHandler({
onResize,
onResetSize,
onResizeStateChange,
size,
style,
}: Props) {
const pointerDown = React.useRef(false)
const startX = React.useRef(0)
const baseSize = React.useRef(size)
@ -22,6 +32,7 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
startX.current = clientX
pointerDown.current = true
baseSize.current = latestPropSize.current
onResizeStateChange?.('resizing')
}, [])
React.useEffect(() => {
@ -39,6 +50,7 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
if (pointerDown.current) {
pointerDown.current = false
baseSize.current = latestPropSize.current
onResizeStateChange?.('idle')
}
}
window.addEventListener('mouseup', onPointerUp)
@ -46,7 +58,12 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
}, [])
return (
<div className={'gitako-resize-handler'} onMouseDown={onPointerDown} style={style}>
<div
className={'gitako-resize-handler'}
onMouseDown={onPointerDown}
onDoubleClick={onResetSize}
style={style}
>
<Icon type={'grabber'} className={'grabber-icon'} size={20} />
</div>
)

View file

@ -35,7 +35,7 @@ export function SearchBar({ onSearch, onFocus, value }: Props) {
/>
<div className={`actions`}>
<button
className={`toggle-mode`}
className={`toggle-search-mode`}
title="Toggle search mode"
onClick={() => {
const newMode = searchMode === 'regex' ? 'fuzzy' : 'regex'

View file

@ -2,164 +2,213 @@ import { AccessDeniedDescription } from 'components/AccessDeniedDescription'
import { FileExplorer } from 'components/FileExplorer'
import { MetaBar } from 'components/MetaBar'
import { Portal } from 'components/Portal'
import { Resizable } from 'components/Resizable'
import { SettingsBar } from 'components/settings/SettingsBar'
import { SideBarBodyWrapper } from 'components/SideBarBodyWrapper'
import { ToggleShowButton } from 'components/ToggleShowButton'
import { connect } from 'driver/connect'
import { SideBarCore } from 'driver/core'
import { ConnectorState, Props } from 'driver/core/SideBar'
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import { useGitHubAttachCopyFileButton, useGitHubAttachCopySnippetButton } from 'platforms/GitHub'
import * as React from 'react'
import { cx } from 'utils/cx'
import { parseURLSearch, run } from 'utils/general'
import * as DOMHelper from 'utils/DOMHelper'
import { run } from 'utils/general'
import { useCatchNetworkError } from 'utils/hooks/useCatchNetworkError'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { loadWithPJAX, useOnPJAXDone, usePJAX } from 'utils/hooks/usePJAX'
import { useProgressBar } from 'utils/hooks/useProgressBar'
import * as keyHelper from 'utils/keyHelper'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SideBarErrorContext } from '../containers/ErrorContext'
import { RepoContext } from '../containers/RepoContext'
import { SideBarStateContext } from '../containers/SideBarState'
import { Theme } from '../containers/Theme'
import { useToggleSideBarWithKeyboard } from '../utils/hooks/useToggleSideBarWithKeyboard'
import { Icon } from './Icon'
import { IIFC } from './IIFC'
import { LoadingIndicator } from './LoadingIndicator'
import { Theme } from './Theme'
const RawGitako: React.FC<Props & ConnectorState> = function RawGitako(props) {
const {
errorDueToAuth,
metaData,
treeData,
defer,
error,
shouldShow,
showSettings,
logoContainerElement,
toggleShowSideBar,
toggleShowSettings,
configContext,
} = props
export function SideBar() {
const metaData = React.useContext(RepoContext)
const state = useLoadedContext(SideBarStateContext).value
const configContext = useConfigs()
const accessToken = configContext.value.accessToken || ''
const [baseSize] = React.useState(() => configContext.value.sideBarWidth)
const $showSettings = useStateIO(false)
const showSettings = $showSettings.value
const toggleShowSettings = React.useCallback(() => $showSettings.onChange(show => !show), [])
const $logoContainerElement = useStateIO<HTMLElement | null>(null)
const hasMetaData = state !== 'disabled' // will be true since retrieving data, cannot use Boolean(metaData)
React.useEffect(() => {
run(async function () {
if (!accessToken) {
const accessToken = await trySetUpAccessTokenWithCode()
if (accessToken) configContext.onChange({ accessToken })
}
})
}, [])
React.useEffect(() => {
props.init()
}, [accessToken])
React.useEffect(
function attachKeyDown() {
if (props.disabled || !configContext.value.shortcut) return
function onKeyDown(e: KeyboardEvent) {
const keys = keyHelper.parseEvent(e)
if (keys === configContext.value.shortcut) {
toggleShowSideBar()
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
},
[toggleShowSideBar, props.disabled, configContext.value.shortcut],
)
if (hasMetaData) {
DOMHelper.markGitakoReadyState(true)
$showSettings.onChange(false)
$logoContainerElement.onChange(DOMHelper.insertLogoMountPoint())
} else {
DOMHelper.markGitakoReadyState(false)
}
}, [hasMetaData])
const sidebarToggleMode = configContext.value.sidebarToggleMode
const intelligentToggle = configContext.value.intelligentToggle
const $shouldShow = useStateIO(intelligentToggle === null ? false : intelligentToggle)
const shouldShow = $shouldShow.value
React.useEffect(() => {
const shouldShow = intelligentToggle === null ? platform.shouldShow() : intelligentToggle
props.setShouldShow(shouldShow)
}, [intelligentToggle, props.metaData])
if (sidebarToggleMode === 'persistent') {
DOMHelper.setBodyIndent(shouldShow)
} else {
DOMHelper.setBodyIndent(false)
}
const updateSideBarVisibility = React.useCallback(
function updateSideBarVisibility() {
if (intelligentToggle === null) {
props.setShouldShow(platform.shouldShow())
}
if (shouldShow) {
DOMHelper.focusFileExplorer() // TODO: verify if it works
}
}, [shouldShow, sidebarToggleMode])
// Save expand state on toggle if auto expand if not on
React.useEffect(() => {
if (intelligentToggle !== null) {
configContext.onChange({ intelligentToggle: shouldShow })
}
}, [shouldShow, intelligentToggle])
const error = useLoadedContext(SideBarErrorContext).value
// Lock shouldShow on error
React.useEffect(() => {
if (error && shouldShow) {
$shouldShow.onChange(false)
}
}, [error])
const setShowSideBar = React.useCallback(
(show: typeof $shouldShow.value) => {
if (!error) $shouldShow.onChange(show)
},
[props.metaData?.branchName, intelligentToggle],
[error],
)
useOnPJAXDone(updateSideBarVisibility)
useGitHubAttachCopyFileButton(configContext.value.copyFileButton)
useGitHubAttachCopySnippetButton(configContext.value.copySnippetButton)
const toggleShowSideBar = React.useCallback(() => {
if (!error) $shouldShow.onChange(show => !show)
}, [error])
useToggleSideBarWithKeyboard(state, configContext, toggleShowSideBar)
useSetShouldShowOnPJAXDone(setShowSideBar)
platform.usePlatformHooks?.()
usePJAX()
useProgressBar()
// Hide sidebar when error due to auth but token is set #128
const hideSidebarOnInvalidToken: boolean =
intelligentToggle === null && Boolean(state === 'error-due-to-auth' && accessToken)
React.useEffect(() => {
if (hideSidebarOnInvalidToken) {
setShowSideBar(false)
}
}, [hideSidebarOnInvalidToken])
return (
<Theme>
<div className={'gitako-side-bar'}>
<Portal into={logoContainerElement}>
{!shouldShow && (
<ToggleShowButton error={error} onClick={error ? undefined : toggleShowSideBar} />
)}
<Portal into={$logoContainerElement.value}>
<ToggleShowButton
error={error}
className={cx({
hidden: shouldShow,
})}
onHover={sidebarToggleMode === 'float' ? () => setShowSideBar(true) : undefined}
onClick={toggleShowSideBar}
/>
</Portal>
<Resizable className={cx({ hidden: error || !shouldShow })} baseSize={baseSize}>
<SideBarBodyWrapper
className={cx(`toggle-mode-${sidebarToggleMode}`, {
collapsed: error || !shouldShow,
})}
baseSize={baseSize}
onLeave={sidebarToggleMode === 'float' ? () => setShowSideBar(false) : undefined}
>
<div className={'gitako-side-bar-body'}>
<div className={'close-side-bar-button-position'}>
<button className={'close-side-bar-button'} onClick={toggleShowSideBar}>
<Icon className={'action-icon'} type={'x'} />
</button>
</div>
<div className={'gitako-side-bar-content'}>
{metaData ? (
<div className={'header'}>
<MetaBar metaData={metaData} />
<div
className={'gitako-side-bar-content'}
onClick={showSettings ? toggleShowSettings : undefined}
>
<div className={'header'}>
<div className={'close-side-bar-button-position'}>
{sidebarToggleMode === 'persistent' && (
<button
title={'Collapse sidebar'}
className={'close-side-bar-button'}
onClick={toggleShowSideBar}
>
<Icon className={'action-icon'} type={'tab'} />
</button>
)}
<button
title={'Toggle sidebar dock mode between float and persistent'}
className={cx('close-side-bar-button', {
active: sidebarToggleMode === 'persistent',
})}
onClick={() =>
configContext.onChange({
sidebarToggleMode: sidebarToggleMode === 'float' ? 'persistent' : 'float',
})
}
>
<Icon className={'action-icon'} type={'pin'} />
</button>
</div>
) : (
<LoadingIndicator text={'Fetching repo meta...'} />
)}
{errorDueToAuth ? (
<AccessDeniedDescription hasToken={Boolean(accessToken)} />
) : (
metaData && (
<FileExplorer
toggleShowSettings={toggleShowSettings}
metaData={metaData}
treeRoot={treeData}
freeze={showSettings}
accessToken={accessToken}
loadWithPJAX={loadWithPJAX}
config={configContext.value}
defer={defer}
/>
)
)}
{metaData && <MetaBar metaData={metaData} />}
</div>
{run(() => {
switch (state) {
case 'disabled':
return null
case 'getting-access-token':
return <LoadingIndicator text={'Getting access token...'} />
case 'after-getting-access-token':
case 'meta-loading':
return <LoadingIndicator text={'Fetching repo meta...'} />
case 'error-due-to-auth':
return <AccessDeniedDescription />
default:
return (
metaData && (
<IIFC>
{() => (
<FileExplorer
metaData={metaData}
freeze={showSettings}
accessToken={accessToken}
loadWithPJAX={loadWithPJAX}
config={configContext.value}
catchNetworkErrors={useCatchNetworkError()}
/>
)}
</IIFC>
)
)
}
})}
</div>
<SettingsBar
defer={defer}
toggleShowSettings={toggleShowSettings}
activated={showSettings}
/>
<SettingsBar toggleShowSettings={toggleShowSettings} activated={showSettings} />
</div>
</Resizable>
</SideBarBodyWrapper>
</div>
</Theme>
)
}
RawGitako.defaultProps = {
shouldShow: false,
showSettings: false,
errorDueToAuth: false,
disabled: false,
}
export const SideBar = connect(SideBarCore)(RawGitako)
async function trySetUpAccessTokenWithCode() {
const search = parseURLSearch()
if ('code' in search) {
const accessToken = await platform.setOAuth(search.code)
if (!accessToken) alert(`Gitako: The OAuth token has expired, please try again.`)
window.history.replaceState(
{},
'removed search param',
window.location.pathname.replace(window.location.search, ''),
)
return accessToken
}
function useSetShouldShowOnPJAXDone(setShouldShow: (value: boolean) => void) {
const { intelligentToggle, sidebarToggleMode } = useConfigs().value
const updateSideBarVisibility = React.useCallback(() => {
if (intelligentToggle === null && sidebarToggleMode === 'persistent') {
setShouldShow(platform.shouldShow())
}
}, [intelligentToggle, sidebarToggleMode])
React.useEffect(() => {
updateSideBarVisibility()
}, [updateSideBarVisibility])
useOnPJAXDone(updateSideBarVisibility)
}

View file

@ -2,23 +2,31 @@ import { HorizontalResizeHandler } from 'components/ResizeHandler'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { useDebounce, useWindowSize } from 'react-use'
import { defaultConfigs } from 'utils/config/helper'
import { cx } from 'utils/cx'
import { setResizingState } from 'utils/DOMHelper'
import * as features from 'utils/features'
import { useCSSVariable } from './useCSSVariable'
import { useCSSVariable } from 'utils/hooks/useCSSVariable'
export type Size = number
type Props = {
baseSize: Size
className?: string
onLeave?: React.HTMLAttributes<HTMLElement>['onMouseLeave']
}
const MINIMAL_CONTENT_VIEWPORT_WIDTH = 100
const MINIMAL_WIDTH = 240
export function Resizable({ baseSize, className, children }: React.PropsWithChildren<Props>) {
export function SideBarBodyWrapper({
baseSize,
className,
children,
onLeave,
}: React.PropsWithChildren<Props>) {
const [size, setSize] = React.useState(baseSize)
const configContext = useConfigs()
const blockLeaveRef = React.useRef(false)
React.useEffect(() => {
setSize(baseSize)
@ -47,10 +55,28 @@ export function Resizable({ baseSize, className, children }: React.PropsWithChil
else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH)
else setSize(size)
}, [])
const onMouseLeave = React.useCallback(
e => {
if (blockLeaveRef.current) return
onLeave?.(e)
},
[onLeave],
)
return (
<div className={cx('gitako-position-wrapper', className)}>
<div className={'gitako-position-content'}>{children}</div>
{features.resize && <HorizontalResizeHandler onResize={onResize} size={size} />}
<div className={cx('gitako-side-bar-body-wrapper', className)} onMouseLeave={onMouseLeave}>
<div className={'gitako-side-bar-body-wrapper-content'}>{children}</div>
{features.resize && (
<HorizontalResizeHandler
onResize={onResize}
onResetSize={() => setSize(defaultConfigs.sideBarWidth)}
onResizeStateChange={state => {
blockLeaveRef.current = state === 'resizing'
}}
size={size}
/>
)}
</div>
)
}

View file

@ -1,27 +1,28 @@
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import { Field } from './settings/Field'
export type SimpleField = {
key: keyof Config
export type SimpleField<Key extends keyof Config> = {
key: Key
label: string
wikiLink?: string
tooltip?: string
description?: string
disabled?: boolean
overwrite?: {
value: <T>(value: T) => boolean
onChange: (checked: boolean) => any
value: (value: Config[Key]) => boolean
onChange: (checked: boolean) => Config[Key]
}
}
type Props = {
field: SimpleField
type Props<Key extends keyof Config> = {
field: SimpleField<Key>
onChange?(): void
}
export function SimpleToggleField({ field, onChange }: Props) {
export function SimpleToggleField<Key extends keyof Config>({ field, onChange }: Props<Key>) {
const { overwrite } = field
const configContext = useConfigs()
const value = configContext.value[field.key]
@ -54,6 +55,7 @@ export function SimpleToggleField({ field, onChange }: Props) {
<input
id={field.key}
name={field.key}
disabled={field.disabled}
type={'checkbox'}
onChange={async e => {
const enabled = e.currentTarget.checked

View file

@ -2,13 +2,16 @@ import iconSrc from 'assets/icons/Gitako.png'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { useDebounce, useWindowSize } from 'react-use'
import { cx } from 'utils/cx'
import { Icon } from './Icon'
type Props = {
error?: string
error?: string | null
className?: React.HTMLAttributes<HTMLButtonElement>['className']
onHover?: React.HTMLAttributes<HTMLButtonElement>['onMouseEnter']
} & Pick<React.HTMLAttributes<HTMLButtonElement>, 'onClick'>
export function ToggleShowButton({ error, onClick }: Props) {
export function ToggleShowButton({ error, className, onClick, onHover }: Props) {
const ref = React.useRef<HTMLDivElement>(null)
const config = useConfigs()
const [distance, setDistance] = React.useState(config.value.toggleButtonVerticalDistance)
@ -35,10 +38,13 @@ export function ToggleShowButton({ error, onClick }: Props) {
const toggleIconMode = config.value.toggleButtonContent
return (
<div ref={ref} className={'gitako-toggle-show-button-wrapper'}>
<div ref={ref} className={cx('gitako-toggle-show-button-wrapper', className)}>
<button
className={'gitako-toggle-show-button'}
className={cx('gitako-toggle-show-button', {
error,
})}
onClick={onClick}
onMouseEnter={onHover}
draggable
onDragStart={event => {
hideDragPreview(event)

View file

@ -8,7 +8,7 @@ import * as React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SettingsSection } from './SettingsSection'
const ACCESS_TOKEN_REGEXP = /^[0-9a-f]+$/
const ACCESS_TOKEN_REGEXP = /^([0-9a-fA-F]+|gh[pousr]_[A-Za-z0-9_]+)$/
type Props = {}
@ -37,30 +37,33 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
[],
)
const onPressAccessToken = React.useCallback(({ key }: React.KeyboardEvent) => {
if (key === 'Enter') saveToken()
}, [])
const saveToken = React.useCallback(
async (hint?: typeof useAccessTokenHint.value) => {
async (
hint: typeof useAccessTokenHint.value = (
<span>
<a href="#" onClick={() => window.location.reload()}>
Reload
</a>{' '}
to activate!
</span>
),
) => {
if (accessToken) {
configContext.onChange({ accessToken })
useAccessToken.onChange('')
useAccessTokenHint.onChange(
hint || (
<span>
<a href="#" onClick={() => window.location.reload()}>
Reload
</a>{' '}
to activate!
</span>
),
)
useAccessTokenHint.onChange(hint)
}
},
[accessToken],
)
const onPressAccessToken = React.useCallback(
({ key }: React.KeyboardEvent) => {
if (key === 'Enter') saveToken()
},
[saveToken],
)
return (
<SettingsSection
title={
@ -121,7 +124,7 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
</div>
</div>
)}
{accessTokenHint && !focusInput.value && <span className={'hint'}>{accessTokenHint}</span>}
{accessTokenHint && <span className={'hint'}>{accessTokenHint}</span>}
</SettingsSection>
)
}

View file

@ -2,7 +2,7 @@ import { wikiLinks } from 'components/settings/SettingsBar'
import { SimpleToggleField } from 'components/SimpleToggleField'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import { Option, SelectInput } from '../SelectInput'
import { Field } from './Field'
import { SettingsSection } from './SettingsSection'
@ -54,7 +54,7 @@ export function FileTreeSettings(props: React.PropsWithChildren<Props>) {
})
}}
value={configContext.value.recursiveToggleFolder}
></SelectInput>
/>
</Field>
<Field title="Icons" id="file-tree-icons">
<SelectInput<Config['icons']>
@ -76,6 +76,20 @@ export function FileTreeSettings(props: React.PropsWithChildren<Props>) {
tooltip: 'Merge folders and their only child folder to make UI more compact.',
}}
/>
<SimpleToggleField
field={{
key: 'commentToggle',
label: 'Show PR file comments',
tooltip: 'Show number of comments next to file names in Pull Requests.',
}}
/>
<SimpleToggleField
field={{
key: 'compactFileTree',
label: 'Compact file tree layout',
tooltip: 'View file tree structures more effectively.',
}}
/>
</SettingsSection>
)
}

View file

@ -1,4 +1,4 @@
import { Label, Link } from '@primer/components'
import { Link } from '@primer/components'
import { Icon } from 'components/Icon'
import { VERSION } from 'env'
import { platform } from 'platforms'
@ -15,13 +15,13 @@ const WIKI_HOME_LINK = 'https://github.com/EnixCoda/Gitako/wiki'
export const wikiLinks = {
compressSingletonFolder: `${WIKI_HOME_LINK}/Compress-Singleton-Folder`,
changeLog: `${WIKI_HOME_LINK}/Change-Log`,
codeFolding: `${WIKI_HOME_LINK}/Code-folding`,
copyFileButton: `${WIKI_HOME_LINK}/Copy-file-and-snippet`,
copySnippet: `${WIKI_HOME_LINK}/Copy-file-and-snippet`,
createAccessToken: `${WIKI_HOME_LINK}/Access-token-for-Gitako`,
}
type Props = {
defer?: boolean
activated: boolean
toggleShowSettings: () => void
}
@ -30,18 +30,24 @@ function SettingsBarContent() {
const useReloadHint = useStateIO<React.ReactNode>('')
const { value: reloadHint } = useReloadHint
const moreFields: SimpleField[] =
const moreFields: SimpleField<'copyFileButton' | 'copySnippetButton'|'codeFolding'>[] =
platform === GitHub
? [
{
key: 'codeFolding',
label: 'Fold source code button',
wikiLink: wikiLinks.codeFolding,
tooltip: `Read more in Gitako's Wiki`,
},
{
key: 'copyFileButton',
label: 'Copy file shortcut',
label: 'Copy file button',
wikiLink: wikiLinks.copyFileButton,
tooltip: `Read more in Gitako's Wiki`,
},
{
key: 'copySnippetButton',
label: 'Copy snippet shortcut',
label: 'Copy snippet button',
wikiLink: wikiLinks.copySnippet,
tooltip: `Read more in Gitako's Wiki`,
},
@ -78,7 +84,7 @@ function SettingsBarContent() {
}
export function SettingsBar(props: Props) {
const { defer, toggleShowSettings, activated } = props
const { toggleShowSettings, activated } = props
return (
<div className={'gitako-settings-bar'}>
{activated && <SettingsBarContent />}
@ -93,15 +99,6 @@ export function SettingsBar(props: Props) {
{VERSION}
</Link>
<div className={'header-right'}>
{defer && (
<Label
title="File tree data is loaded on demand. And search results are limited."
bg="yellow.5"
color="gray.6"
>
Lazy Mode
</Label>
)}
<button className={'settings-button'} onClick={toggleShowSettings}>
{activated ? (
<Icon type={'chevron-down'} className={'hide-settings-icon'} />

View file

@ -3,7 +3,7 @@ import { Option, SelectInput } from 'components/SelectInput'
import { SimpleToggleField } from 'components/SimpleToggleField'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import { friendlyFormatShortcut } from 'utils/general'
import { useStateIO } from 'utils/hooks/useStateIO'
import * as keyHelper from 'utils/keyHelper'
@ -88,16 +88,21 @@ export function SidebarSettings(props: React.PropsWithChildren<Props>) {
})
}}
value={configContext.value.toggleButtonContent}
></SelectInput>
/>
</Field>
<SimpleToggleField
field={{
key: 'intelligentToggle',
label: 'Auto expand',
tooltip:
'Gitako will expand when exploring source files, pull requests, etc. And collapse otherwise.',
disabled: configContext.value.sidebarToggleMode === 'float',
tooltip: `Gitako will expand when exploring source files, pull requests, etc. And collapse otherwise.${
configContext.value.sidebarToggleMode === 'float'
? '\nNow disabled as sidebar is in float mode.'
: ''
}`,
overwrite: {
value: enabled => enabled === null,
value: enabled =>
configContext.value.sidebarToggleMode === 'float' ? false : enabled === null,
onChange: checked => (checked ? null : true),
},
}}

View file

@ -1,6 +1,5 @@
import * as React from 'react'
import * as configsHelper from 'utils/configHelper'
import { Config } from 'utils/configHelper'
import { Config, configHelper } from 'utils/config/helper'
type Props = {}
@ -12,19 +11,19 @@ export const ConfigsContext = React.createContext<ContextShape | null>(null)
export function ConfigsContextWrapper(props: React.PropsWithChildren<Props>) {
const [configs, setConfigs] = React.useState<Config | null>(null)
React.useEffect(() => {
configsHelper.get().then(setConfigs)
configHelper.get().then(setConfigs)
}, [])
const onChange = React.useCallback(
(updatedConfigs: Partial<Config>) => {
const mergedConfigs = { ...configs, ...updatedConfigs } as Config
configsHelper.set(mergedConfigs)
configHelper.set(mergedConfigs)
setConfigs(mergedConfigs)
},
[configs, setConfigs],
)
if (configs === null) return null
return (
<ConfigsContext.Provider value={{ value: configs, onChange: onChange }}>
<ConfigsContext.Provider value={{ value: configs, onChange }}>
{props.children}
</ConfigsContext.Provider>
)

View file

@ -0,0 +1,12 @@
import * as React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
export type SideBarErrorContextShape = IO<string | null>
export const SideBarErrorContext = React.createContext<SideBarErrorContextShape | null>(null)
export function StateBarErrorContextWrapper({ children }: React.PropsWithChildren<{}>) {
const $error = useStateIO<string | null>(null)
return <SideBarErrorContext.Provider value={$error}>{children}</SideBarErrorContext.Provider>
}

View file

@ -0,0 +1,61 @@
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import * as React from 'react'
import { parseURLSearch, run } from 'utils/general'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SideBarStateContext } from './SideBarState'
/**
* Setup access token before sending other requests
*/
export function OAuthWrapper({ children }: React.PropsWithChildren<{}>) {
const running = useGetAccessToken()
const $state = useLoadedContext(SideBarStateContext)
const needGetAccessTokenRef = React.useRef(running)
React.useEffect(() => {
if (needGetAccessTokenRef.current) {
$state.onChange(running ? 'getting-access-token' : 'after-getting-access-token')
}
}, [running])
// block children rendering on the first render if setting token
if (running && $state.value !== 'getting-access-token') return null
return <>{children}</>
}
function useGetAccessToken() {
const $block = useStateIO(() => Boolean(getCodeSearchParam()))
const configContext = useConfigs()
const { accessToken } = configContext.value
React.useEffect(() => {
run(async function () {
const code = getCodeSearchParam()
if (code && !accessToken) {
const accessToken = await getAccessTokenWithCode(code)
if (accessToken) configContext.onChange({ accessToken })
}
$block.onChange(false)
})
}, [])
return $block.value
}
function getCodeSearchParam() {
return parseURLSearch().get('code')
}
async function getAccessTokenWithCode(code: string) {
const accessToken = await platform.setOAuth(code)
if (!accessToken) alert(`Gitako: The OAuth token may have expired, please try again.`)
const search = parseURLSearch()
search.delete('code')
window.history.replaceState(
{},
'removed search param',
window.location.pathname.replace(window.location.search, '?' + search.toString()),
)
return accessToken
}

View file

@ -0,0 +1,107 @@
import { useConfigs } from 'containers/ConfigsContext'
import { platform } from 'platforms'
import * as React from 'react'
import { useEffectOnSerializableUpdates } from 'utils/hooks/useEffectOnSerializableUpdates'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import { useStateIO } from 'utils/hooks/useStateIO'
import { useCatchNetworkError } from '../utils/hooks/useCatchNetworkError'
import { SideBarStateContext } from './SideBarState'
export const RepoContext = React.createContext<MetaData | null>(null)
export function RepoContextWrapper({ children }: React.PropsWithChildren<{}>) {
const partialMetaData = usePartialMetaData()
const defaultBranch = useDefaultBranch(partialMetaData)
const metaData = useMetaData(partialMetaData, defaultBranch)
return <RepoContext.Provider value={metaData}>{children}</RepoContext.Provider>
}
function resolvePartialMetaData() {
const partialMetaData = platform.resolvePartialMetaData()
if (partialMetaData) {
const { userName, repoName, type } = partialMetaData
return {
userName,
repoName,
type: type === 'pull' ? type : undefined,
}
} else {
return partialMetaData
}
}
function usePartialMetaData(): PartialMetaData | null {
const $state = useLoadedContext(SideBarStateContext)
const isGettingAccessToken = $state.value === 'getting-access-token' // will be false after getting access token and trigger meta-resolve progress
// sync along URL and DOM
const $partialMetaData = useStateIO(isGettingAccessToken ? null : resolvePartialMetaData)
const $committedPartialMetaData = useStateIO($partialMetaData.value)
const setPartialMetaData = () => $partialMetaData.onChange(resolvePartialMetaData())
React.useEffect(() => {
if (!isGettingAccessToken) setPartialMetaData()
}, [isGettingAccessToken])
useOnPJAXDone(setPartialMetaData)
useEffectOnSerializableUpdates(
$partialMetaData.value,
JSON.stringify,
$committedPartialMetaData.onChange,
)
React.useEffect(() => {
if (!$partialMetaData.value && !isGettingAccessToken) {
$state.onChange('disabled')
}
}, [$partialMetaData.value])
return $committedPartialMetaData.value
}
function useBranchName(): MetaData['branchName'] | null {
// sync along URL and DOM
const $branchName = useStateIO(() => platform.resolvePartialMetaData()?.branchName || null)
useOnPJAXDone(() => $branchName.onChange(platform.resolvePartialMetaData()?.branchName || null))
return $branchName.value
}
function useDefaultBranch(partialMetaData: PartialMetaData | null) {
const { accessToken } = useConfigs().value
const $state = useLoadedContext(SideBarStateContext)
const $defaultBranch = useStateIO<string | null>(null)
const catchNetworkError = useCatchNetworkError()
React.useEffect(() => {
catchNetworkError(async () => {
if (!partialMetaData) return
$state.onChange('meta-loading')
const defaultBranch = await platform.getDefaultBranchName(partialMetaData, accessToken)
$defaultBranch.onChange(defaultBranch)
})
}, [partialMetaData, accessToken])
return $defaultBranch.value
}
function useMetaData(
partialMetaData: PartialMetaData | null,
defaultBranchName: MetaData['defaultBranchName'] | null,
) {
const $state = useLoadedContext(SideBarStateContext)
const $metaData = useStateIO<MetaData | null>(null)
const branchName = useBranchName()
const theBranch = branchName && branchName !== defaultBranchName ? branchName : defaultBranchName
React.useEffect(() => {
if (partialMetaData && defaultBranchName && theBranch) {
const { userName, repoName } = partialMetaData
const safeMetaData: MetaData = {
userName,
repoName,
branchName: theBranch,
defaultBranchName,
}
$metaData.onChange(safeMetaData)
$state.onChange('meta-loaded')
} else {
$metaData.onChange(null)
}
}, [partialMetaData, defaultBranchName, theBranch])
return $metaData.value
}

View file

@ -0,0 +1,28 @@
import * as React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
export type SideBarState =
| 'disabled'
| 'getting-access-token'
| 'after-getting-access-token' // mid-state for a smoother state switch out of 'getting-access-token'
| 'meta-loading'
| 'meta-loaded'
| 'tree-loading'
| 'tree-rendering'
| 'tree-rendered'
| 'idle'
| 'error-due-to-auth'
export type SideBarStateContextShape = IO<SideBarState>
export const SideBarStateContext = React.createContext<SideBarStateContextShape | null>(null)
export function StateBarStateContextWrapper({ children }: React.PropsWithChildren<{}>) {
const $state = useStateIO<SideBarState>('disabled')
return (
<SideBarStateContext.Provider value={$state}>
{$state.value !== null && children}
</SideBarStateContext.Provider>
)
}

View file

@ -6,7 +6,7 @@ import * as React from 'react'
import * as ReactDOM from 'react-dom'
import './content.scss'
if (platform.resolveMeta()) {
if (platform.resolvePartialMetaData()) {
addMiddleware(withErrorLog)
if (document.readyState === 'loading') {
@ -26,7 +26,7 @@ async function init() {
// injects a copy of stylesheets so that other extensions(e.g. dark reader) could read
// resolves when style is loaded to prevent render without proper styles
async function injectStyles(url: string) {
return new Promise(resolve => {
return new Promise<void>(resolve => {
const linkElement = document.createElement('link')
linkElement.setAttribute('rel', 'stylesheet')
linkElement.setAttribute('href', url)

View file

@ -1,26 +1,26 @@
import { SideBarStateContextShape } from 'containers/SideBarState'
import { GetCreatedMethod, MethodCreator } from 'driver/connect'
import { platform } from 'platforms'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import * as DOMHelper from 'utils/DOMHelper'
import { OperatingSystems, os } from 'utils/general'
import { VisibleNodes, VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'
export type Props = {
treeRoot?: TreeNode
metaData: MetaData
freeze: boolean
accessToken: string | undefined
toggleShowSettings: React.MouseEventHandler
config: Config
loadWithPJAX(url: string): void
defer?: boolean
catchNetworkErrors: <T>(fn: () => T) => Promise<T | undefined>
}
export type ConnectorState = {
state: 'pulling' | 'rendering' | 'done'
visibleNodesGenerator: VisibleNodesGenerator | null
visibleNodes: VisibleNodes | null
searchKey: string
searched: boolean // derived state from searchKey, = !!searchKey
defer: boolean
handleKeyDown: GetCreatedMethod<typeof handleKeyDown>
updateSearchKey: GetCreatedMethod<typeof updateSearchKey>
@ -45,37 +45,58 @@ type BoundMethodCreator<Args extends any[] = []> = MethodCreator<Props, Connecto
export const setUpTree: BoundMethodCreator<
[
Required<Pick<Props, 'treeRoot' | 'metaData'>> & {
config: Pick<Config, 'compressSingletonFolder' | 'accessToken'>
},
{ stateContext: SideBarStateContextShape } & Required<Pick<Props, 'metaData'>> & {
config: Pick<Config, 'compressSingletonFolder' | 'accessToken'>
},
]
> = dispatch => async ({ treeRoot, metaData, config }) => {
dispatch.set({ state: 'rendering' })
> = dispatch => ({ stateContext, metaData, config }) => {
const {
props: { catchNetworkErrors },
} = dispatch.get()
const visibleNodesGenerator = new VisibleNodesGenerator({
root: treeRoot,
compress: config.compressSingletonFolder,
async getTreeData(path) {
const { root } = await platform.getTreeData(metaData, path, false, config.accessToken)
return root
},
})
dispatch.set({ visibleNodesGenerator })
visibleNodesGenerator.onUpdate(visibleNodes => dispatch.set({ visibleNodes }))
catchNetworkErrors(async () => {
const { userName, repoName, branchName } = metaData
if (platform.shouldExpandAll?.()) {
const unsubscribe = visibleNodesGenerator.onUpdate(visibleNodes => {
unsubscribe()
visibleNodes.nodes.forEach(node =>
dispatch.call(toggleNodeExpansion, node, { recursive: true }),
)
stateContext.onChange('tree-loading')
const { root: treeRoot, defer = false } = await platform.getTreeData(
{
branchName: branchName,
userName,
repoName,
},
'/',
true,
config.accessToken,
)
stateContext.onChange('tree-rendering')
dispatch.set({ defer })
const visibleNodesGenerator = new VisibleNodesGenerator({
root: treeRoot,
compress: config.compressSingletonFolder,
async getTreeData(path) {
const { root } = await platform.getTreeData(metaData, path, false, config.accessToken)
return root
},
})
} else {
const targetPath = platform.getCurrentPath(metaData.branchName)
if (targetPath) dispatch.call(goTo, targetPath)
}
dispatch.set({ visibleNodesGenerator })
visibleNodesGenerator.onUpdate(visibleNodes => dispatch.set({ visibleNodes }))
dispatch.set({ state: 'done' })
if (platform.shouldExpandAll?.()) {
const unsubscribe = visibleNodesGenerator.onUpdate(visibleNodes => {
unsubscribe()
visibleNodes.nodes.forEach(node =>
dispatch.call(toggleNodeExpansion, node, { recursive: true }),
)
})
} else {
const targetPath = platform.getCurrentPath(metaData.branchName)
if (targetPath) dispatch.call(goTo, targetPath)
}
stateContext.onChange('tree-rendered')
})
}
export const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch => event => {
@ -112,6 +133,10 @@ export const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch
break
case 'ArrowLeft':
if (wouldBlockHistoryNavigation(event)) {
muteEvent = false
break
}
if (expandedNodes.has(focusedNode.path)) {
dispatch.call(toggleNodeExpansion, focusedNode, { recursive: event.altKey })
} else {
@ -125,6 +150,10 @@ export const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch
// consider the two keys as 'confirm' key
case 'ArrowRight':
if (wouldBlockHistoryNavigation(event)) {
muteEvent = false
break
}
// expand node or focus on first content node or redirect to file page
if (focusedNode.type === 'tree') {
if (expandedNodes.has(focusedNode.path)) {
@ -285,3 +314,8 @@ export const expandTo: BoundMethodCreator<[string[]]> = dispatch => async curren
visibleNodesGenerator.focusNode(nodeExpandedTo)
}
}
function wouldBlockHistoryNavigation(event: React.KeyboardEvent) {
// Alt + left/right is usually history navigation shortcut on OS other than macOS
return os !== OperatingSystems.macOS && event.altKey
}

View file

@ -1,185 +0,0 @@
import { ConfigsContextShape } from 'containers/ConfigsContext'
import { GetCreatedMethod, MethodCreator } from 'driver/connect'
import { errors, platform, platformName } from 'platforms'
import * as DOMHelper from 'utils/DOMHelper'
import { createPromiseQueue } from 'utils/general'
export type Props = {
configContext: ConfigsContextShape
}
export type ConnectorState = {
// error message
error?: string
// whether Gitako side bar should be shown
shouldShow: boolean
// whether show settings pane
showSettings: boolean
// whether failed loading the repo due to it is private
errorDueToAuth: boolean
// meta data for the repository
metaData?: MetaData
// file tree data
treeData?: TreeNode
logoContainerElement: Element | null
defer?: boolean
disabled: boolean
initializingPromise: Promise<void> | null
} & {
init: GetCreatedMethod<typeof init>
setShouldShow: GetCreatedMethod<typeof setShouldShow>
toggleShowSideBar: GetCreatedMethod<typeof toggleShowSideBar>
toggleShowSettings: GetCreatedMethod<typeof toggleShowSettings>
}
type BoundMethodCreator<Args extends any[] = []> = MethodCreator<Props, ConnectorState, Args>
const promiseQueue = createPromiseQueue()
export const init: BoundMethodCreator = dispatch => async () => {
const leave = await promiseQueue.enter()
try {
const metaData = platform.resolveMeta()
if (!metaData) {
dispatch.set({ disabled: true })
return
}
const { userName, repoName, branchName } = metaData
DOMHelper.markGitakoReadyState(true)
dispatch.set({
errorDueToAuth: false,
showSettings: false,
logoContainerElement: DOMHelper.insertLogoMountPoint(),
})
const {
props: { configContext },
} = dispatch.get()
const { accessToken } = configContext.value
const guessDefaultBranch = 'master' // when to switch to 'main'?
let getTreeData = platform.getTreeData(
{
branchName: branchName || guessDefaultBranch,
userName,
repoName,
},
'/',
true,
accessToken,
)
getTreeData.catch(error => error) // catch it early to prevent the error being raised higher
if (branchName) {
const safeMetaData = {
userName,
repoName,
branchName,
}
dispatch.set({ metaData: safeMetaData })
getTreeData.catch(error => {
dispatch.call(handleError, error)
})
} else {
const defaultBranchName = await platform.getDefaultBranchName(
{ userName, repoName },
accessToken,
)
if (!defaultBranchName) {
throw new Error(`Failed resolving default branch name`)
}
const safeMetaData = {
userName,
repoName,
branchName: defaultBranchName,
}
dispatch.set({ metaData: safeMetaData })
if (defaultBranchName !== guessDefaultBranch && metaData.type !== 'pull') {
// Accessing repository's non-homepage(no branch name in URL, nor in DOM)
// We predicted its default branch to be 'master' and sent aggressive request
// Throw that request due to the repo do not use {defaultBranchName} as default branch
getTreeData = platform.getTreeData(
{
branchName: defaultBranchName,
userName,
repoName,
},
'/',
true,
accessToken,
)
}
}
const { root: treeData, defer } = await getTreeData
dispatch.set({ treeData, defer })
} catch (err) {
dispatch.call(handleError, err)
}
leave()
}
export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => {
if (err.message === errors.EMPTY_PROJECT) {
dispatch.call(setError, 'This project seems to be empty.')
} else if (err.message === errors.BLOCKED_PROJECT) {
dispatch.call(setError, 'Access to the project is blocked.')
} else if (
err.message === errors.NOT_FOUND ||
err.message === errors.BAD_CREDENTIALS ||
err.message === errors.API_RATE_LIMIT
) {
dispatch.set({ errorDueToAuth: true })
} else if (err.message === errors.CONNECTION_BLOCKED) {
const { props } = dispatch.get()
if (props.configContext.value.accessToken) {
dispatch.call(setError, `Cannot connect to ${platformName}.`)
} else {
dispatch.set({ errorDueToAuth: true })
}
} else if (err.message === errors.SERVER_FAULT) {
dispatch.call(setError, `${platformName} server went down.`)
} else {
DOMHelper.markGitakoReadyState(false)
dispatch.call(setError, 'Some thing went wrong.')
throw err
}
}
export const toggleShowSideBar: BoundMethodCreator = dispatch => () => {
const {
state: { shouldShow },
props: { configContext },
} = dispatch.get()
dispatch.call(setShouldShow, !shouldShow)
const {
value: { intelligentToggle },
} = configContext
if (intelligentToggle !== null) {
configContext.onChange({ intelligentToggle: !shouldShow })
}
}
export const setShouldShow: BoundMethodCreator<
[ConnectorState['shouldShow']]
> = dispatch => shouldShow => {
dispatch.set({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : undefined)
DOMHelper.setBodyIndent(shouldShow)
}
export const setError: BoundMethodCreator<[ConnectorState['error']]> = dispatch => error => {
dispatch.set({ error })
dispatch.call(setShouldShow, false)
}
export const toggleShowSettings: BoundMethodCreator = dispatch => () =>
dispatch.set(({ showSettings }) => ({
showSettings: !showSettings,
}))

View file

@ -4,8 +4,5 @@ import {
ConnectorState as FileExplorerConnectorState,
Props as FileExplorerProps,
} from './FileExplorer'
import * as SideBar from './SideBar'
import { ConnectorState as SideBarConnectorState, Props as SideBarProps } from './SideBar'
export const FileExplorerCore: Sources<FileExplorerProps, FileExplorerConnectorState> = FileExplorer
export const SideBarCore: Sources<SideBarProps, SideBarConnectorState> = SideBar

4
src/global.d.ts vendored
View file

@ -1,10 +1,13 @@
type MetaData = {
userName: string
repoName: string
defaultBranchName: string
branchName: string
type?: EnumString<'tree' | 'blob' | 'pull'>
}
type PartialMetaData = Omit<MakeOptional<MetaData, 'branchName'>, 'defaultBranchName'>
type TreeNode = {
name: string
contents?: TreeNode[]
@ -13,6 +16,7 @@ type TreeNode = {
url?: string
sha?: string
accessDenied?: boolean
comments?: number
}
type IO<T, ChangeT = T> = {

View file

@ -106,6 +106,16 @@ export async function getPullTreeData(
return await request(url, { accessToken })
}
export async function getPullComments(
userName: string,
repoName: string,
pullId: string,
accessToken?: string,
): Promise<GitHubAPI.PullComments> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/pulls/${pullId}/comments`
return await request(url, { accessToken })
}
export async function getPullPageDocument(
userName: string,
repoName: string,

View file

@ -43,7 +43,7 @@ export function CopyFileButton(props: React.PropsWithChildren<Props>) {
}, [])
return (
<a ref={elementRef} className={cx('btn btn-sm BtnGroup-item copy-file-btn', className)}>
<a ref={elementRef} className={cx('btn btn-sm BtnGroup-item', className)}>
{content}
</a>
)

View file

@ -44,8 +44,9 @@ export function getCurrentBranch(passive = false) {
if (branchButtonElement) {
const branchNameSpanElement = branchButtonElement.querySelector('span')
if (branchNameSpanElement) {
const partialBranchNameFromInnerText = branchNameSpanElement.innerText
if (!partialBranchNameFromInnerText.includes('…')) return partialBranchNameFromInnerText
const partialBranchNameFromInnerText = branchNameSpanElement.textContent || ''
if (partialBranchNameFromInnerText && !partialBranchNameFromInnerText.includes('…'))
return partialBranchNameFromInnerText
}
const defaultTitle = 'Switch branches or tags'
const title = branchButtonElement.title.trim()
@ -139,6 +140,13 @@ export function getCodeElement() {
* click these buttons will copy file content to clipboard
*/
export function attachCopyFileBtn() {
const removeButtons = () => {
const buttons = document.querySelectorAll(`.${copyFileButtonClassName}`)
buttons.forEach(button => {
button.parentElement?.removeChild(button)
})
}
if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) {
// the button group in file content header
const buttonGroupSelector = '.repository-content .Box-header .BtnGroup'
@ -148,6 +156,8 @@ export function attachCopyFileBtn() {
raiseError(new Error(`No button groups found`))
}
removeButtons() // prevent duplicated buttons
buttonGroups.forEach(async buttonGroup => {
if (!buttonGroup.lastElementChild) return
const button = await renderReact(React.createElement(CopyFileButton))
@ -158,12 +168,7 @@ export function attachCopyFileBtn() {
}
// return callback so that disabling after redirecting from file page to non-page works properly
return () => {
const buttons = document.querySelectorAll(`.${copyFileButtonClassName}`)
buttons.forEach(button => {
button.parentElement?.removeChild(button)
})
}
return removeButtons
}
export function attachCopySnippet() {
@ -228,3 +233,22 @@ export function attachCopySnippet() {
)
})
}
export function getPath() {
const folderPathElementSelector = '.file-navigation .position-relative' // available when in path like '/tree/...'
const blobPathElementSelector = '#blob-path' // available when in path like '/blob/...'
const pathElement =
document.querySelector(blobPathElementSelector) ||
document.querySelector(folderPathElementSelector)?.nextElementSibling
if (!pathElement?.querySelector('.js-repo-root')) {
return []
}
const path = ((pathElement as HTMLDivElement).textContent || '')
.replace(/\n/g, '')
.replace(/\/\s+Jump to.*/m, '')
.trim()
.split('/')
.filter(Boolean)
.slice(1) // the first is the repo's name
return path
}

View file

@ -35,7 +35,19 @@ declare namespace GitHubAPI {
changed_files: number
}
type PullComment = {
path: string
pull_request_review_id: number
id: number
node_id: string
diff_hunk: string
body: string
html_url: string
author_association: string
}
type PullTreeData = PullTreeItem[]
type PullComments = PullComment[]
type MetaData = {
name: string

View file

@ -0,0 +1,17 @@
import { platform } from 'platforms'
import * as React from 'react'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import * as DOMHelper from '../DOMHelper'
import { GitHub } from '../index'
export function useGitHubAttachCopyFileButton(copyFileButton: boolean) {
const attachCopyFileButton = React.useCallback(
function attachCopyFileButton() {
if (platform !== GitHub) return
if (copyFileButton) return DOMHelper.attachCopyFileBtn() || undefined // for the sake of react effect
},
[copyFileButton],
)
React.useEffect(attachCopyFileButton, [copyFileButton])
useOnPJAXDone(attachCopyFileButton)
}

View file

@ -0,0 +1,17 @@
import { platform } from 'platforms'
import * as React from 'react'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import * as DOMHelper from '../DOMHelper'
import { GitHub } from '../index'
export function useGitHubAttachCopySnippetButton(copySnippetButton: boolean) {
const attachCopySnippetButton = React.useCallback(
function attachCopySnippetButton() {
if (platform !== GitHub) return
if (copySnippetButton) return DOMHelper.attachCopySnippet() || undefined // for the sake of react effect
},
[copySnippetButton],
)
React.useEffect(attachCopySnippetButton, [copySnippetButton])
useOnPJAXDone(attachCopySnippetButton)
}

View file

@ -0,0 +1,162 @@
import { platform } from 'platforms'
import { useCallback, useEffect } from 'react'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import { GitHub } from '..'
const theCSSClassMark = 'gitako-code-fold-attached'
const theCSSClassMarkWhenDisabled = 'gitako-code-fold-attached-disabled'
const theCSSClassForHiddenLine = 'gitako-code-fold-hidden'
const theCSSClassForToggleElement = 'gitako-code-fold-handler'
const tableSelector = `.blob-wrapper table`
const selectorOfLineNumber = `.blob-num`
const selectorOfLineContent = `.blob-code`
const theCSSClassForToggleElementOnActive = 'active'
function init() {
type LineNumber = number // alias
const blocks: LineNumber[] = [] // startLine -> exclusiveEndLine
/**
* For example, given such input
*
* 0 | line1
* 1 | line2
* 2 |
* 3 | line3
* 4 | line4
* 5 | line5
*
* blocks will be
* 0 -> 5
* 1 -> 3
* 2
* 3 -> 5
* 4
* 5
*/
const table = document.querySelector(tableSelector)
const lineElements = Array.from(document.querySelectorAll([tableSelector, 'tr'].join(' ')))
if (!table || !lineElements.length) return
if (table.classList.contains(theCSSClassMark)) {
if (table.classList.contains(theCSSClassMarkWhenDisabled)) {
// reactivate
table.classList.remove(theCSSClassMarkWhenDisabled)
}
return
}
table.classList.add(theCSSClassMark)
// setup blocks
{
type Level = number // measured by leading whitespace amount
const stack: [Level, LineNumber][] = []
function trySeal(lineNumber: number, level: number) {
let ignoredTheHighestLevelItem = false
while (stack.length) {
const top = stack.pop()! // safe
const [$LineNumber, $level] = top
if ($level < level) {
stack.push(top)
break
} else if (!ignoredTheHighestLevelItem) {
ignoredTheHighestLevelItem = true
} else {
blocks[$LineNumber] = lineNumber
}
}
stack.push([lineNumber, level])
}
lineElements.forEach((element, lineNumber) => {
const text = element.querySelector(selectorOfLineContent)?.textContent || null
if (text === null) return
const level = getTextLevel(text)
if (level === -1) return
trySeal(lineNumber, level)
})
trySeal(lineElements.length, -1)
}
// attach toggle buttons
blocks.forEach((end, line) => {
if (!end) return
const toggleElement = document.createElement('div')
toggleElement.setAttribute('role', 'button')
toggleElement.classList.add(theCSSClassForToggleElement)
const lineNumberElement = lineElements[line].querySelector(selectorOfLineNumber)
lineNumberElement?.appendChild(toggleElement)
})
const foldLines = new Set<LineNumber>()
function toggleLine(line: number) {
// const lineElements = Array.from(document.querySelectorAll(lineSelector))
const element = lineElements[line]
element.classList.toggle(theCSSClassForToggleElementOnActive)
if (foldLines.has(line)) {
foldLines.delete(line)
} else {
foldLines.add(line)
}
const linesToHide = new Set<LineNumber>()
for (const line of foldLines.values()) {
const end = blocks[line]
for (let $line = line + 1; $line < end; $line++) linesToHide.add($line)
}
lineElements.forEach((element, i) => {
if (linesToHide.has(i)) element.classList.add(theCSSClassForHiddenLine)
else element.classList.remove(theCSSClassForHiddenLine)
})
}
table.addEventListener('click', e => {
if (e.target instanceof HTMLElement) {
if (e.target.classList.contains(theCSSClassForToggleElement)) {
const tr = e.target.parentElement?.parentElement
if (tr) {
toggleLine(lineElements.indexOf(tr))
e.stopPropagation()
}
}
}
})
}
function cancelToggleFeature() {
const table = document.querySelector(tableSelector)
table?.classList.add(theCSSClassMarkWhenDisabled)
}
function getTextLevel(text: string) {
const leading = countLeadingWhitespace(text)
return leading === text.length ? -1 : leading
}
function countLeadingWhitespace(text: string) {
let i = 0
for (; i < text.length; i++) {
// Mixed usage of space and table indentation? ¯\_(ツ)_/¯
if (!(text[i] === ' ' || text[i] === '\t' || text[i] === '\n')) break
}
return i
}
export function useGitHubCodeFold(active: boolean) {
const effect = useCallback(() => {
if (platform !== GitHub) return
if (active) {
init()
return () => cancelToggleFeature()
}
}, [active])
useEffect(effect, [effect])
useOnPJAXDone(effect)
}

View file

@ -1,13 +1,15 @@
import { useConfigs } from 'containers/ConfigsContext'
import { GITHUB_OAUTH } from 'env'
import { Base64 } from 'js-base64'
import { platform } from 'platforms'
import * as React from 'react'
import { resolveGitModules } from 'utils/gitSubmodule'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import { sortFoldersToFront } from 'utils/treeParser'
import * as API from './API'
import * as DOMHelper from './DOMHelper'
import { useGitHubAttachCopyFileButton } from './hooks/useGitHubAttachCopyFileButton'
import { useGitHubAttachCopySnippetButton } from './hooks/useGitHubAttachCopySnippetButton'
import { useGitHubCodeFold } from './hooks/useGitHubCodeFold'
import * as URLHelper from './URLHelper'
export { useGitHubCodeFold } from './hooks/useGitHubCodeFold'
function processTree(tree: TreeNode[]): TreeNode {
// nodes are created from items and put onto tree
@ -70,9 +72,7 @@ function getUrlForRedirect(
// Modern browsers have great support for handling unsafe URL,
// It may be possible to sanitize path with
// `path => path.includes('#') ? path.replace(/#/g, '%23') : '...'
return `https://${
window.location.host
}/${userName}/${repoName}/${type}/${branchName}/${path
return `https://${window.location.host}/${userName}/${repoName}/${type}/${branchName}/${path
.split('/')
.map(encodeURIComponent)
.join('/')}`
@ -82,11 +82,32 @@ export function isEnterprise() {
return !window.location.host.endsWith('github.com')
}
function resolvePageScope(defaultBranchName?: string) {
const parsed = URLHelper.parse()
switch (parsed.type) {
case 'blob':
case 'tree': {
// handle URLs like {user}/{repo}/{'tree'|'blob'}/{sha|branch}, issue #131
const branchName = DOMHelper.getCurrentBranch()
if (branchName && branchName !== defaultBranchName) return `branch-${branchName}`
break
}
case 'tags':
return 'tags'
case 'releases':
return 'releases'
case 'pull':
const pullId = URLHelper.isInPullPage()
if (pullId) return `pull-${pullId}`
}
return 'general'
}
const pathSHAMap = new Map<string, string>()
export const GitHub: Platform = {
isEnterprise,
resolveMeta() {
resolvePartialMetaData() {
if (!DOMHelper.isInRepoPage()) {
return null
}
@ -119,8 +140,7 @@ export const GitHub: Platform = {
return metaData
},
async getDefaultBranchName({ userName, repoName }, accessToken) {
const data = await API.getRepoMeta(userName, repoName, accessToken)
return data.default_branch
return (await API.getRepoMeta(userName, repoName, accessToken)).default_branch
},
resolveUrlFromMetaData({ userName, repoName }) {
return {
@ -140,9 +160,10 @@ export const GitHub: Platform = {
GITHUB_API_RESPONSE_LENGTH_LIMIT / GITHUB_API_PAGED_RESPONSE_LENGTH_LIMIT,
)
let page = 1
const [pullData, treeData] = await Promise.all([
const [pullData, treeData, commentData] = await Promise.all([
API.getPullData(userName, repoName, pullId, accessToken),
API.getPullTreeData(userName, repoName, pullId, page, accessToken),
API.getPullComments(userName, repoName, pullId, accessToken),
])
const count = pullData.changed_files
@ -170,6 +191,7 @@ export const GitHub: Platform = {
window.location.search
}#${creator(item.filename) || ''}`,
sha: item.sha,
comments: commentData?.filter(comment => item.filename === comment.path).length,
}))
const root = processTree(nodes)
@ -235,7 +257,11 @@ export const GitHub: Platform = {
return Boolean(URLHelper.isInPullPage())
},
getCurrentPath(branchName) {
return URLHelper.getCurrentPath(branchName)
if (URLHelper.parse().path.length) {
return DOMHelper.getPath()
} else {
return []
}
},
setOAuth(code) {
return API.OAuth(code)
@ -248,6 +274,12 @@ export const GitHub: Platform = {
})
return `https://github.com/login/oauth/authorize?` + params.toString()
},
usePlatformHooks() {
const { copyFileButton, copySnippetButton, codeFolding } = useConfigs().value
useGitHubAttachCopyFileButton(copyFileButton)
useGitHubAttachCopySnippetButton(copySnippetButton)
useGitHubCodeFold(codeFolding)
},
}
async function createPullFileResolver(userName: string, repoName: string, pullId: string) {
@ -258,54 +290,3 @@ async function createPullFileResolver(userName: string, repoName: string, pullId
return id
}
}
function findMissingFolders(nodes: TreeNode[]) {
const folders = new Set<string>()
const foundFolders = new Set<string>()
for (const node of nodes) {
let path = node.path
if (node.type === 'tree') foundFolders.add(path)
else {
while (true) {
// 'a/b' -> 'a'
// 'a' -> ''
path = path.substring(0, path.lastIndexOf('/'))
if (path === '') break
folders.add(path)
}
}
}
const missingFolders: string[] = []
for (const folder of folders) {
if (!foundFolders.has(folder)) {
missingFolders.push(folder)
}
}
return missingFolders
}
export function useGitHubAttachCopySnippetButton(copySnippetButton: boolean) {
const attachCopySnippetButton = React.useCallback(
function attachCopySnippetButton() {
if (platform !== GitHub) return
if (copySnippetButton) return DOMHelper.attachCopySnippet() || undefined // for the sake of react effect
},
[copySnippetButton],
)
React.useEffect(attachCopySnippetButton, [copySnippetButton])
useOnPJAXDone(attachCopySnippetButton)
}
export function useGitHubAttachCopyFileButton(copyFileButton: boolean) {
const attachCopyFileButton = React.useCallback(
function attachCopyFileButton() {
if (platform !== GitHub) return
if (copyFileButton) return DOMHelper.attachCopyFileBtn() || undefined // for the sake of react effect
},
[copyFileButton],
)
React.useEffect(attachCopyFileButton, [copyFileButton])
useOnPJAXDone(attachCopyFileButton)
}

View file

@ -57,14 +57,14 @@ async function request(
}
}
export const API_ENDPOINT = `${window.location.host}/api/v1`
export const API_ENDPOINT = `${window.location.protocol}//${window.location.host}/api/v1`
export async function getRepoMeta(
userName: string,
repoName: string,
accessToken?: string,
): Promise<GiteaAPI.MetaData> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}`
return await request(url, { accessToken })
}
@ -78,7 +78,7 @@ export async function getTreeData(
const search = new URLSearchParams()
if (recursive) search.set('recursive', '1')
const url =
`https://${API_ENDPOINT}/repos/${userName}/${repoName}/git/trees/${branchName}?` + search
`${API_ENDPOINT}/repos/${userName}/${repoName}/git/trees/${branchName}?` + search
return await request(url, { accessToken })
}
@ -88,7 +88,7 @@ export async function getBlobData(
sha: string,
accessToken?: string,
): Promise<GitHubAPI.BlobData> {
const url = `https://${API_ENDPOINT}/repos/${userName}/${repoName}/git/blobs/${sha}`
const url = `${API_ENDPOINT}/repos/${userName}/${repoName}/git/blobs/${sha}`
return await request(url, { accessToken })
}

View file

@ -65,7 +65,7 @@ function getUrlForRedirect(
// Modern browsers have great support for handling unsafe URL,
// It may be possible to sanitize path with
// `path => path.includes('#') ? path.replace(/#/g, '%23') : '...'
return `https://${
return `${window.location.protocol}//${
window.location.host
}/${userName}/${repoName}/src/branch/${branchName}/${path
.split('/')
@ -77,7 +77,7 @@ export const Gitea: Platform = {
isEnterprise() {
return !window.location.host.endsWith('gitea.com')
},
resolveMeta() {
resolvePartialMetaData() {
if (!DOMHelper.isInRepoPage()) {
return null
}
@ -107,8 +107,8 @@ export const Gitea: Platform = {
},
resolveUrlFromMetaData({ userName, repoName }) {
return {
repoUrl: `https://${window.location.host}/${userName}/${repoName}`,
userUrl: `https://${window.location.host}/${userName}`,
repoUrl: `${window.location.protocol}//${window.location.host}/${userName}/${repoName}`,
userUrl: `${window.location.protocol}//${window.location.host}/${userName}`,
}
},
async getTreeData(metaData, path, recursive, accessToken) {
@ -163,6 +163,6 @@ export const Gitea: Platform = {
return API.OAuth(code)
},
getOAuthLink() {
return `https://${window.location.host}/api/v1/user/applications/oauth2`
return `${window.location.protocol}//${window.location.host}/api/v1/user/applications/oauth2`
},
}

View file

@ -73,7 +73,7 @@ export const Gitee: Platform = {
isEnterprise() {
return !window.location.host.endsWith('gitee.com')
},
resolveMeta() {
resolvePartialMetaData() {
if (!DOMHelper.isInRepoPage()) {
return null
}

View file

@ -2,7 +2,7 @@ export const dummyPlatformForTypeSafety: Platform = {
isEnterprise() {
return false
},
resolveMeta() {
resolvePartialMetaData() {
return null
},
getDefaultBranchName: dummyPlatformMethod,

View file

@ -1,11 +1,9 @@
import { dummyPlatformForTypeSafety } from './dummyPlatformForTypeSafety'
import { Gitee } from './Gitee'
import { Gitea } from './Gitea'
import { Gitee } from './Gitee'
import { GitHub } from './GitHub'
const platforms: {
[name: string]: Platform
} = {
const platforms = {
GitHub: GitHub,
Gitee: Gitee,
Gitea: Gitea,
@ -13,7 +11,7 @@ const platforms: {
function resolvePlatform() {
for (const platform of Object.values(platforms)) {
if (platform.resolveMeta()) return platform
if (platform.resolvePartialMetaData()) return platform
}
return dummyPlatformForTypeSafety
}

View file

@ -1,19 +1,18 @@
type Platform = {
isEnterprise(): boolean
// branch name might not be available when resolving from DOM and URL
resolveMeta(): MakeOptional<MetaData, 'branchName'> | null
resolvePartialMetaData(): PartialMetaData | null
// resolveMetaData(metaData: PartialMetaData, accessToken?: string): Async<MetaData>
getDefaultBranchName(
metaData: Pick<MetaData, 'userName' | 'repoName'>,
accessToken?: string,
): Promise<string>
resolveUrlFromMetaData(
metaData: MetaData,
): {
resolveUrlFromMetaData(metaData: Pick<MetaData, 'userName' | 'repoName'>): {
userUrl: string
repoUrl: string
}
getTreeData(
metaData: MetaData,
metaData: Pick<MetaData, 'userName' | 'repoName' | 'branchName'>,
path?: string,
recursive?: boolean,
accessToken?: string,
@ -23,4 +22,5 @@ type Platform = {
getCurrentPath(branchName: string): string[] | null
setOAuth(code: string): Promise<string | null>
getOAuthLink(): string
usePlatformHooks?(): void
}

View file

@ -1,68 +0,0 @@
interface Window {
ResizeObserver: ResizeObserver
}
/**
* The ResizeObserver interface is used to observe changes to Element's content
* rect.
*
* It is modeled after MutationObserver and IntersectionObserver.
*/
interface ResizeObserver {
new (callback: ResizeObserverCallback): ResizeObserver
/**
* Adds target to the list of observed elements.
*/
observe: (target: Element) => void
/**
* Removes target from the list of observed elements.
*/
unobserve: (target: Element) => void
/**
* Clears both the observationTargets and activeTargets lists.
*/
disconnect: () => void
}
/**
* This callback delivers ResizeObserver's notifications. It is invoked by a
* broadcast active observations algorithm.
*/
interface ResizeObserverCallback {
(entries: ResizeObserverEntry[], observer: ResizeObserver): void
}
interface ResizeObserverEntry {
/**
* @param target The Element whose size has changed.
*/
new (target: Element): void
/**
* The Element whose size has changed.
*/
readonly target: Element
/**
* Element's content rect when ResizeObserverCallback is invoked.
*/
readonly contentRect: DOMRectReadOnly
}
interface DOMRectReadOnly {
// static fromRect(other: DOMRectInit | undefined): DOMRectReadOnly;
readonly x: number
readonly y: number
readonly width: number
readonly height: number
readonly top: number
readonly right: number
readonly bottom: number
readonly left: number
toJSON: () => any
}

View file

@ -1,579 +0,0 @@
@use './variables.scss' as variables;
@mixin assign {
--gitako-auto-black: #{variables.$auto-black};
--gitako-auto-white: #{variables.$auto-white};
--gitako-auto-gray-0: #{variables.$auto-gray-0};
--gitako-auto-gray-1: #{variables.$auto-gray-1};
--gitako-auto-gray-2: #{variables.$auto-gray-2};
--gitako-auto-gray-3: #{variables.$auto-gray-3};
--gitako-auto-gray-4: #{variables.$auto-gray-4};
--gitako-auto-gray-5: #{variables.$auto-gray-5};
--gitako-auto-gray-6: #{variables.$auto-gray-6};
--gitako-auto-gray-7: #{variables.$auto-gray-7};
--gitako-auto-gray-8: #{variables.$auto-gray-8};
--gitako-auto-gray-9: #{variables.$auto-gray-9};
--gitako-auto-blue-0: #{variables.$auto-blue-0};
--gitako-auto-blue-1: #{variables.$auto-blue-1};
--gitako-auto-blue-2: #{variables.$auto-blue-2};
--gitako-auto-blue-3: #{variables.$auto-blue-3};
--gitako-auto-blue-4: #{variables.$auto-blue-4};
--gitako-auto-blue-5: #{variables.$auto-blue-5};
--gitako-auto-blue-6: #{variables.$auto-blue-6};
--gitako-auto-blue-7: #{variables.$auto-blue-7};
--gitako-auto-blue-8: #{variables.$auto-blue-8};
--gitako-auto-blue-9: #{variables.$auto-blue-9};
--gitako-auto-green-0: #{variables.$auto-green-0};
--gitako-auto-green-1: #{variables.$auto-green-1};
--gitako-auto-green-2: #{variables.$auto-green-2};
--gitako-auto-green-3: #{variables.$auto-green-3};
--gitako-auto-green-4: #{variables.$auto-green-4};
--gitako-auto-green-5: #{variables.$auto-green-5};
--gitako-auto-green-6: #{variables.$auto-green-6};
--gitako-auto-green-7: #{variables.$auto-green-7};
--gitako-auto-green-8: #{variables.$auto-green-8};
--gitako-auto-green-9: #{variables.$auto-green-9};
--gitako-auto-yellow-0: #{variables.$auto-yellow-0};
--gitako-auto-yellow-1: #{variables.$auto-yellow-1};
--gitako-auto-yellow-2: #{variables.$auto-yellow-2};
--gitako-auto-yellow-3: #{variables.$auto-yellow-3};
--gitako-auto-yellow-4: #{variables.$auto-yellow-4};
--gitako-auto-yellow-5: #{variables.$auto-yellow-5};
--gitako-auto-yellow-6: #{variables.$auto-yellow-6};
--gitako-auto-yellow-7: #{variables.$auto-yellow-7};
--gitako-auto-yellow-8: #{variables.$auto-yellow-8};
--gitako-auto-yellow-9: #{variables.$auto-yellow-9};
--gitako-auto-orange-0: #{variables.$auto-orange-0};
--gitako-auto-orange-1: #{variables.$auto-orange-1};
--gitako-auto-orange-2: #{variables.$auto-orange-2};
--gitako-auto-orange-3: #{variables.$auto-orange-3};
--gitako-auto-orange-4: #{variables.$auto-orange-4};
--gitako-auto-orange-5: #{variables.$auto-orange-5};
--gitako-auto-orange-6: #{variables.$auto-orange-6};
--gitako-auto-orange-7: #{variables.$auto-orange-7};
--gitako-auto-orange-8: #{variables.$auto-orange-8};
--gitako-auto-orange-9: #{variables.$auto-orange-9};
--gitako-auto-red-0: #{variables.$auto-red-0};
--gitako-auto-red-1: #{variables.$auto-red-1};
--gitako-auto-red-2: #{variables.$auto-red-2};
--gitako-auto-red-3: #{variables.$auto-red-3};
--gitako-auto-red-4: #{variables.$auto-red-4};
--gitako-auto-red-5: #{variables.$auto-red-5};
--gitako-auto-red-6: #{variables.$auto-red-6};
--gitako-auto-red-7: #{variables.$auto-red-7};
--gitako-auto-red-8: #{variables.$auto-red-8};
--gitako-auto-red-9: #{variables.$auto-red-9};
--gitako-auto-purple-0: #{variables.$auto-purple-0};
--gitako-auto-purple-1: #{variables.$auto-purple-1};
--gitako-auto-purple-2: #{variables.$auto-purple-2};
--gitako-auto-purple-3: #{variables.$auto-purple-3};
--gitako-auto-purple-4: #{variables.$auto-purple-4};
--gitako-auto-purple-5: #{variables.$auto-purple-5};
--gitako-auto-purple-6: #{variables.$auto-purple-6};
--gitako-auto-purple-7: #{variables.$auto-purple-7};
--gitako-auto-purple-8: #{variables.$auto-purple-8};
--gitako-auto-purple-9: #{variables.$auto-purple-9};
--gitako-auto-pink-0: #{variables.$auto-pink-0};
--gitako-auto-pink-1: #{variables.$auto-pink-1};
--gitako-auto-pink-2: #{variables.$auto-pink-2};
--gitako-auto-pink-3: #{variables.$auto-pink-3};
--gitako-auto-pink-4: #{variables.$auto-pink-4};
--gitako-auto-pink-5: #{variables.$auto-pink-5};
--gitako-auto-pink-6: #{variables.$auto-pink-6};
--gitako-auto-pink-7: #{variables.$auto-pink-7};
--gitako-auto-pink-8: #{variables.$auto-pink-8};
--gitako-auto-pink-9: #{variables.$auto-pink-9};
--gitako-text-primary: #{variables.$text-primary};
--gitako-text-secondary: #{variables.$text-secondary};
--gitako-text-tertiary: #{variables.$text-tertiary};
--gitako-text-placeholder: #{variables.$text-placeholder};
--gitako-text-disabled: #{variables.$text-disabled};
--gitako-text-inverse: #{variables.$text-inverse};
--gitako-text-link: #{variables.$text-link};
--gitako-text-danger: #{variables.$text-danger};
--gitako-text-success: #{variables.$text-success};
--gitako-text-warning: #{variables.$text-warning};
--gitako-text-white: #{variables.$text-white};
--gitako-icon-primary: #{variables.$icon-primary};
--gitako-icon-secondary: #{variables.$icon-secondary};
--gitako-icon-tertiary: #{variables.$icon-tertiary};
--gitako-icon-info: #{variables.$icon-info};
--gitako-icon-danger: #{variables.$icon-danger};
--gitako-icon-success: #{variables.$icon-success};
--gitako-icon-warning: #{variables.$icon-warning};
--gitako-border-primary: #{variables.$border-primary};
--gitako-border-secondary: #{variables.$border-secondary};
--gitako-border-tertiary: #{variables.$border-tertiary};
--gitako-border-overlay: #{variables.$border-overlay};
--gitako-border-inverse: #{variables.$border-inverse};
--gitako-border-info: #{variables.$border-info};
--gitako-border-danger: #{variables.$border-danger};
--gitako-border-success: #{variables.$border-success};
--gitako-border-warning: #{variables.$border-warning};
--gitako-bg-canvas: #{variables.$bg-canvas};
--gitako-bg-canvas-mobile: #{variables.$bg-canvas-mobile};
--gitako-bg-canvas-inverse: #{variables.$bg-canvas-inverse};
--gitako-bg-canvas-inset: #{variables.$bg-canvas-inset};
--gitako-bg-primary: #{variables.$bg-primary};
--gitako-bg-secondary: #{variables.$bg-secondary};
--gitako-bg-tertiary: #{variables.$bg-tertiary};
--gitako-bg-overlay: #{variables.$bg-overlay};
--gitako-bg-backdrop: #{variables.$bg-backdrop};
--gitako-bg-info: #{variables.$bg-info};
--gitako-bg-info-inverse: #{variables.$bg-info-inverse};
--gitako-bg-danger: #{variables.$bg-danger};
--gitako-bg-danger-inverse: #{variables.$bg-danger-inverse};
--gitako-bg-success: #{variables.$bg-success};
--gitako-bg-success-inverse: #{variables.$bg-success-inverse};
--gitako-bg-warning: #{variables.$bg-warning};
--gitako-bg-warning-inverse: #{variables.$bg-warning-inverse};
--gitako-shadow-small: #{variables.$shadow-small};
--gitako-shadow-medium: #{variables.$shadow-medium};
--gitako-shadow-large: #{variables.$shadow-large};
--gitako-shadow-extra-large: #{variables.$shadow-extra-large};
--gitako-shadow-highlight: #{variables.$shadow-highlight};
--gitako-shadow-inset: #{variables.$shadow-inset};
--gitako-state-hover-primary-bg: #{variables.$state-hover-primary-bg};
--gitako-state-hover-primary-border: #{variables.$state-hover-primary-border};
--gitako-state-hover-primary-text: #{variables.$state-hover-primary-text};
--gitako-state-hover-primary-icon: #{variables.$state-hover-primary-icon};
--gitako-state-hover-secondary-bg: #{variables.$state-hover-secondary-bg};
--gitako-state-hover-secondary-border: #{variables.$state-hover-secondary-border};
--gitako-state-selected-primary-bg: #{variables.$state-selected-primary-bg};
--gitako-state-selected-primary-border: #{variables.$state-selected-primary-border};
--gitako-state-selected-primary-text: #{variables.$state-selected-primary-text};
--gitako-state-selected-primary-icon: #{variables.$state-selected-primary-icon};
--gitako-state-focus-border: #{variables.$state-focus-border};
--gitako-state-focus-shadow: #{variables.$state-focus-shadow};
--gitako-fade-fg-10: #{variables.$fade-fg-10};
--gitako-fade-fg-15: #{variables.$fade-fg-15};
--gitako-fade-fg-30: #{variables.$fade-fg-30};
--gitako-fade-fg-50: #{variables.$fade-fg-50};
--gitako-fade-fg-70: #{variables.$fade-fg-70};
--gitako-fade-fg-85: #{variables.$fade-fg-85};
--gitako-fade-black-10: #{variables.$fade-black-10};
--gitako-fade-black-15: #{variables.$fade-black-15};
--gitako-fade-black-30: #{variables.$fade-black-30};
--gitako-fade-black-50: #{variables.$fade-black-50};
--gitako-fade-black-70: #{variables.$fade-black-70};
--gitako-fade-black-85: #{variables.$fade-black-85};
--gitako-fade-white-10: #{variables.$fade-white-10};
--gitako-fade-white-15: #{variables.$fade-white-15};
--gitako-fade-white-30: #{variables.$fade-white-30};
--gitako-fade-white-50: #{variables.$fade-white-50};
--gitako-fade-white-70: #{variables.$fade-white-70};
--gitako-fade-white-85: #{variables.$fade-white-85};
--gitako-alert-info-text: #{variables.$alert-info-text};
--gitako-alert-info-icon: #{variables.$alert-info-icon};
--gitako-alert-info-bg: #{variables.$alert-info-bg};
--gitako-alert-info-border: #{variables.$alert-info-border};
--gitako-alert-warn-text: #{variables.$alert-warn-text};
--gitako-alert-warn-icon: #{variables.$alert-warn-icon};
--gitako-alert-warn-bg: #{variables.$alert-warn-bg};
--gitako-alert-warn-border: #{variables.$alert-warn-border};
--gitako-alert-error-text: #{variables.$alert-error-text};
--gitako-alert-error-icon: #{variables.$alert-error-icon};
--gitako-alert-error-bg: #{variables.$alert-error-bg};
--gitako-alert-error-border: #{variables.$alert-error-border};
--gitako-alert-success-text: #{variables.$alert-success-text};
--gitako-alert-success-icon: #{variables.$alert-success-icon};
--gitako-alert-success-bg: #{variables.$alert-success-bg};
--gitako-alert-success-border: #{variables.$alert-success-border};
--gitako-autocomplete-shadow: #{variables.$autocomplete-shadow};
--gitako-autocomplete-row-border: #{variables.$autocomplete-row-border};
--gitako-blankslate-icon: #{variables.$blankslate-icon};
--gitako-btn-text: #{variables.$btn-text};
--gitako-btn-bg: #{variables.$btn-bg};
--gitako-btn-border: #{variables.$btn-border};
--gitako-btn-shadow: #{variables.$btn-shadow};
--gitako-btn-inset-shadow: #{variables.$btn-inset-shadow};
--gitako-btn-hover-bg: #{variables.$btn-hover-bg};
--gitako-btn-hover-border: #{variables.$btn-hover-border};
--gitako-btn-selected-bg: #{variables.$btn-selected-bg};
--gitako-btn-focus-bg: #{variables.$btn-focus-bg};
--gitako-btn-focus-border: #{variables.$btn-focus-border};
--gitako-btn-focus-shadow: #{variables.$btn-focus-shadow};
--gitako-btn-shadow-active: #{variables.$btn-shadow-active};
--gitako-btn-shadow-input-focus: #{variables.$btn-shadow-input-focus};
--gitako-btn-primary-text: #{variables.$btn-primary-text};
--gitako-btn-primary-bg: #{variables.$btn-primary-bg};
--gitako-btn-primary-border: #{variables.$btn-primary-border};
--gitako-btn-primary-shadow: #{variables.$btn-primary-shadow};
--gitako-btn-primary-inset-shadow: #{variables.$btn-primary-inset-shadow};
--gitako-btn-primary-hover-bg: #{variables.$btn-primary-hover-bg};
--gitako-btn-primary-hover-border: #{variables.$btn-primary-hover-border};
--gitako-btn-primary-selected-bg: #{variables.$btn-primary-selected-bg};
--gitako-btn-primary-selected-shadow: #{variables.$btn-primary-selected-shadow};
--gitako-btn-primary-disabled-text: #{variables.$btn-primary-disabled-text};
--gitako-btn-primary-disabled-bg: #{variables.$btn-primary-disabled-bg};
--gitako-btn-primary-disabled-border: #{variables.$btn-primary-disabled-border};
--gitako-btn-primary-focus-bg: #{variables.$btn-primary-focus-bg};
--gitako-btn-primary-focus-border: #{variables.$btn-primary-focus-border};
--gitako-btn-primary-focus-shadow: #{variables.$btn-primary-focus-shadow};
--gitako-btn-primary-icon: #{variables.$btn-primary-icon};
--gitako-btn-primary-counter-bg: #{variables.$btn-primary-counter-bg};
--gitako-btn-outline-text: #{variables.$btn-outline-text};
--gitako-btn-outline-hover-text: #{variables.$btn-outline-hover-text};
--gitako-btn-outline-hover-bg: #{variables.$btn-outline-hover-bg};
--gitako-btn-outline-hover-border: #{variables.$btn-outline-hover-border};
--gitako-btn-outline-hover-shadow: #{variables.$btn-outline-hover-shadow};
--gitako-btn-outline-hover-inset-shadow: #{variables.$btn-outline-hover-inset-shadow};
--gitako-btn-outline-hover-counter-bg: #{variables.$btn-outline-hover-counter-bg};
--gitako-btn-outline-selected-text: #{variables.$btn-outline-selected-text};
--gitako-btn-outline-selected-bg: #{variables.$btn-outline-selected-bg};
--gitako-btn-outline-selected-border: #{variables.$btn-outline-selected-border};
--gitako-btn-outline-selected-shadow: #{variables.$btn-outline-selected-shadow};
--gitako-btn-outline-disabled-text: #{variables.$btn-outline-disabled-text};
--gitako-btn-outline-disabled-bg: #{variables.$btn-outline-disabled-bg};
--gitako-btn-outline-disabled-counter-bg: #{variables.$btn-outline-disabled-counter-bg};
--gitako-btn-outline-focus-border: #{variables.$btn-outline-focus-border};
--gitako-btn-outline-focus-shadow: #{variables.$btn-outline-focus-shadow};
--gitako-btn-outline-counter-bg: #{variables.$btn-outline-counter-bg};
--gitako-btn-danger-text: #{variables.$btn-danger-text};
--gitako-btn-danger-hover-text: #{variables.$btn-danger-hover-text};
--gitako-btn-danger-hover-bg: #{variables.$btn-danger-hover-bg};
--gitako-btn-danger-hover-border: #{variables.$btn-danger-hover-border};
--gitako-btn-danger-hover-shadow: #{variables.$btn-danger-hover-shadow};
--gitako-btn-danger-hover-inset-shadow: #{variables.$btn-danger-hover-inset-shadow};
--gitako-btn-danger-hover-counter-bg: #{variables.$btn-danger-hover-counter-bg};
--gitako-btn-danger-selected-text: #{variables.$btn-danger-selected-text};
--gitako-btn-danger-selected-bg: #{variables.$btn-danger-selected-bg};
--gitako-btn-danger-selected-border: #{variables.$btn-danger-selected-border};
--gitako-btn-danger-selected-shadow: #{variables.$btn-danger-selected-shadow};
--gitako-btn-danger-disabled-text: #{variables.$btn-danger-disabled-text};
--gitako-btn-danger-disabled-bg: #{variables.$btn-danger-disabled-bg};
--gitako-btn-danger-disabled-counter-bg: #{variables.$btn-danger-disabled-counter-bg};
--gitako-btn-danger-focus-border: #{variables.$btn-danger-focus-border};
--gitako-btn-danger-focus-shadow: #{variables.$btn-danger-focus-shadow};
--gitako-btn-danger-counter-bg: #{variables.$btn-danger-counter-bg};
--gitako-btn-counter-bg: #{variables.$btn-counter-bg};
--gitako-counter-text: #{variables.$counter-text};
--gitako-counter-bg: #{variables.$counter-bg};
--gitako-counter-primary-text: #{variables.$counter-primary-text};
--gitako-counter-primary-bg: #{variables.$counter-primary-bg};
--gitako-counter-secondary-text: #{variables.$counter-secondary-text};
--gitako-dropdown-shadow: #{variables.$dropdown-shadow};
--gitako-label-border: #{variables.$label-border};
--gitako-label-primary-text: #{variables.$label-primary-text};
--gitako-label-primary-border: #{variables.$label-primary-border};
--gitako-label-secondary-text: #{variables.$label-secondary-text};
--gitako-label-secondary-border: #{variables.$label-secondary-border};
--gitako-label-info-text: #{variables.$label-info-text};
--gitako-label-info-border: #{variables.$label-info-border};
--gitako-label-success-text: #{variables.$label-success-text};
--gitako-label-success-border: #{variables.$label-success-border};
--gitako-label-warning-text: #{variables.$label-warning-text};
--gitako-label-warning-border: #{variables.$label-warning-border};
--gitako-label-danger-text: #{variables.$label-danger-text};
--gitako-label-danger-border: #{variables.$label-danger-border};
--gitako-label-orange-text: #{variables.$label-orange-text};
--gitako-label-orange-border: #{variables.$label-orange-border};
--gitako-input-bg: #{variables.$input-bg};
--gitako-input-contrast-bg: #{variables.$input-contrast-bg};
--gitako-input-border: #{variables.$input-border};
--gitako-input-shadow: #{variables.$input-shadow};
--gitako-input-disabled-bg: #{variables.$input-disabled-bg};
--gitako-input-disabled-border: #{variables.$input-disabled-border};
--gitako-input-warning-border: #{variables.$input-warning-border};
--gitako-input-error-border: #{variables.$input-error-border};
--gitako-input-tooltip-success-text: #{variables.$input-tooltip-success-text};
--gitako-input-tooltip-success-bg: #{variables.$input-tooltip-success-bg};
--gitako-input-tooltip-success-border: #{variables.$input-tooltip-success-border};
--gitako-input-tooltip-warning-text: #{variables.$input-tooltip-warning-text};
--gitako-input-tooltip-warning-bg: #{variables.$input-tooltip-warning-bg};
--gitako-input-tooltip-warning-border: #{variables.$input-tooltip-warning-border};
--gitako-input-tooltip-error-text: #{variables.$input-tooltip-error-text};
--gitako-input-tooltip-error-bg: #{variables.$input-tooltip-error-bg};
--gitako-input-tooltip-error-border: #{variables.$input-tooltip-error-border};
--gitako-avatar-bg: #{variables.$avatar-bg};
--gitako-avatar-border: #{variables.$avatar-border};
--gitako-avatar-stack-fade: #{variables.$avatar-stack-fade};
--gitako-avatar-stack-fade-more: #{variables.$avatar-stack-fade-more};
--gitako-avatar-child-shadow: #{variables.$avatar-child-shadow};
--gitako-toast-text: #{variables.$toast-text};
--gitako-toast-bg: #{variables.$toast-bg};
--gitako-toast-border: #{variables.$toast-border};
--gitako-toast-shadow: #{variables.$toast-shadow};
--gitako-toast-icon: #{variables.$toast-icon};
--gitako-toast-icon-bg: #{variables.$toast-icon-bg};
--gitako-toast-icon-border: #{variables.$toast-icon-border};
--gitako-toast-success-text: #{variables.$toast-success-text};
--gitako-toast-success-border: #{variables.$toast-success-border};
--gitako-toast-success-icon: #{variables.$toast-success-icon};
--gitako-toast-success-icon-bg: #{variables.$toast-success-icon-bg};
--gitako-toast-success-icon-border: #{variables.$toast-success-icon-border};
--gitako-toast-warning-text: #{variables.$toast-warning-text};
--gitako-toast-warning-border: #{variables.$toast-warning-border};
--gitako-toast-warning-icon: #{variables.$toast-warning-icon};
--gitako-toast-warning-icon-bg: #{variables.$toast-warning-icon-bg};
--gitako-toast-warning-icon-border: #{variables.$toast-warning-icon-border};
--gitako-toast-danger-text: #{variables.$toast-danger-text};
--gitako-toast-danger-border: #{variables.$toast-danger-border};
--gitako-toast-danger-icon: #{variables.$toast-danger-icon};
--gitako-toast-danger-icon-bg: #{variables.$toast-danger-icon-bg};
--gitako-toast-danger-icon-border: #{variables.$toast-danger-icon-border};
--gitako-toast-loading-text: #{variables.$toast-loading-text};
--gitako-toast-loading-border: #{variables.$toast-loading-border};
--gitako-toast-loading-icon: #{variables.$toast-loading-icon};
--gitako-toast-loading-icon-bg: #{variables.$toast-loading-icon-bg};
--gitako-toast-loading-icon-border: #{variables.$toast-loading-icon-border};
--gitako-timeline-text: #{variables.$timeline-text};
--gitako-timeline-badge-bg: #{variables.$timeline-badge-bg};
--gitako-timeline-target-badge-border: #{variables.$timeline-target-badge-border};
--gitako-timeline-target-badge-shadow: #{variables.$timeline-target-badge-shadow};
--gitako-select-menu-border-secondary: #{variables.$select-menu-border-secondary};
--gitako-select-menu-shadow: #{variables.$select-menu-shadow};
--gitako-select-menu-backdrop-bg: #{variables.$select-menu-backdrop-bg};
--gitako-select-menu-backdrop-border: #{variables.$select-menu-backdrop-border};
--gitako-select-menu-tap-highlight: #{variables.$select-menu-tap-highlight};
--gitako-select-menu-tap-focus-bg: #{variables.$select-menu-tap-focus-bg};
--gitako-box-blue-border: #{variables.$box-blue-border};
--gitako-box-row-yellow-bg: #{variables.$box-row-yellow-bg};
--gitako-box-row-blue-bg: #{variables.$box-row-blue-bg};
--gitako-box-header-blue-bg: #{variables.$box-header-blue-bg};
--gitako-box-header-blue-border: #{variables.$box-header-blue-border};
--gitako-box-border-info: #{variables.$box-border-info};
--gitako-box-bg-info: #{variables.$box-bg-info};
--gitako-box-border-warning: #{variables.$box-border-warning};
--gitako-box-bg-warning: #{variables.$box-bg-warning};
--gitako-branch-name-text: #{variables.$branch-name-text};
--gitako-branch-name-icon: #{variables.$branch-name-icon};
--gitako-branch-name-bg: #{variables.$branch-name-bg};
--gitako-branch-name-link-text: #{variables.$branch-name-link-text};
--gitako-branch-name-link-icon: #{variables.$branch-name-link-icon};
--gitako-branch-name-link-bg: #{variables.$branch-name-link-bg};
--gitako-markdown-code-bg: #{variables.$markdown-code-bg};
--gitako-markdown-frame-border: #{variables.$markdown-frame-border};
--gitako-markdown-blockquote-border: #{variables.$markdown-blockquote-border};
--gitako-markdown-table-border: #{variables.$markdown-table-border};
--gitako-markdown-table-tr-border: #{variables.$markdown-table-tr-border};
--gitako-menu-heading-text: #{variables.$menu-heading-text};
--gitako-menu-border-active: #{variables.$menu-border-active};
--gitako-menu-bg-active: #{variables.$menu-bg-active};
--gitako-sidenav-selected-bg: #{variables.$sidenav-selected-bg};
--gitako-sidenav-border-active: #{variables.$sidenav-border-active};
--gitako-header-text: #{variables.$header-text};
--gitako-header-bg: #{variables.$header-bg};
--gitako-header-logo: #{variables.$header-logo};
--gitako-filter-item-bar-bg: #{variables.$filter-item-bar-bg};
--gitako-hidden-text-expander-bg: #{variables.$hidden-text-expander-bg};
--gitako-hidden-text-expander-bg-hover: #{variables.$hidden-text-expander-bg-hover};
--gitako-drag-and-drop-border: #{variables.$drag-and-drop-border};
--gitako-upload-enabled-border: #{variables.$upload-enabled-border};
--gitako-upload-enabled-border-focused: #{variables.$upload-enabled-border-focused};
--gitako-previewable-comment-form-border: #{variables.$previewable-comment-form-border};
--gitako-underlinenav-border: #{variables.$underlinenav-border};
--gitako-underlinenav-border-hover: #{variables.$underlinenav-border-hover};
--gitako-underlinenav-border-active: #{variables.$underlinenav-border-active};
--gitako-underlinenav-text: #{variables.$underlinenav-text};
--gitako-underlinenav-text-hover: #{variables.$underlinenav-text-hover};
--gitako-underlinenav-text-active: #{variables.$underlinenav-text-active};
--gitako-underlinenav-icon: #{variables.$underlinenav-icon};
--gitako-underlinenav-icon-hover: #{variables.$underlinenav-icon-hover};
--gitako-underlinenav-icon-active: #{variables.$underlinenav-icon-active};
--gitako-underlinenav-counter-text: #{variables.$underlinenav-counter-text};
--gitako-verified-badge-text: #{variables.$verified-badge-text};
--gitako-verified-badge-bg: #{variables.$verified-badge-bg};
--gitako-verified-badge-border: #{variables.$verified-badge-border};
--gitako-social-count-bg: #{variables.$social-count-bg};
--gitako-tooltip-text: #{variables.$tooltip-text};
--gitako-tooltip-bg: #{variables.$tooltip-bg};
--gitako-header-search-bg: #{variables.$header-search-bg};
--gitako-header-search-border: #{variables.$header-search-border};
--gitako-search-keyword-hl: #{variables.$search-keyword-hl};
--gitako-diffstat-neutral-bg: #{variables.$diffstat-neutral-bg};
--gitako-diffstat-neutral-border: #{variables.$diffstat-neutral-border};
--gitako-diffstat-deletion-bg: #{variables.$diffstat-deletion-bg};
--gitako-diffstat-deletion-border: #{variables.$diffstat-deletion-border};
--gitako-diffstat-addition-bg: #{variables.$diffstat-addition-bg};
--gitako-diffstat-addition-border: #{variables.$diffstat-addition-border};
--gitako-files-explorer-icon: #{variables.$files-explorer-icon};
--gitako-hl-author-bg: #{variables.$hl-author-bg};
--gitako-hl-author-border: #{variables.$hl-author-border};
--gitako-logo-subdued: #{variables.$logo-subdued};
--gitako-discussion-border: #{variables.$discussion-border};
--gitako-discussion-bg-success: #{variables.$discussion-bg-success};
--gitako-actions-workflow-table-sticky-bg: #{variables.$actions-workflow-table-sticky-bg};
--gitako-repo-language-color-border: #{variables.$repo-language-color-border};
--gitako-code-selection-bg: #{variables.$code-selection-bg};
--gitako-blob-line-highlight-bg: #{variables.$blob-line-highlight-bg};
--gitako-blob-line-highlight-border: #{variables.$blob-line-highlight-border};
--gitako-diff-addition-text: #{variables.$diff-addition-text};
--gitako-diff-addition-bg: #{variables.$diff-addition-bg};
--gitako-diff-addition-border: #{variables.$diff-addition-border};
--gitako-diff-deletion-text: #{variables.$diff-deletion-text};
--gitako-diff-deletion-bg: #{variables.$diff-deletion-bg};
--gitako-diff-deletion-border: #{variables.$diff-deletion-border};
--gitako-diff-change-text: #{variables.$diff-change-text};
--gitako-diff-change-bg: #{variables.$diff-change-bg};
--gitako-diff-change-border: #{variables.$diff-change-border};
--gitako-diff-blob-num-text: #{variables.$diff-blob-num-text};
--gitako-diff-blob-num-hover-text: #{variables.$diff-blob-num-hover-text};
--gitako-diff-blob-addition-num-text: #{variables.$diff-blob-addition-num-text};
--gitako-diff-blob-addition-num-hover-text: #{variables.$diff-blob-addition-num-hover-text};
--gitako-diff-blob-addition-num-bg: #{variables.$diff-blob-addition-num-bg};
--gitako-diff-blob-addition-line-bg: #{variables.$diff-blob-addition-line-bg};
--gitako-diff-blob-addition-word-bg: #{variables.$diff-blob-addition-word-bg};
--gitako-diff-blob-deletion-num-text: #{variables.$diff-blob-deletion-num-text};
--gitako-diff-blob-deletion-num-hover-text: #{variables.$diff-blob-deletion-num-hover-text};
--gitako-diff-blob-deletion-num-bg: #{variables.$diff-blob-deletion-num-bg};
--gitako-diff-blob-deletion-line-bg: #{variables.$diff-blob-deletion-line-bg};
--gitako-diff-blob-deletion-word-bg: #{variables.$diff-blob-deletion-word-bg};
--gitako-diff-blob-hunk-text: #{variables.$diff-blob-hunk-text};
--gitako-diff-blob-hunk-num-bg: #{variables.$diff-blob-hunk-num-bg};
--gitako-diff-blob-hunk-line-bg: #{variables.$diff-blob-hunk-line-bg};
--gitako-diff-blob-empty-block-bg: #{variables.$diff-blob-empty-block-bg};
--gitako-diff-blob-selected-line-highlight-bg: #{variables.$diff-blob-selected-line-highlight-bg};
--gitako-diff-blob-selected-line-highlight-border: #{variables.$diff-blob-selected-line-highlight-border};
--gitako-diff-blob-selected-line-highlight-mix-blend-mode: #{variables.$diff-blob-selected-line-highlight-mix-blend-mode};
--gitako-diff-blob-expander-icon: #{variables.$diff-blob-expander-icon};
--gitako-diff-blob-expander-hover-icon: #{variables.$diff-blob-expander-hover-icon};
--gitako-diff-blob-expander-hover-bg: #{variables.$diff-blob-expander-hover-bg};
--gitako-diff-blob-comment-button-icon: #{variables.$diff-blob-comment-button-icon};
--gitako-diff-blob-comment-button-bg: #{variables.$diff-blob-comment-button-bg};
--gitako-diff-blob-comment-button-gradient-bg: #{variables.$diff-blob-comment-button-gradient-bg};
--gitako-global-nav-logo: #{variables.$global-nav-logo};
--gitako-global-nav-bg: #{variables.$global-nav-bg};
--gitako-global-nav-text: #{variables.$global-nav-text};
--gitako-global-nav-icon: #{variables.$global-nav-icon};
--gitako-global-nav-input-bg: #{variables.$global-nav-input-bg};
--gitako-global-nav-input-border: #{variables.$global-nav-input-border};
--gitako-global-nav-input-icon: #{variables.$global-nav-input-icon};
--gitako-global-nav-input-placeholder: #{variables.$global-nav-input-placeholder};
--gitako-calendar-graph-day-bg: #{variables.$calendar-graph-day-bg};
--gitako-calendar-graph-day-border: #{variables.$calendar-graph-day-border};
--gitako-calendar-graph-day-l1-bg: #{variables.$calendar-graph-day-l1-bg};
--gitako-calendar-graph-day-l2-bg: #{variables.$calendar-graph-day-l2-bg};
--gitako-calendar-graph-day-l3-bg: #{variables.$calendar-graph-day-l3-bg};
--gitako-calendar-graph-day-l4-bg: #{variables.$calendar-graph-day-l4-bg};
--gitako-calendar-graph-day-l4-border: #{variables.$calendar-graph-day-l4-border};
--gitako-calendar-graph-day-l3-border: #{variables.$calendar-graph-day-l3-border};
--gitako-calendar-graph-day-l2-border: #{variables.$calendar-graph-day-l2-border};
--gitako-calendar-graph-day-l1-border: #{variables.$calendar-graph-day-l1-border};
--gitako-footer-invertocat-octicon: #{variables.$footer-invertocat-octicon};
--gitako-footer-invertocat-octicon-hover: #{variables.$footer-invertocat-octicon-hover};
--gitako-pr-state-draft-text: #{variables.$pr-state-draft-text};
--gitako-pr-state-draft-bg: #{variables.$pr-state-draft-bg};
--gitako-pr-state-draft-border: #{variables.$pr-state-draft-border};
--gitako-pr-state-open-text: #{variables.$pr-state-open-text};
--gitako-pr-state-open-bg: #{variables.$pr-state-open-bg};
--gitako-pr-state-open-border: #{variables.$pr-state-open-border};
--gitako-pr-state-merged-text: #{variables.$pr-state-merged-text};
--gitako-pr-state-merged-bg: #{variables.$pr-state-merged-bg};
--gitako-pr-state-merged-border: #{variables.$pr-state-merged-border};
--gitako-pr-state-closed-text: #{variables.$pr-state-closed-text};
--gitako-pr-state-closed-bg: #{variables.$pr-state-closed-bg};
--gitako-pr-state-closed-border: #{variables.$pr-state-closed-border};
--gitako-topic-tag-text: #{variables.$topic-tag-text};
--gitako-topic-tag-bg: #{variables.$topic-tag-bg};
--gitako-topic-tag-hover-bg: #{variables.$topic-tag-hover-bg};
--gitako-topic-tag-active-bg: #{variables.$topic-tag-active-bg};
--gitako-merge-box-success-icon-bg: #{variables.$merge-box-success-icon-bg};
--gitako-merge-box-success-icon-text: #{variables.$merge-box-success-icon-text};
--gitako-merge-box-success-icon-border: #{variables.$merge-box-success-icon-border};
--gitako-merge-box-success-indicator-bg: #{variables.$merge-box-success-indicator-bg};
--gitako-merge-box-success-indicator-border: #{variables.$merge-box-success-indicator-border};
--gitako-merge-box-merged-icon-bg: #{variables.$merge-box-merged-icon-bg};
--gitako-merge-box-merged-icon-text: #{variables.$merge-box-merged-icon-text};
--gitako-merge-box-merged-icon-border: #{variables.$merge-box-merged-icon-border};
--gitako-merge-box-merged-box-border: #{variables.$merge-box-merged-box-border};
--gitako-merge-box-neutral-icon-bg: #{variables.$merge-box-neutral-icon-bg};
--gitako-merge-box-neutral-icon-text: #{variables.$merge-box-neutral-icon-text};
--gitako-merge-box-neutral-icon-border: #{variables.$merge-box-neutral-icon-border};
--gitako-merge-box-neutral-indicator-bg: #{variables.$merge-box-neutral-indicator-bg};
--gitako-merge-box-neutral-indicator-border: #{variables.$merge-box-neutral-indicator-border};
--gitako-merge-box-warning-icon-bg: #{variables.$merge-box-warning-icon-bg};
--gitako-merge-box-warning-icon-text: #{variables.$merge-box-warning-icon-text};
--gitako-merge-box-warning-icon-border: #{variables.$merge-box-warning-icon-border};
--gitako-merge-box-warning-box-border: #{variables.$merge-box-warning-box-border};
--gitako-merge-box-warning-merge-highlight: #{variables.$merge-box-warning-merge-highlight};
--gitako-merge-box-error-icon-bg: #{variables.$merge-box-error-icon-bg};
--gitako-merge-box-error-icon-text: #{variables.$merge-box-error-icon-text};
--gitako-merge-box-error-icon-border: #{variables.$merge-box-error-icon-border};
--gitako-merge-box-error-indicator-bg: #{variables.$merge-box-error-indicator-bg};
--gitako-merge-box-error-indicator-border: #{variables.$merge-box-error-indicator-border};
--gitako-project-card-bg: #{variables.$project-card-bg};
--gitako-project-header-bg: #{variables.$project-header-bg};
--gitako-project-sidebar-bg: #{variables.$project-sidebar-bg};
--gitako-project-gradient-in: #{variables.$project-gradient-in};
--gitako-project-gradient-out: #{variables.$project-gradient-out};
--gitako-marketing-icon-primary: #{variables.$marketing-icon-primary};
--gitako-marketing-icon-secondary: #{variables.$marketing-icon-secondary};
--gitako-prettylights-syntax-comment: #{variables.$prettylights-syntax-comment};
--gitako-prettylights-syntax-constant: #{variables.$prettylights-syntax-constant};
--gitako-prettylights-syntax-entity: #{variables.$prettylights-syntax-entity};
--gitako-prettylights-syntax-storage-modifier-import: #{variables.$prettylights-syntax-storage-modifier-import};
--gitako-prettylights-syntax-entity-tag: #{variables.$prettylights-syntax-entity-tag};
--gitako-prettylights-syntax-keyword: #{variables.$prettylights-syntax-keyword};
--gitako-prettylights-syntax-string: #{variables.$prettylights-syntax-string};
--gitako-prettylights-syntax-variable: #{variables.$prettylights-syntax-variable};
--gitako-prettylights-syntax-brackethighlighter-unmatched: #{variables.$prettylights-syntax-brackethighlighter-unmatched};
--gitako-prettylights-syntax-invalid-illegal-text: #{variables.$prettylights-syntax-invalid-illegal-text};
--gitako-prettylights-syntax-invalid-illegal-bg: #{variables.$prettylights-syntax-invalid-illegal-bg};
--gitako-prettylights-syntax-carriage-return-text: #{variables.$prettylights-syntax-carriage-return-text};
--gitako-prettylights-syntax-carriage-return-bg: #{variables.$prettylights-syntax-carriage-return-bg};
--gitako-prettylights-syntax-string-regexp: #{variables.$prettylights-syntax-string-regexp};
--gitako-prettylights-syntax-markup-list: #{variables.$prettylights-syntax-markup-list};
--gitako-prettylights-syntax-markup-heading: #{variables.$prettylights-syntax-markup-heading};
--gitako-prettylights-syntax-markup-italic: #{variables.$prettylights-syntax-markup-italic};
--gitako-prettylights-syntax-markup-bold: #{variables.$prettylights-syntax-markup-bold};
--gitako-prettylights-syntax-markup-deleted-text: #{variables.$prettylights-syntax-markup-deleted-text};
--gitako-prettylights-syntax-markup-deleted-bg: #{variables.$prettylights-syntax-markup-deleted-bg};
--gitako-prettylights-syntax-markup-inserted-text: #{variables.$prettylights-syntax-markup-inserted-text};
--gitako-prettylights-syntax-markup-inserted-bg: #{variables.$prettylights-syntax-markup-inserted-bg};
--gitako-prettylights-syntax-markup-changed-text: #{variables.$prettylights-syntax-markup-changed-text};
--gitako-prettylights-syntax-markup-changed-bg: #{variables.$prettylights-syntax-markup-changed-bg};
--gitako-prettylights-syntax-markup-ignored-text: #{variables.$prettylights-syntax-markup-ignored-text};
--gitako-prettylights-syntax-markup-ignored-bg: #{variables.$prettylights-syntax-markup-ignored-bg};
--gitako-prettylights-syntax-meta-diff-range: #{variables.$prettylights-syntax-meta-diff-range};
--gitako-prettylights-syntax-brackethighlighter-angle: #{variables.$prettylights-syntax-brackethighlighter-angle};
--gitako-prettylights-syntax-sublimelinter-gutter-mark: #{variables.$prettylights-syntax-sublimelinter-gutter-mark};
--gitako-prettylights-syntax-constant-other-reference-link: #{variables.$prettylights-syntax-constant-other-reference-link};
--gitako-codemirror-text: #{variables.$codemirror-text};
--gitako-codemirror-bg: #{variables.$codemirror-bg};
--gitako-codemirror-gutters-bg: #{variables.$codemirror-gutters-bg};
--gitako-codemirror-guttermarker-text: #{variables.$codemirror-guttermarker-text};
--gitako-codemirror-guttermarker-subtle-text: #{variables.$codemirror-guttermarker-subtle-text};
--gitako-codemirror-linenumber-text: #{variables.$codemirror-linenumber-text};
--gitako-codemirror-cursor: #{variables.$codemirror-cursor};
--gitako-codemirror-selection-bg: #{variables.$codemirror-selection-bg};
--gitako-codemirror-activeline-bg: #{variables.$codemirror-activeline-bg};
--gitako-codemirror-matchingbracket-text: #{variables.$codemirror-matchingbracket-text};
--gitako-codemirror-lines-bg: #{variables.$codemirror-lines-bg};
--gitako-codemirror-syntax-comment: #{variables.$codemirror-syntax-comment};
--gitako-codemirror-syntax-constant: #{variables.$codemirror-syntax-constant};
--gitako-codemirror-syntax-entity: #{variables.$codemirror-syntax-entity};
--gitako-codemirror-syntax-keyword: #{variables.$codemirror-syntax-keyword};
--gitako-codemirror-syntax-storage: #{variables.$codemirror-syntax-storage};
--gitako-codemirror-syntax-string: #{variables.$codemirror-syntax-string};
--gitako-codemirror-syntax-support: #{variables.$codemirror-syntax-support};
--gitako-codemirror-syntax-variable: #{variables.$codemirror-syntax-variable};
--gitako-ansi-black: #{variables.$ansi-black};
--gitako-ansi-black-bright: #{variables.$ansi-black-bright};
--gitako-ansi-white: #{variables.$ansi-white};
--gitako-ansi-white-bright: #{variables.$ansi-white-bright};
--gitako-ansi-gray: #{variables.$ansi-gray};
--gitako-ansi-red: #{variables.$ansi-red};
--gitako-ansi-red-bright: #{variables.$ansi-red-bright};
--gitako-ansi-green: #{variables.$ansi-green};
--gitako-ansi-green-bright: #{variables.$ansi-green-bright};
--gitako-ansi-yellow: #{variables.$ansi-yellow};
--gitako-ansi-yellow-bright: #{variables.$ansi-yellow-bright};
--gitako-ansi-blue: #{variables.$ansi-blue};
--gitako-ansi-blue-bright: #{variables.$ansi-blue-bright};
--gitako-ansi-magenta: #{variables.$ansi-magenta};
--gitako-ansi-magenta-bright: #{variables.$ansi-magenta-bright};
--gitako-ansi-cyan: #{variables.$ansi-cyan};
--gitako-ansi-cyan-bright: #{variables.$ansi-cyan-bright};
}
// How to merge the selectors?
// Is it possible to not have two copies of dark theme variables?
@media (prefers-color-scheme: dark) {
:root[data-color-mode='auto'] {
@include assign();
}
}
:root[data-color-mode='dark'] {
@include assign();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,680 @@
$variables: (
'auto-black': '#cdd9e5',
'auto-white': '#1c2128',
'auto-gray-0': '#22272e',
'auto-gray-1': '#2d333b',
'auto-gray-2': '#373e47',
'auto-gray-3': '#444c56',
'auto-gray-4': '#545d68',
'auto-gray-5': '#636e7b',
'auto-gray-6': '#768390',
'auto-gray-7': '#909dab',
'auto-gray-8': '#adbac7',
'auto-gray-9': '#cdd9e5',
'auto-blue-0': '#0f2d5c',
'auto-blue-1': '#143d79',
'auto-blue-2': '#1b4b91',
'auto-blue-3': '#255ab2',
'auto-blue-4': '#316dca',
'auto-blue-5': '#4184e4',
'auto-blue-6': '#539bf5',
'auto-blue-7': '#6cb6ff',
'auto-blue-8': '#96d0ff',
'auto-blue-9': '#c6e6ff',
'auto-green-0': '#113417',
'auto-green-1': '#1b4721',
'auto-green-2': '#245829',
'auto-green-3': '#2b6a30',
'auto-green-4': '#347d39',
'auto-green-5': '#46954a',
'auto-green-6': '#57ab5a',
'auto-green-7': '#6bc46d',
'auto-green-8': '#8ddb8c',
'auto-green-9': '#b4f1b4',
'auto-yellow-0': '#452700',
'auto-yellow-1': '#593600',
'auto-yellow-2': '#6c4400',
'auto-yellow-3': '#805400',
'auto-yellow-4': '#966600',
'auto-yellow-5': '#ae7c14',
'auto-yellow-6': '#c69026',
'auto-yellow-7': '#daaa3f',
'auto-yellow-8': '#eac55f',
'auto-yellow-9': '#fbe090',
'auto-orange-0': '#4d210c',
'auto-orange-1': '#682d0f',
'auto-orange-2': '#7f3913',
'auto-orange-3': '#94471b',
'auto-orange-4': '#ae5622',
'auto-orange-5': '#cc6b2c',
'auto-orange-6': '#e0823d',
'auto-orange-7': '#f69d50',
'auto-orange-8': '#ffbc6f',
'auto-orange-9': '#ffddb0',
'auto-red-0': '#78191b',
'auto-red-1': '#78191b',
'auto-red-2': '#922323',
'auto-red-3': '#ad2e2c',
'auto-red-4': '#c93c37',
'auto-red-5': '#e5534b',
'auto-red-6': '#f47067',
'auto-red-7': '#ff938a',
'auto-red-8': '#ffb8b0',
'auto-red-9': '#ffd8d3',
'auto-purple-0': '#352160',
'auto-purple-1': '#472c82',
'auto-purple-2': '#5936a2',
'auto-purple-3': '#6b44bc',
'auto-purple-4': '#8256d0',
'auto-purple-5': '#986ee2',
'auto-purple-6': '#b083f0',
'auto-purple-7': '#dcbdfb',
'auto-purple-8': '#dcbdfb',
'auto-purple-9': '#eedcff',
'auto-pink-0': '#551639',
'auto-pink-1': '#69264a',
'auto-pink-2': '#7e325a',
'auto-pink-3': '#983b6e',
'auto-pink-4': '#ae4c82',
'auto-pink-5': '#c96198',
'auto-pink-6': '#e275ad',
'auto-pink-7': '#fc8dc7',
'auto-pink-8': '#ffb3d8',
'auto-pink-9': '#ffd7eb',
'text-primary': '#adbac7',
'text-secondary': '#768390',
'text-tertiary': '#768390',
'text-placeholder': '#545d68',
'text-disabled': '#545d68',
'text-inverse': '#22272e',
'text-link': '#539bf5',
'text-danger': '#e5534b',
'text-success': '#6bc46d',
'text-warning': '#daaa3f',
'text-white': '#cdd9e5',
'icon-primary': '#adbac7',
'icon-secondary': '#636e7b',
'icon-tertiary': '#545d68',
'icon-info': '#6cb6ff',
'icon-danger': '#e5534b',
'icon-success': '#6bc46d',
'icon-warning': '#daaa3f',
'border-primary': '#444c56',
'border-secondary': '#373e47',
'border-tertiary': '#636e7b',
'border-overlay': '#444c56',
'border-inverse': '#cdd9e5',
'border-info': '#4184e466',
'border-danger': '#e5534b66',
'border-success': '#57ab5a66',
'border-warning': '#ae7c1466',
'bg-canvas': '#22272e',
'bg-canvas-mobile': '#1c2128',
'bg-canvas-inverse': '#cdd9e5',
'bg-canvas-inset': '#1e2228',
'bg-primary': '#22272e',
'bg-secondary': '#22272e',
'bg-tertiary': '#2d333b',
'bg-overlay': '#373e47',
'bg-backdrop': '#1c2128cc',
'bg-info': '#4184e41a',
'bg-info-inverse': '#4184e4',
'bg-danger': '#e5534b1a',
'bg-danger-inverse': '#c93c37',
'bg-success': '#46954a1a',
'bg-success-inverse': '#46954a',
'bg-warning': '#ae7c141a',
'bg-warning-inverse': '#ae7c14',
'shadow-small': '0 0 #0000',
'shadow-medium': '0 3px 6px #1c2128',
'shadow-large': '0 8px 24px #1c2128',
'shadow-extra-large': '0 12px 48px #1c2128',
'shadow-highlight': '0 0 #0000',
'shadow-inset': '0 0 #0000',
'state-hover-primary-bg': '#316dca',
'state-hover-primary-border': '#4184e4',
'state-hover-primary-text': '#cdd9e5',
'state-hover-primary-icon': '#cdd9e5',
'state-hover-secondary-bg': '#2d333b',
'state-hover-secondary-border': '#2d333b',
'state-selected-primary-bg': '#316dca',
'state-selected-primary-border': '#4184e4',
'state-selected-primary-text': '#cdd9e5',
'state-selected-primary-icon': '#cdd9e5',
'state-focus-border': '#4184e4',
'state-focus-shadow': '0 0 0 3px #143d79',
'fade-fg-10': '#cdd9e51a',
'fade-fg-15': '#cdd9e526',
'fade-fg-30': '#cdd9e54d',
'fade-fg-50': '#cdd9e580',
'fade-fg-70': '#cdd9e5b3',
'fade-fg-85': '#cdd9e5d9',
'fade-black-10': '#1c21281a',
'fade-black-15': '#1c212826',
'fade-black-30': '#1c21284d',
'fade-black-50': '#1c212880',
'fade-black-70': '#1c2128b3',
'fade-black-85': '#1c2128d9',
'fade-white-10': '#cdd9e51a',
'fade-white-15': '#cdd9e526',
'fade-white-30': '#cdd9e54d',
'fade-white-50': '#cdd9e580',
'fade-white-70': '#cdd9e5b3',
'fade-white-85': '#cdd9e5d9',
'alert-info-text': '#6cb6ff',
'alert-info-icon': '#6cb6ff',
'alert-info-bg': '#4184e41a',
'alert-info-border': '#4184e466',
'alert-warn-text': '#daaa3f',
'alert-warn-icon': '#daaa3f',
'alert-warn-bg': '#ae7c141a',
'alert-warn-border': '#ae7c1466',
'alert-error-text': '#f47067',
'alert-error-icon': '#f47067',
'alert-error-bg': '#e5534b1a',
'alert-error-border': '#e5534b66',
'alert-success-text': '#6bc46d',
'alert-success-icon': '#6bc46d',
'alert-success-bg': '#46954a1a',
'alert-success-border': '#46954a66',
'autocomplete-shadow': '0 16px 32px #1c2128d9',
'autocomplete-row-border': '#444c56',
'blankslate-icon': '#5f6a76',
'btn-text': '#adbac7',
'btn-bg': '#373e47',
'btn-border': '#444c56',
'btn-shadow': '0 0 #0000',
'btn-inset-shadow': '0 0 #0000',
'btn-hover-bg': '#444c56',
'btn-hover-border': '#768390',
'btn-selected-bg': '#2d333b',
'btn-focus-bg': '#373e47',
'btn-focus-border': '#768390',
'btn-focus-shadow': '0 0 0 3px #7683904d',
'btn-shadow-active': 'inset 0 0.15em 0.3em #1c212826',
'btn-shadow-input-focus': '0 0 0 0.2em #316dca4d',
'btn-primary-text': '#fff',
'btn-primary-bg': '#347d39',
'btn-primary-border': '#46954a',
'btn-primary-shadow': '0 0 #0000',
'btn-primary-inset-shadow': '0 0 #0000',
'btn-primary-hover-bg': '#46954a',
'btn-primary-hover-border': '#57ab5a',
'btn-primary-selected-bg': '#347d39',
'btn-primary-selected-shadow': '0 0 #0000',
'btn-primary-disabled-text': '#cdd9e580',
'btn-primary-disabled-bg': '#347d3999',
'btn-primary-disabled-border': '#0000',
'btn-primary-focus-bg': '#347d39',
'btn-primary-focus-border': '#57ab5a',
'btn-primary-focus-shadow': '0 0 0 3px #2ea44f66',
'btn-primary-icon': '#cdd9e5',
'btn-primary-counter-bg': '#cdd9e533',
'btn-outline-text': '#539bf5',
'btn-outline-hover-text': '#539bf5',
'btn-outline-hover-bg': '#444c56',
'btn-outline-hover-border': '#539bf5',
'btn-outline-hover-shadow': '0 1px 0 #1c21281a',
'btn-outline-hover-inset-shadow': 'inset 0 1px 0 #cdd9e508',
'btn-outline-hover-counter-bg': '#cdd9e533',
'btn-outline-selected-text': '#cdd9e5',
'btn-outline-selected-bg': '#1b4b91',
'btn-outline-selected-border': '#cdd9e51a',
'btn-outline-selected-shadow': '0 0 #0000',
'btn-outline-disabled-text': '#539bf580',
'btn-outline-disabled-bg': '#22272e',
'btn-outline-disabled-counter-bg': '#316dca0d',
'btn-outline-focus-border': '#539bf5',
'btn-outline-focus-shadow': '0 0 0 3px #255ab266',
'btn-outline-counter-bg': '#316dca1a',
'btn-danger-text': '#e5534b',
'btn-danger-hover-text': '#fff',
'btn-danger-hover-bg': '#c93c37',
'btn-danger-hover-border': '#e5534b',
'btn-danger-hover-shadow': '0 0 #0000',
'btn-danger-hover-inset-shadow': '0 0 #0000',
'btn-danger-hover-counter-bg': '#fff3',
'btn-danger-selected-text': '#fff',
'btn-danger-selected-bg': '#ad2e2c',
'btn-danger-selected-border': '#cdd9e51a',
'btn-danger-selected-shadow': '0 0 #0000',
'btn-danger-disabled-text': '#e5534b80',
'btn-danger-disabled-bg': '#22272e',
'btn-danger-disabled-counter-bg': '#c93c370d',
'btn-danger-focus-border': '#e5534b',
'btn-danger-focus-shadow': '0 0 0 3px #ad2e2c66',
'btn-danger-counter-bg': '#c93c371a',
'btn-danger-icon': '#e5534b',
'btn-danger-hover-icon': '#cdd9e5',
'btn-counter-bg': '#444c56',
'counter-text': '#adbac7',
'counter-bg': '#444c56',
'counter-primary-text': '#adbac7',
'counter-primary-bg': '#636e7b',
'counter-secondary-text': '#768390',
'counter-secondary-bg': '#76839033',
'dropdown-shadow': '0 16px 32px #1c2128d9',
'label-border': '#444c56',
'label-primary-text': '#909dab',
'label-primary-border': '#636e7b',
'label-secondary-text': '#768390',
'label-secondary-border': '#444c56',
'label-info-text': '#4184e4',
'label-info-border': '#4184e466',
'label-success-text': '#57ab5a',
'label-success-border': '#46954a66',
'label-warning-text': '#daaa3f',
'label-warning-border': '#f2d35b66',
'label-danger-text': '#e5534b',
'label-danger-border': '#e5534b66',
'label-orange-text': '#cc6b2c',
'label-orange-border': '#cc6b2c66',
'input-bg': '#22272e',
'input-contrast-bg': '#1c212880',
'input-border': '#373e47',
'input-shadow': '0 0 #0000',
'input-disabled-bg': '#2d333b',
'input-disabled-border': '#444c56',
'input-warning-border': '#c69026',
'input-error-border': '#e5534b',
'input-tooltip-success-text': '#6bc46d',
'input-tooltip-success-bg': '#263231',
'input-tooltip-success-border': '#335a3b',
'input-tooltip-warning-text': '#daaa3f',
'input-tooltip-warning-bg': '#30302b',
'input-tooltip-warning-border': '#624e22',
'input-tooltip-error-text': '#f47067',
'input-tooltip-error-bg': '#362b31',
'input-tooltip-error-border': '#7c3b3b',
'avatar-bg': '#cdd9e51a',
'avatar-border': '#cdd9e51a',
'avatar-stack-fade': '#444c56',
'avatar-stack-fade-more': '#373e47',
'avatar-child-shadow': '-2px -2px 0 #22272e',
'toast-text': '#adbac7',
'toast-bg': '#444c56',
'toast-border': '#636e7b',
'toast-shadow': '0 8px 24px #1c2128',
'toast-icon': '#cdd9e5',
'toast-icon-bg': '#316dca',
'toast-icon-border': '#4184e4',
'toast-success-text': '#adbac7',
'toast-success-border': '#636e7b',
'toast-success-icon': '#cdd9e5',
'toast-success-icon-bg': '#46954a',
'toast-success-icon-border': '#57ab5a',
'toast-warning-text': '#adbac7',
'toast-warning-border': '#636e7b',
'toast-warning-icon': '#cdd9e5',
'toast-warning-icon-bg': '#ae7c14',
'toast-warning-icon-border': '#c69026',
'toast-danger-text': '#adbac7',
'toast-danger-border': '#636e7b',
'toast-danger-icon': '#cdd9e5',
'toast-danger-icon-bg': '#c93c37',
'toast-danger-icon-border': '#e5534b',
'toast-loading-text': '#adbac7',
'toast-loading-border': '#636e7b',
'toast-loading-icon': '#cdd9e5',
'toast-loading-icon-bg': '#636e7b',
'toast-loading-icon-border': '#768390',
'timeline-text': '#909dab',
'timeline-badge-bg': '#22272e',
'timeline-badge-success-border': '#46954a',
'timeline-target-badge-border': '#316dca',
'timeline-target-badge-shadow': '#1b4b91',
'select-menu-border-secondary': '#444c56',
'select-menu-shadow': '0 0 18px #1c212866',
'select-menu-backdrop-bg': '#1c212880',
'select-menu-backdrop-border': '#545d68',
'select-menu-tap-highlight': '#444c5680',
'select-menu-tap-focus-bg': '#143d79',
'box-blue-border': '#1b4b91',
'box-row-yellow-bg': '#ebc4401a',
'box-row-blue-bg': '#6cb6ff1a',
'box-header-blue-bg': '#22272e',
'box-header-blue-border': '#444c56',
'box-border-info': '#4184e466',
'box-bg-info': '#4184e41a',
'box-border-warning': '#ae7c1466',
'box-bg-warning': '#ae7c141a',
'branch-name-text': '#adbac7',
'branch-name-icon': '#909dab',
'branch-name-bg': '#539bf51a',
'branch-name-link-text': '#539bf5',
'branch-name-link-icon': '#539bf5',
'branch-name-link-bg': '#539bf51a',
'markdown-code-bg': '#cdd9e526',
'markdown-frame-border': '#4f5964',
'markdown-blockquote-border': '#4f5964',
'markdown-table-border': '#4f5964',
'markdown-table-tr-border': '#3b424b',
'menu-heading-text': '#768390',
'menu-border-active': '#f78166',
'menu-bg-active': '#2d333b',
'sidenav-selected-bg': '#373e47',
'sidenav-border-active': '#f78166',
'header-text': '#cdd9e5b3',
'header-bg': '#2d333b',
'header-logo': '#cdd9e5',
'filter-item-bar-bg': '#292e35',
'hidden-text-expander-bg': '#373e47',
'hidden-text-expander-bg-hover': '#444c56',
'drag-and-drop-border': '#393f48',
'upload-enabled-border': '#4f5964',
'upload-enabled-border-focused': '#4f84d4',
'previewable-comment-form-border': '#393f48',
'underlinenav-border': '#444c5600',
'underlinenav-border-hover': '#444c56',
'underlinenav-border-active': '#f78166',
'underlinenav-text': '#768390',
'underlinenav-text-hover': '#adbac7',
'underlinenav-text-active': '#adbac7',
'underlinenav-icon': '#636e7b',
'underlinenav-icon-hover': '#adbac7',
'underlinenav-icon-active': '#adbac7',
'underlinenav-counter-text': '#768390',
'underlinenav-counter-bg': '#76839033',
'verified-badge-text': '#57ab5a',
'verified-badge-bg': '#57ab5a1a',
'verified-badge-border': '#57ab5a66',
'social-count-bg': '#373e47',
'tooltip-text': '#cdd9e5',
'tooltip-bg': '#636e7b',
'header-search-bg': '#22272e',
'header-search-border': '#373e47',
'search-keyword-hl': '#ae7c1466',
'diffstat-neutral-bg': '#444c56',
'diffstat-neutral-border': '#cdd9e51a',
'diffstat-deletion-bg': '#c93c37',
'diffstat-deletion-border': '#e5534b',
'diffstat-addition-bg': '#347d39',
'diffstat-addition-border': '#46954a',
'mktg-success': '#3d8942',
'mktg-info': '#3877d5',
'mktg-bg-shade-gradient-top': '#1c212811',
'mktg-bg-shade-gradient-bottom': '#1c212800',
'mktg-btn-bg': '#316dca',
'mktg-btn-border': '#316dca',
'mktg-btn-text': '#cdd9e5',
'mktg-btn-icon': '#cdd9e5',
'mktg-btn-focus-shadow': '0 0 0 3px #316dca4d',
'mktg-btn-hover-bg': '#4184e4',
'mktg-btn-hover-border': '#4184e4',
'mktg-btn-disabled-bg': '#4184e480',
'mktg-btn-disabled-border': '#0000',
'mktg-btn-disabled-text': '#cdd9e580',
'mktg-btn-disabled-icon': '#cdd9e580',
'mktg-btn-primary-bg': '#347d39',
'mktg-btn-primary-border': '#347d39',
'mktg-btn-primary-text': '#cdd9e5',
'mktg-btn-primary-icon': '#cdd9e5',
'mktg-btn-primary-focus-shadow': '0 0 0 3px #347d394d',
'mktg-btn-primary-hover-bg': '#46954a',
'mktg-btn-primary-hover-border': '#46954a',
'mktg-btn-primary-disabled-bg': '#46954a80',
'mktg-btn-primary-disabled-border': '#0000',
'mktg-btn-primary-disabled-text': '#cdd9e580',
'mktg-btn-primary-disabled-icon': '#cdd9e580',
'mktg-btn-outline-bg': '#0000',
'mktg-btn-outline-border': '#4184e480',
'mktg-btn-outline-text': '#4184e4',
'mktg-btn-outline-icon': '#4184e4',
'mktg-btn-outline-focus-shadow': '0 0 0 3px #4184e44d',
'mktg-btn-outline-hover-bg': '#0000',
'mktg-btn-outline-hover-border': '#4184e4',
'mktg-btn-outline-hover-text': '#539bf5',
'mktg-btn-outline-hover-icon': '#539bf5',
'mktg-btn-outline-disabled-bg': '#0000',
'mktg-btn-outline-disabled-border': '#4184e433',
'mktg-btn-outline-disabled-text': '#4184e480',
'mktg-btn-outline-disabled-icon': '#4184e480',
'mktg-btn-dark-bg': '#0000',
'mktg-btn-dark-border': '#adbac780',
'mktg-btn-dark-text': '#adbac7',
'mktg-btn-dark-icon': '#adbac7',
'mktg-btn-dark-focus-shadow': '0 0 0 3px #adbac74d',
'mktg-btn-dark-hover-bg': '#adbac780',
'mktg-btn-dark-hover-border': '#adbac780',
'mktg-btn-dark-hover-text': '#22272e',
'mktg-btn-dark-hover-icon': '#22272e',
'mktg-btn-dark-disabled-bg': '#0000',
'mktg-btn-dark-disabled-border': '#adbac733',
'mktg-btn-dark-disabled-text': '#adbac780',
'mktg-btn-dark-disabled-icon': '#adbac780',
'files-explorer-icon': '#636e7b',
'hl-author-bg': '#0f2d5c',
'hl-author-border': '#1b4b91',
'logo-subdued': '#444c56',
'discussion-border': '#494c49',
'discussion-bg-success': '#46954a1a',
'actions-workflow-table-sticky-bg': '#22272ef2',
'repo-language-color-border': '#cdd9e533',
'code-selection-bg': '#6cb6ff4d',
'highlight-text': '#ffd467',
'highlight-bg': '#cc8f2c61',
'blob-line-highlight-bg': '#c6902626',
'blob-line-highlight-border': '#daaa3f',
'diff-addition-text': '#6bc46d',
'diff-addition-bg': '#46954a33',
'diff-addition-border': '#2b6a30',
'diff-deletion-text': '#e5534b',
'diff-deletion-bg': '#c93c3733',
'diff-deletion-border': '#ad2e2c',
'diff-change-text': '#daaa3f',
'diff-change-bg': '#452700',
'diff-change-border': '#966600',
'diff-blob-num-text': '#cdd9e54d',
'diff-blob-num-hover-text': '#cdd9e599',
'diff-blob-addition-num-text': '#57ab5a',
'diff-blob-addition-num-hover-text': '#8ddb8c',
'diff-blob-addition-num-bg': '#46954a1a',
'diff-blob-addition-line-bg': '#46954a33',
'diff-blob-addition-word-bg': '#46954a8c',
'diff-blob-deletion-num-text': '#e5534b',
'diff-blob-deletion-num-hover-text': '#ff938a',
'diff-blob-deletion-num-bg': '#c93c371a',
'diff-blob-deletion-line-bg': '#c93c3733',
'diff-blob-deletion-word-bg': '#c93c3780',
'diff-blob-hunk-text': '#768390',
'diff-blob-hunk-num-bg': '#539bf526',
'diff-blob-hunk-line-bg': '#539bf51a',
'diff-blob-empty-block-bg': '#2d333b',
'diff-blob-selected-line-highlight-bg': '#ae7c141a',
'diff-blob-selected-line-highlight-border': '#ae7c14',
'diff-blob-selected-line-highlight-mix-blend-mode': 'normal',
'diff-blob-expander-icon': '#768390',
'diff-blob-expander-hover-icon': '#cdd9e5',
'diff-blob-expander-hover-bg': '#316dca',
'diff-blob-comment-button-icon': '#cdd9e5',
'diff-blob-comment-button-bg': '#316dca',
'diff-blob-comment-button-gradient-bg': '#437bd1',
'global-nav-logo': '#cdd9e5',
'global-nav-bg': '#2d333b',
'global-nav-text': '#adbac7',
'global-nav-icon': '#adbac7',
'global-nav-input-bg': '#22272e',
'global-nav-input-border': '#373e47',
'global-nav-input-icon': '#373e47',
'global-nav-input-placeholder': '#545d68',
'calendar-graph-day-bg': '#2d333b',
'calendar-graph-day-border': '#1b1f230f',
'calendar-graph-day-L1-bg': '#003820',
'calendar-graph-day-L2-bg': '#00602d',
'calendar-graph-day-L3-bg': '#10983d',
'calendar-graph-day-L4-bg': '#27d545',
'calendar-graph-day-L4-border': '#ffffff0d',
'calendar-graph-day-L3-border': '#ffffff0d',
'calendar-graph-day-L2-border': '#ffffff0d',
'calendar-graph-day-L1-border': '#ffffff0d',
'footer-invertocat-octicon': '#444c56',
'footer-invertocat-octicon-hover': '#636e7b',
'pr-state-draft-text': '#768390',
'pr-state-draft-bg': '#7683901a',
'pr-state-draft-border': '#76839066',
'pr-state-open-text': '#57ab5a',
'pr-state-open-bg': '#57ab5a1a',
'pr-state-open-border': '#57ab5a66',
'pr-state-merged-text': '#986ee2',
'pr-state-merged-bg': '#b083f01a',
'pr-state-merged-border': '#b083f066',
'pr-state-closed-text': '#e5534b',
'pr-state-closed-bg': '#c93c371a',
'pr-state-closed-border': '#c93c3766',
'topic-tag-text': '#539bf5',
'topic-tag-bg': '#4184e41a',
'topic-tag-hover-bg': '#4184e433',
'topic-tag-active-bg': '#4184e426',
'merge-box-success-icon-bg': '#46954a1a',
'merge-box-success-icon-text': '#57ab5a',
'merge-box-success-icon-border': '#46954a66',
'merge-box-success-indicator-bg': '#347d39',
'merge-box-success-indicator-border': '#46954a',
'merge-box-merged-icon-bg': '#b083f01a',
'merge-box-merged-icon-text': '#986ee2',
'merge-box-merged-icon-border': '#b083f066',
'merge-box-merged-box-border': '#b083f066',
'merge-box-neutral-icon-bg': '#adbac71a',
'merge-box-neutral-icon-text': '#768390',
'merge-box-neutral-icon-border': '#adbac766',
'merge-box-neutral-indicator-bg': '#545d68',
'merge-box-neutral-indicator-border': '#636e7b',
'merge-box-warning-icon-bg': '#ae7c141a',
'merge-box-warning-icon-text': '#daaa3f',
'merge-box-warning-icon-border': '#ae7c1466',
'merge-box-warning-box-border': '#ae7c1466',
'merge-box-warning-merge-highlight': '#ae7c141a',
'merge-box-error-icon-bg': '#e5534b1a',
'merge-box-error-icon-text': '#e5534b',
'merge-box-error-icon-border': '#e5534b66',
'merge-box-error-indicator-bg': '#c93c37',
'merge-box-error-indicator-border': '#e5534b',
'project-card-bg': '#2d333b',
'project-header-bg': '#22272e',
'project-sidebar-bg': '#2d333b',
'project-gradient-in': '#2d333b',
'project-gradient-out': '#2d333b00',
'checks-bg': '#1e2228',
'checks-run-border-width': '1px',
'checks-container-border-width': '1px',
'checks-text-primary': '#adbac7',
'checks-text-secondary': '#768390',
'checks-text-link': '#539bf5',
'checks-btn-icon': '#636e7b',
'checks-btn-hover-icon': '#adbac7',
'checks-btn-hover-bg': '#444c56',
'checks-input-text': '#768390',
'checks-input-placeholder-text': '#545d68',
'checks-input-focus-text': '#adbac7',
'checks-input-bg': '#22272e',
'checks-input-shadow': '0 0 0 1px #373e47',
'checks-dropdown-text': '#adbac7',
'checks-dropdown-bg': '#373e47',
'checks-dropdown-border': '#444c56',
'checks-dropdown-hover-text': '#cdd9e5',
'checks-dropdown-hover-bg': '#316dca',
'checks-dropdown-btn-hover-text': '#cdd9e5',
'checks-dropdown-btn-hover-bg': '#2d333b',
'checks-scrollbar-thumb-bg': '#444c56',
'checks-header-label-text': '#768390',
'checks-header-label-open-text': '#adbac7',
'checks-header-border': '#373e47',
'checks-header-icon': '#636e7b',
'checks-line-text': '#768390',
'checks-line-num-text': '#768390',
'checks-line-timestamp-text': '#768390',
'checks-line-hover-bg': '#2d333b',
'checks-line-selected-bg': '#4184e41a',
'checks-line-selected-num-text': '#539bf5',
'checks-line-dt-fm-text': '#22272e',
'checks-line-dt-fm-bg': '#c69026',
'checks-gate-bg': '#80540026',
'checks-gate-text': '#768390',
'checks-gate-waiting-text': '#daaa3f',
'checks-step-header-open-bg': '#2d333b',
'checks-step-error-text': '#e5534b',
'checks-step-warning-text': '#daaa3f',
'checks-logline-text': '#636e7b',
'checks-logline-num-text': '#768390',
'checks-logline-debug-text': '#b083f0',
'checks-logline-error-text': '#768390',
'checks-logline-error-num-text': '#768390',
'checks-logline-error-bg': '#e5534b1a',
'checks-logline-warning-text': '#768390',
'checks-logline-warning-num-text': '#daaa3f',
'checks-logline-warning-bg': '#ae7c141a',
'checks-logline-command-text': '#539bf5',
'checks-logline-section-text': '#6bc46d',
'intro-shelf-gradient-left': '#4184e41a',
'intro-shelf-gradient-right': '#46954a1a',
'intro-shelf-gradient-in': '#22272e',
'intro-shelf-gradient-out': '#22272e00',
'marketing-icon-primary': '#6cb6ff',
'marketing-icon-secondary': '#316dca',
'prettylights-syntax-comment': '#768390',
'prettylights-syntax-constant': '#6cb6ff',
'prettylights-syntax-entity': '#dcbdfb',
'prettylights-syntax-storage-modifier-import': '#adbac7',
'prettylights-syntax-entity-tag': '#8ddb8c',
'prettylights-syntax-keyword': '#f47067',
'prettylights-syntax-string': '#96d0ff',
'prettylights-syntax-variable': '#f69d50',
'prettylights-syntax-brackethighlighter-unmatched': '#e5534b',
'prettylights-syntax-invalid-illegal-text': '#cdd9e5',
'prettylights-syntax-invalid-illegal-bg': '#922323',
'prettylights-syntax-carriage-return-text': '#cdd9e5',
'prettylights-syntax-carriage-return-bg': '#ad2e2c',
'prettylights-syntax-string-regexp': '#8ddb8c',
'prettylights-syntax-markup-list': '#eac55f',
'prettylights-syntax-markup-heading': '#316dca',
'prettylights-syntax-markup-italic': '#adbac7',
'prettylights-syntax-markup-bold': '#adbac7',
'prettylights-syntax-markup-deleted-text': '#ffd8d3',
'prettylights-syntax-markup-deleted-bg': '#78191b',
'prettylights-syntax-markup-inserted-text': '#b4f1b4',
'prettylights-syntax-markup-inserted-bg': '#1b4721',
'prettylights-syntax-markup-changed-text': '#ffddb0',
'prettylights-syntax-markup-changed-bg': '#682d0f',
'prettylights-syntax-markup-ignored-text': '#adbac7',
'prettylights-syntax-markup-ignored-bg': '#255ab2',
'prettylights-syntax-meta-diff-range': '#dcbdfb',
'prettylights-syntax-brackethighlighter-angle': '#768390',
'prettylights-syntax-sublimelinter-gutter-mark': '#545d68',
'prettylights-syntax-constant-other-reference-link': '#96d0ff',
'codemirror-text': '#adbac7',
'codemirror-bg': '#22272e',
'codemirror-gutters-bg': '#22272e',
'codemirror-guttermarker-text': '#22272e',
'codemirror-guttermarker-subtle-text': '#636e7b',
'codemirror-linenumber-text': '#768390',
'codemirror-cursor': '#cdd9e5',
'codemirror-selection-bg': '#6cb6ff4d',
'codemirror-activeline-bg': '#2d333b',
'codemirror-matchingbracket-text': '#adbac7',
'codemirror-lines-bg': '#22272e',
'codemirror-syntax-comment': '#768390',
'codemirror-syntax-constant': '#6cb6ff',
'codemirror-syntax-entity': '#dcbdfb',
'codemirror-syntax-keyword': '#f47067',
'codemirror-syntax-storage': '#f47067',
'codemirror-syntax-string': '#96d0ff',
'codemirror-syntax-support': '#6cb6ff',
'codemirror-syntax-variable': '#f69d50',
'ansi-black': '#22272e',
'ansi-black-bright': '#2d333b',
'ansi-white': '#909dab',
'ansi-white-bright': '#909dab',
'ansi-gray': '#636e7b',
'ansi-red': '#f47067',
'ansi-red-bright': '#ff938a',
'ansi-green': '#57ab5a',
'ansi-green-bright': '#6bc46d',
'ansi-yellow': '#c69026',
'ansi-yellow-bright': '#daaa3f',
'ansi-blue': '#539bf5',
'ansi-blue-bright': '#6cb6ff',
'ansi-magenta': '#b083f0',
'ansi-magenta-bright': '#dcbdfb',
'ansi-cyan': '#76e3ea',
'ansi-cyan-bright': '#b3f0ff',
);

View file

@ -1,7 +1,6 @@
@import '~nprogress/nprogress.css';
@import './dark/export.scss';
@import './light/export.scss';
@import './themes.scss';
$name: gitako;
@ -14,6 +13,40 @@ $github-header-z-index: 32;
$github-pull-request-float-header-z-index: 110;
$minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-z-index) + 1;
@mixin interactive-background($default, $hover, $active, $focus: $hover) {
background-color: $default;
&:hover {
background-color: $hover;
}
&:focus {
background-color: $focus;
}
&:active {
background-color: $active;
}
}
@mixin interactive-background-on-before($default, $hover, $active, $focus: $hover) {
&::before {
background-color: $default;
}
&:hover {
&::before {
background-color: $hover;
}
}
&:active {
&::before {
background-color: $active;
}
}
&:hover {
&::before {
background-color: $hover;
}
}
}
:root {
--gitako-width: #{$side-bar-base-width};
}
@ -55,6 +88,117 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
}
}
// code folding start
.blob-wrapper table .blob-num {
position: relative; // for positioning
min-width: 60px;
padding-right: 20px;
}
// cancel code fold if not wrapped with .gitako-code-fold-mark
.gitako-code-fold-handler {
display: none;
}
.gitako-code-fold-hidden {
display: table-cell;
}
.gitako-code-fold-attached {
tr {
.gitako-code-fold-handler {
display: initial;
position: absolute;
top: 0px;
right: 0px;
width: 20px;
height: 20px;
display: flex;
justify-content: center;
align-items: center;
&::before {
content: '';
display: block;
width: 10px;
height: 20px;
cursor: pointer;
user-select: none;
transition: 0.25s ease;
-webkit-mask-image: url('~@primer/octicons/build/svg/chevron-down.svg?inline');
mask-image: url('~@primer/octicons/build/svg/chevron-down.svg?inline');
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-position: center;
mask-position: center;
}
@include interactive-background-on-before(
var(--gitako-icon-tertiary),
var(--gitako-icon-primary),
var(--gitako-icon-secondary)
);
}
&.active {
.gitako-code-fold-handler {
&::before {
transform: rotate(-90deg);
}
@include interactive-background-on-before(
var(--gitako-icon-secondary),
var(--gitako-icon-tertiary),
var(--gitako-icon-primary)
);
}
}
}
.gitako-code-fold-hidden {
display: none;
}
}
// code folding end
// clippy button
.markdown-body {
.clippy-wrapper {
position: relative;
width: 0;
height: 0;
top: 8px;
left: calc(100% - 40px);
z-index: 1;
.clippy {
width: 32px;
height: 32px;
border: 1px solid var(--gitako-border-tertiary);
border-radius: 4px;
@include interactive-background(
var(--gitako-btn-bg),
var(--gitako-btn-hover-bg),
var(--gitako-btn-focus-bg)
);
.icon {
width: 100%;
height: 100%;
display: block;
background-image: url('~@primer/octicons-react/build/svg/clippy-16.svg?inline');
background-position: center;
background-repeat: no-repeat;
&.success {
background-image: url('~@primer/octicons-react/build/svg/check-16.svg?inline');
}
&.fail {
background-image: url('~@primer/octicons-react/build/svg/x-16.svg?inline');
}
}
}
}
}
// gitee
&.git-project {
#git-header-nav {
@ -98,45 +242,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
}
}
.markdown-body {
.clippy-wrapper {
position: relative;
width: 0;
height: 0;
top: 8px;
left: calc(100% - 40px);
z-index: 1;
.clippy {
width: 32px;
height: 32px;
border: 1px solid var(--gitako-border-tertiary);
border-radius: 4px;
background: var(--gitako-bg-secondary);
&:hover {
background: var(--gitako-bg-secondary);
}
&:active {
background: var(--gitako-bg-secondary);
}
.icon {
width: 100%;
height: 100%;
display: block;
background-image: url('~@primer/octicons-react/build/svg/clippy-16.svg?inline');
background-position: center;
background-repeat: no-repeat;
&.success {
background-image: url('~@primer/octicons-react/build/svg/check-16.svg?inline');
}
&.fail {
background-image: url('~@primer/octicons-react/build/svg/x-16.svg?inline');
}
}
}
}
}
.progress-pjax-loader.is-loading {
left: 0; /* reposition progress bar of GitHub */
}
@ -145,31 +250,24 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
display: none;
}
.invisible {
visibility: hidden;
}
@mixin flex-center {
display: inline-flex;
justify-content: center;
align-items: center;
}
@mixin icon-button {
@mixin icon-button(
$default: transparent,
$hover: var(--gitako-btn-hover-bg),
$active: var(--gitako-btn-focus-bg),
$focus: $hover
) {
@include flex-center();
cursor: pointer;
padding: 0;
}
@mixin button-color {
border: none;
background: transparent;
&:hover {
background: var(--gitako-btn-hover-bg);
}
&:active {
background: var(--gitako-btn-focus-bg);
}
@include interactive-background($default, $hover, $active, $focus);
}
// Why does not TextInput get theme properly?
@ -187,17 +285,30 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
left: 0px;
display: inline-flex;
align-items: center;
transition: left 0.25s linear;
&.hidden {
left: -40px;
}
.#{$name}-toggle-show-button {
@include icon-button;
@include icon-button(transparent, transparent, transparent);
background: transparent;
border: none;
padding: 0;
position: relative;
left: -8px;
&.error {
cursor: not-allowed;
}
.octoface-icon {
@include flex-center();
width: 32px;
height: 32px;
padding: 4px;
border-radius: 6px;
border: 1px solid var(--gitako-border-tertiary);
background: var(--gitako-bg-tertiary);
@ -207,33 +318,27 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
}
transition: all ease 0.3s;
font-size: 16px;
transform: translateX(0px);
transform: translateX(8px);
&:active,
&:hover {
transform: translateX(4px);
transform: translateX(12px);
}
}
.tentacle {
width: 40px;
height: 40px;
object-fit: contain;
transition: all ease 0.4s;
transform: translateX(-16px);
transform: translateX(-8px);
filter: drop-shadow(0 0 1px var(--gitako-bg-backdrop));
}
&:active,
&:hover {
.tentacle {
transform: translateX(-12px);
transform: translateX(-4px);
filter: drop-shadow(0 0 2px var(--gitako-bg-backdrop));
}
}
img {
filter: drop-shadow(0 0 1px var(--gitako-bg-backdrop));
transition: all ease 0.3s;
width: 90%;
height: 90%;
object-fit: contain;
}
}
.error-message {
@ -264,7 +369,8 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
color: var(--gitako-text-link);
}
.#{$name}-position-wrapper {
$resizeHandlerWidth: 1px;
.#{$name}-side-bar-body-wrapper {
position: fixed;
top: 0;
left: 0;
@ -272,11 +378,21 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
z-index: $minimal-z-index;
display: flex;
&.hidden {
@extend %hidden;
&.toggle-mode-persistent {
&.collapsed {
@extend %hidden;
}
}
.gitako-position-content {
&.toggle-mode-float {
left: 0;
transition: all 0.25s cubic-bezier(0.55, 0.06, 0.68, 0.19); // values from Chrome devtools
&.collapsed {
left: calc(0px - #{$resizeHandlerWidth} - var(--gitako-width));
}
}
.gitako-side-bar-body-wrapper-content {
width: var(--gitako-width);
}
}
@ -287,7 +403,7 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
user-select: none;
width: 0;
background: var(--gitako-bg-tertiary);
border-right: 1px solid var(--gitako-border-tertiary);
border-right: $resizeHandlerWidth solid var(--gitako-border-tertiary);
overflow: hidden;
box-sizing: content-box;
&:hover,
@ -315,10 +431,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
border-left: 1px solid var(--gitako-border-overlay);
overflow: hidden;
&.hidden {
@extend %hidden;
}
.octicon {
transition: transform 0.3s ease;
color: var(--gitako-icon-tertiary);
@ -337,70 +449,79 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
text-align: center;
}
.close-side-bar-button-position {
position: absolute;
right: 6px;
top: 6px;
z-index: 1; // prevent being covered by following elements
.close-side-bar-button {
@include icon-button;
@include button-color;
width: $button-size;
height: $button-size;
border-radius: $button-size;
// feedback to click should be instant
&:not(:active) {
transition: background linear 0.3s;
}
.action-icon {
color: var(--gitako-icon-tertiary);
width: 20px;
height: 20px;
text-align: center;
.octicon {
width: 100%;
height: 100%;
}
}
}
}
.#{$name}-side-bar-content {
display: flex;
flex: 1;
flex-direction: column;
max-height: calc(100vh - 34px); // temporary fix for layout issue occurred since Chrome v76
min-height: 0; // make content shrinkable
.header {
position: relative;
min-height: 62px; // GitHub header height if login
background: var(--gitako-bg-tertiary);
padding: 6px 10px;
flex-shrink: 0;
position: relative; // prevent overlap by outline of other elements
.meta-bar {
position: relative; // prevent overlap by outline of other elements
min-height: 62px; // GitHub header height if login
flex-shrink: 0;
padding: 8px 10px;
padding-right: $button-size + 8px * 2; // space for toggle button
background: var(--gitako-bg-tertiary);
.user-and-repo {
display: inline; // would be override by CSS rule of section from @primer/css
line-height: 32px;
.user-and-repo a {
a {
color: var(--gitako-text-link);
white-space: normal;
}
}
.branch-name {
margin-left: 4px;
padding: 4px 6px; // did not align well for some reason :/
color: var(--gitako-branch-name-text);
background-color: var(--gitako-branch-name-bg);
}
.close-side-bar-button-position {
float: right;
z-index: 1; // prevent being covered by following elements
.close-side-bar-button {
@include icon-button;
width: $button-size;
height: $button-size;
border-radius: $button-size;
// feedback to click should be instant
&:not(:active) {
transition: background linear 0.3s;
}
&.active .octicon {
color: var(--gitako-icon-primary);
}
.action-icon {
color: var(--gitako-icon-tertiary);
width: 20px;
height: 20px;
text-align: center;
.octicon {
width: 100%;
height: 100%;
}
.Pin,
.Tab {
transform: rotateY(180deg);
}
}
}
.branch-name {
.close-side-bar-button + .close-side-bar-button {
margin-left: 4px;
padding: 4px 6px; // did not align well for some reason :/
color: var(--gitako-branch-name-text);
background-color: var(--gitako-branch-name-bg);
}
}
}
.description {
border-top: 1px solid var(--gitako-border-overlay);
.description-area {
padding: 4px 10px;
}
@ -474,25 +595,23 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
align-items: center;
padding: 0 4px;
.toggle-mode {
.toggle-search-mode {
margin: 0;
padding: 2px 4px;
min-width: 32px;
border: 1px solid var(--gitako-border-secondary);
border-radius: 8px;
background: var(--gitako-bg-primary);
color: var(--gitako-text-secondary);
font-size: 12px;
font-weight: 500;
line-height: 1;
outline: none;
&:focus,
&:hover {
background: var(--gitako-bg-secondary);
}
&:active {
background: var(--gitako-bg-tertiary);
}
@include interactive-background(
var(--gitako-bg-primary),
var(--gitako-bg-secondary),
var(--gitako-bg-tertiary)
);
}
}
}
@ -504,15 +623,27 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
}
.node-item {
background: var(--gitako-bg-primary);
&.focused,
&:hover {
background: var(--gitako-bg-tertiary);
text-decoration: initial; // revert underline from .gitako-side-bar a:hover
.node-item-label {
text-decoration: underline; // apply underline like .gitako-side-bar a:hover
}
}
&:active {
background: var(--gitako-bg-secondary);
&.focused {
@include interactive-background(
var(--gitako-bg-tertiary),
var(--gitako-bg-secondary),
var(--gitako-bg-overlay)
);
}
@include interactive-background(
var(--gitako-auto-white),
var(--gitako-bg-secondary),
var(--gitako-bg-tertiary)
);
&.disabled {
pointer-events: none;
color: var(--gitako-text-disabled);
@ -523,10 +654,18 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
margin: 0;
line-height: 20px;
cursor: pointer;
border-bottom: 1px solid var(--gitako-border-secondary);
white-space: nowrap;
transition: padding 0.3s ease; // on toggle expansion in search results
border-bottom: 1px solid var(--gitako-border-secondary);
&.compact {
border-bottom: none;
&.focused {
border-top: 1px solid var(--gitako-border-secondary);
border-bottom: 1px solid var(--gitako-border-secondary);
}
}
&:not(:hover) {
color: var(--gitako-text-primary);
}
@ -579,18 +718,43 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
}
}
.go-to-button,
.find-in-folder-button {
@include icon-button();
@include button-color();
width: 28px;
height: 28px;
border-radius: 28px;
white-space: nowrap;
.octicon-wrapper {
margin-right: 0;
.actions {
z-index: 1; // prevent being covered by next .node-item
padding-right: 8px;
.node-item-comment {
display: inline-block;
width: 48px;
padding: 0 4px;
color: var(--gitako-text-tertiary);
.octicon-wrapper {
margin: 0; // make it closer to the comment amount label
}
}
.go-to-button,
.find-in-folder-button {
@include icon-button();
width: 28px;
height: 28px;
border-radius: 28px;
white-space: nowrap;
.octicon-wrapper {
margin-right: 0;
}
}
}
&.compact {
.go-to-button,
.find-in-folder-button {
border-radius: 4px;
width: 20px;
height: 20px;
}
}
&:not(:hover) {
.go-to-button,
.find-in-folder-button {
@ -722,7 +886,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header-
.settings-button {
@include icon-button();
@include button-color();
$size: 32px;
width: $size;
height: $size;

View file

@ -1,568 +0,0 @@
@use './variables.scss' as variables;
:root {
--gitako-auto-black: #{variables.$auto-black};
--gitako-auto-white: #{variables.$auto-white};
--gitako-auto-gray-0: #{variables.$auto-gray-0};
--gitako-auto-gray-1: #{variables.$auto-gray-1};
--gitako-auto-gray-2: #{variables.$auto-gray-2};
--gitako-auto-gray-3: #{variables.$auto-gray-3};
--gitako-auto-gray-4: #{variables.$auto-gray-4};
--gitako-auto-gray-5: #{variables.$auto-gray-5};
--gitako-auto-gray-6: #{variables.$auto-gray-6};
--gitako-auto-gray-7: #{variables.$auto-gray-7};
--gitako-auto-gray-8: #{variables.$auto-gray-8};
--gitako-auto-gray-9: #{variables.$auto-gray-9};
--gitako-auto-blue-0: #{variables.$auto-blue-0};
--gitako-auto-blue-1: #{variables.$auto-blue-1};
--gitako-auto-blue-2: #{variables.$auto-blue-2};
--gitako-auto-blue-3: #{variables.$auto-blue-3};
--gitako-auto-blue-4: #{variables.$auto-blue-4};
--gitako-auto-blue-5: #{variables.$auto-blue-5};
--gitako-auto-blue-6: #{variables.$auto-blue-6};
--gitako-auto-blue-7: #{variables.$auto-blue-7};
--gitako-auto-blue-8: #{variables.$auto-blue-8};
--gitako-auto-blue-9: #{variables.$auto-blue-9};
--gitako-auto-green-0: #{variables.$auto-green-0};
--gitako-auto-green-1: #{variables.$auto-green-1};
--gitako-auto-green-2: #{variables.$auto-green-2};
--gitako-auto-green-3: #{variables.$auto-green-3};
--gitako-auto-green-4: #{variables.$auto-green-4};
--gitako-auto-green-5: #{variables.$auto-green-5};
--gitako-auto-green-6: #{variables.$auto-green-6};
--gitako-auto-green-7: #{variables.$auto-green-7};
--gitako-auto-green-8: #{variables.$auto-green-8};
--gitako-auto-green-9: #{variables.$auto-green-9};
--gitako-auto-yellow-0: #{variables.$auto-yellow-0};
--gitako-auto-yellow-1: #{variables.$auto-yellow-1};
--gitako-auto-yellow-2: #{variables.$auto-yellow-2};
--gitako-auto-yellow-3: #{variables.$auto-yellow-3};
--gitako-auto-yellow-4: #{variables.$auto-yellow-4};
--gitako-auto-yellow-5: #{variables.$auto-yellow-5};
--gitako-auto-yellow-6: #{variables.$auto-yellow-6};
--gitako-auto-yellow-7: #{variables.$auto-yellow-7};
--gitako-auto-yellow-8: #{variables.$auto-yellow-8};
--gitako-auto-yellow-9: #{variables.$auto-yellow-9};
--gitako-auto-orange-0: #{variables.$auto-orange-0};
--gitako-auto-orange-1: #{variables.$auto-orange-1};
--gitako-auto-orange-2: #{variables.$auto-orange-2};
--gitako-auto-orange-3: #{variables.$auto-orange-3};
--gitako-auto-orange-4: #{variables.$auto-orange-4};
--gitako-auto-orange-5: #{variables.$auto-orange-5};
--gitako-auto-orange-6: #{variables.$auto-orange-6};
--gitako-auto-orange-7: #{variables.$auto-orange-7};
--gitako-auto-orange-8: #{variables.$auto-orange-8};
--gitako-auto-orange-9: #{variables.$auto-orange-9};
--gitako-auto-red-0: #{variables.$auto-red-0};
--gitako-auto-red-1: #{variables.$auto-red-1};
--gitako-auto-red-2: #{variables.$auto-red-2};
--gitako-auto-red-3: #{variables.$auto-red-3};
--gitako-auto-red-4: #{variables.$auto-red-4};
--gitako-auto-red-5: #{variables.$auto-red-5};
--gitako-auto-red-6: #{variables.$auto-red-6};
--gitako-auto-red-7: #{variables.$auto-red-7};
--gitako-auto-red-8: #{variables.$auto-red-8};
--gitako-auto-red-9: #{variables.$auto-red-9};
--gitako-auto-purple-0: #{variables.$auto-purple-0};
--gitako-auto-purple-1: #{variables.$auto-purple-1};
--gitako-auto-purple-2: #{variables.$auto-purple-2};
--gitako-auto-purple-3: #{variables.$auto-purple-3};
--gitako-auto-purple-4: #{variables.$auto-purple-4};
--gitako-auto-purple-5: #{variables.$auto-purple-5};
--gitako-auto-purple-6: #{variables.$auto-purple-6};
--gitako-auto-purple-7: #{variables.$auto-purple-7};
--gitako-auto-purple-8: #{variables.$auto-purple-8};
--gitako-auto-purple-9: #{variables.$auto-purple-9};
--gitako-auto-pink-0: #{variables.$auto-pink-0};
--gitako-auto-pink-1: #{variables.$auto-pink-1};
--gitako-auto-pink-2: #{variables.$auto-pink-2};
--gitako-auto-pink-3: #{variables.$auto-pink-3};
--gitako-auto-pink-4: #{variables.$auto-pink-4};
--gitako-auto-pink-5: #{variables.$auto-pink-5};
--gitako-auto-pink-6: #{variables.$auto-pink-6};
--gitako-auto-pink-7: #{variables.$auto-pink-7};
--gitako-auto-pink-8: #{variables.$auto-pink-8};
--gitako-auto-pink-9: #{variables.$auto-pink-9};
--gitako-text-primary: #{variables.$text-primary};
--gitako-text-secondary: #{variables.$text-secondary};
--gitako-text-tertiary: #{variables.$text-tertiary};
--gitako-text-placeholder: #{variables.$text-placeholder};
--gitako-text-disabled: #{variables.$text-disabled};
--gitako-text-inverse: #{variables.$text-inverse};
--gitako-text-link: #{variables.$text-link};
--gitako-text-danger: #{variables.$text-danger};
--gitako-text-success: #{variables.$text-success};
--gitako-text-warning: #{variables.$text-warning};
--gitako-text-white: #{variables.$text-white};
--gitako-icon-primary: #{variables.$icon-primary};
--gitako-icon-secondary: #{variables.$icon-secondary};
--gitako-icon-tertiary: #{variables.$icon-tertiary};
--gitako-icon-info: #{variables.$icon-info};
--gitako-icon-danger: #{variables.$icon-danger};
--gitako-icon-success: #{variables.$icon-success};
--gitako-icon-warning: #{variables.$icon-warning};
--gitako-border-primary: #{variables.$border-primary};
--gitako-border-secondary: #{variables.$border-secondary};
--gitako-border-tertiary: #{variables.$border-tertiary};
--gitako-border-overlay: #{variables.$border-overlay};
--gitako-border-inverse: #{variables.$border-inverse};
--gitako-border-info: #{variables.$border-info};
--gitako-border-danger: #{variables.$border-danger};
--gitako-border-success: #{variables.$border-success};
--gitako-border-warning: #{variables.$border-warning};
--gitako-bg-canvas: #{variables.$bg-canvas};
--gitako-bg-canvas-mobile: #{variables.$bg-canvas-mobile};
--gitako-bg-canvas-inverse: #{variables.$bg-canvas-inverse};
--gitako-bg-canvas-inset: #{variables.$bg-canvas-inset};
--gitako-bg-primary: #{variables.$bg-primary};
--gitako-bg-secondary: #{variables.$bg-secondary};
--gitako-bg-tertiary: #{variables.$bg-tertiary};
--gitako-bg-overlay: #{variables.$bg-overlay};
--gitako-bg-backdrop: #{variables.$bg-backdrop};
--gitako-bg-info: #{variables.$bg-info};
--gitako-bg-info-inverse: #{variables.$bg-info-inverse};
--gitako-bg-danger: #{variables.$bg-danger};
--gitako-bg-danger-inverse: #{variables.$bg-danger-inverse};
--gitako-bg-success: #{variables.$bg-success};
--gitako-bg-success-inverse: #{variables.$bg-success-inverse};
--gitako-bg-warning: #{variables.$bg-warning};
--gitako-bg-warning-inverse: #{variables.$bg-warning-inverse};
--gitako-shadow-small: #{variables.$shadow-small};
--gitako-shadow-medium: #{variables.$shadow-medium};
--gitako-shadow-large: #{variables.$shadow-large};
--gitako-shadow-extra-large: #{variables.$shadow-extra-large};
--gitako-shadow-highlight: #{variables.$shadow-highlight};
--gitako-shadow-inset: #{variables.$shadow-inset};
--gitako-state-hover-primary-bg: #{variables.$state-hover-primary-bg};
--gitako-state-hover-primary-border: #{variables.$state-hover-primary-border};
--gitako-state-hover-primary-text: #{variables.$state-hover-primary-text};
--gitako-state-hover-primary-icon: #{variables.$state-hover-primary-icon};
--gitako-state-hover-secondary-bg: #{variables.$state-hover-secondary-bg};
--gitako-state-hover-secondary-border: #{variables.$state-hover-secondary-border};
--gitako-state-selected-primary-bg: #{variables.$state-selected-primary-bg};
--gitako-state-selected-primary-border: #{variables.$state-selected-primary-border};
--gitako-state-selected-primary-text: #{variables.$state-selected-primary-text};
--gitako-state-selected-primary-icon: #{variables.$state-selected-primary-icon};
--gitako-state-focus-border: #{variables.$state-focus-border};
--gitako-state-focus-shadow: #{variables.$state-focus-shadow};
--gitako-fade-fg-10: #{variables.$fade-fg-10};
--gitako-fade-fg-15: #{variables.$fade-fg-15};
--gitako-fade-fg-30: #{variables.$fade-fg-30};
--gitako-fade-fg-50: #{variables.$fade-fg-50};
--gitako-fade-fg-70: #{variables.$fade-fg-70};
--gitako-fade-fg-85: #{variables.$fade-fg-85};
--gitako-fade-black-10: #{variables.$fade-black-10};
--gitako-fade-black-15: #{variables.$fade-black-15};
--gitako-fade-black-30: #{variables.$fade-black-30};
--gitako-fade-black-50: #{variables.$fade-black-50};
--gitako-fade-black-70: #{variables.$fade-black-70};
--gitako-fade-black-85: #{variables.$fade-black-85};
--gitako-fade-white-10: #{variables.$fade-white-10};
--gitako-fade-white-15: #{variables.$fade-white-15};
--gitako-fade-white-30: #{variables.$fade-white-30};
--gitako-fade-white-50: #{variables.$fade-white-50};
--gitako-fade-white-70: #{variables.$fade-white-70};
--gitako-fade-white-85: #{variables.$fade-white-85};
--gitako-alert-info-text: #{variables.$alert-info-text};
--gitako-alert-info-icon: #{variables.$alert-info-icon};
--gitako-alert-info-bg: #{variables.$alert-info-bg};
--gitako-alert-info-border: #{variables.$alert-info-border};
--gitako-alert-warn-text: #{variables.$alert-warn-text};
--gitako-alert-warn-icon: #{variables.$alert-warn-icon};
--gitako-alert-warn-bg: #{variables.$alert-warn-bg};
--gitako-alert-warn-border: #{variables.$alert-warn-border};
--gitako-alert-error-text: #{variables.$alert-error-text};
--gitako-alert-error-icon: #{variables.$alert-error-icon};
--gitako-alert-error-bg: #{variables.$alert-error-bg};
--gitako-alert-error-border: #{variables.$alert-error-border};
--gitako-alert-success-text: #{variables.$alert-success-text};
--gitako-alert-success-icon: #{variables.$alert-success-icon};
--gitako-alert-success-bg: #{variables.$alert-success-bg};
--gitako-alert-success-border: #{variables.$alert-success-border};
--gitako-autocomplete-shadow: #{variables.$autocomplete-shadow};
--gitako-autocomplete-row-border: #{variables.$autocomplete-row-border};
--gitako-blankslate-icon: #{variables.$blankslate-icon};
--gitako-btn-text: #{variables.$btn-text};
--gitako-btn-bg: #{variables.$btn-bg};
--gitako-btn-border: #{variables.$btn-border};
--gitako-btn-shadow: #{variables.$btn-shadow};
--gitako-btn-inset-shadow: #{variables.$btn-inset-shadow};
--gitako-btn-hover-bg: #{variables.$btn-hover-bg};
--gitako-btn-hover-border: #{variables.$btn-hover-border};
--gitako-btn-selected-bg: #{variables.$btn-selected-bg};
--gitako-btn-focus-bg: #{variables.$btn-focus-bg};
--gitako-btn-focus-border: #{variables.$btn-focus-border};
--gitako-btn-focus-shadow: #{variables.$btn-focus-shadow};
--gitako-btn-shadow-active: #{variables.$btn-shadow-active};
--gitako-btn-shadow-input-focus: #{variables.$btn-shadow-input-focus};
--gitako-btn-primary-text: #{variables.$btn-primary-text};
--gitako-btn-primary-bg: #{variables.$btn-primary-bg};
--gitako-btn-primary-border: #{variables.$btn-primary-border};
--gitako-btn-primary-shadow: #{variables.$btn-primary-shadow};
--gitako-btn-primary-inset-shadow: #{variables.$btn-primary-inset-shadow};
--gitako-btn-primary-hover-bg: #{variables.$btn-primary-hover-bg};
--gitako-btn-primary-hover-border: #{variables.$btn-primary-hover-border};
--gitako-btn-primary-selected-bg: #{variables.$btn-primary-selected-bg};
--gitako-btn-primary-selected-shadow: #{variables.$btn-primary-selected-shadow};
--gitako-btn-primary-disabled-text: #{variables.$btn-primary-disabled-text};
--gitako-btn-primary-disabled-bg: #{variables.$btn-primary-disabled-bg};
--gitako-btn-primary-disabled-border: #{variables.$btn-primary-disabled-border};
--gitako-btn-primary-focus-bg: #{variables.$btn-primary-focus-bg};
--gitako-btn-primary-focus-border: #{variables.$btn-primary-focus-border};
--gitako-btn-primary-focus-shadow: #{variables.$btn-primary-focus-shadow};
--gitako-btn-primary-icon: #{variables.$btn-primary-icon};
--gitako-btn-primary-counter-bg: #{variables.$btn-primary-counter-bg};
--gitako-btn-outline-text: #{variables.$btn-outline-text};
--gitako-btn-outline-hover-text: #{variables.$btn-outline-hover-text};
--gitako-btn-outline-hover-bg: #{variables.$btn-outline-hover-bg};
--gitako-btn-outline-hover-border: #{variables.$btn-outline-hover-border};
--gitako-btn-outline-hover-shadow: #{variables.$btn-outline-hover-shadow};
--gitako-btn-outline-hover-inset-shadow: #{variables.$btn-outline-hover-inset-shadow};
--gitako-btn-outline-hover-counter-bg: #{variables.$btn-outline-hover-counter-bg};
--gitako-btn-outline-selected-text: #{variables.$btn-outline-selected-text};
--gitako-btn-outline-selected-bg: #{variables.$btn-outline-selected-bg};
--gitako-btn-outline-selected-border: #{variables.$btn-outline-selected-border};
--gitako-btn-outline-selected-shadow: #{variables.$btn-outline-selected-shadow};
--gitako-btn-outline-disabled-text: #{variables.$btn-outline-disabled-text};
--gitako-btn-outline-disabled-bg: #{variables.$btn-outline-disabled-bg};
--gitako-btn-outline-disabled-counter-bg: #{variables.$btn-outline-disabled-counter-bg};
--gitako-btn-outline-focus-border: #{variables.$btn-outline-focus-border};
--gitako-btn-outline-focus-shadow: #{variables.$btn-outline-focus-shadow};
--gitako-btn-outline-counter-bg: #{variables.$btn-outline-counter-bg};
--gitako-btn-danger-text: #{variables.$btn-danger-text};
--gitako-btn-danger-hover-text: #{variables.$btn-danger-hover-text};
--gitako-btn-danger-hover-bg: #{variables.$btn-danger-hover-bg};
--gitako-btn-danger-hover-border: #{variables.$btn-danger-hover-border};
--gitako-btn-danger-hover-shadow: #{variables.$btn-danger-hover-shadow};
--gitako-btn-danger-hover-inset-shadow: #{variables.$btn-danger-hover-inset-shadow};
--gitako-btn-danger-hover-counter-bg: #{variables.$btn-danger-hover-counter-bg};
--gitako-btn-danger-selected-text: #{variables.$btn-danger-selected-text};
--gitako-btn-danger-selected-bg: #{variables.$btn-danger-selected-bg};
--gitako-btn-danger-selected-border: #{variables.$btn-danger-selected-border};
--gitako-btn-danger-selected-shadow: #{variables.$btn-danger-selected-shadow};
--gitako-btn-danger-disabled-text: #{variables.$btn-danger-disabled-text};
--gitako-btn-danger-disabled-bg: #{variables.$btn-danger-disabled-bg};
--gitako-btn-danger-disabled-counter-bg: #{variables.$btn-danger-disabled-counter-bg};
--gitako-btn-danger-focus-border: #{variables.$btn-danger-focus-border};
--gitako-btn-danger-focus-shadow: #{variables.$btn-danger-focus-shadow};
--gitako-btn-danger-counter-bg: #{variables.$btn-danger-counter-bg};
--gitako-btn-counter-bg: #{variables.$btn-counter-bg};
--gitako-counter-text: #{variables.$counter-text};
--gitako-counter-bg: #{variables.$counter-bg};
--gitako-counter-primary-text: #{variables.$counter-primary-text};
--gitako-counter-primary-bg: #{variables.$counter-primary-bg};
--gitako-counter-secondary-text: #{variables.$counter-secondary-text};
--gitako-dropdown-shadow: #{variables.$dropdown-shadow};
--gitako-label-border: #{variables.$label-border};
--gitako-label-primary-text: #{variables.$label-primary-text};
--gitako-label-primary-border: #{variables.$label-primary-border};
--gitako-label-secondary-text: #{variables.$label-secondary-text};
--gitako-label-secondary-border: #{variables.$label-secondary-border};
--gitako-label-info-text: #{variables.$label-info-text};
--gitako-label-info-border: #{variables.$label-info-border};
--gitako-label-success-text: #{variables.$label-success-text};
--gitako-label-success-border: #{variables.$label-success-border};
--gitako-label-warning-text: #{variables.$label-warning-text};
--gitako-label-warning-border: #{variables.$label-warning-border};
--gitako-label-danger-text: #{variables.$label-danger-text};
--gitako-label-danger-border: #{variables.$label-danger-border};
--gitako-label-orange-text: #{variables.$label-orange-text};
--gitako-label-orange-border: #{variables.$label-orange-border};
--gitako-input-bg: #{variables.$input-bg};
--gitako-input-contrast-bg: #{variables.$input-contrast-bg};
--gitako-input-border: #{variables.$input-border};
--gitako-input-shadow: #{variables.$input-shadow};
--gitako-input-disabled-bg: #{variables.$input-disabled-bg};
--gitako-input-disabled-border: #{variables.$input-disabled-border};
--gitako-input-warning-border: #{variables.$input-warning-border};
--gitako-input-error-border: #{variables.$input-error-border};
--gitako-input-tooltip-success-text: #{variables.$input-tooltip-success-text};
--gitako-input-tooltip-success-bg: #{variables.$input-tooltip-success-bg};
--gitako-input-tooltip-success-border: #{variables.$input-tooltip-success-border};
--gitako-input-tooltip-warning-text: #{variables.$input-tooltip-warning-text};
--gitako-input-tooltip-warning-bg: #{variables.$input-tooltip-warning-bg};
--gitako-input-tooltip-warning-border: #{variables.$input-tooltip-warning-border};
--gitako-input-tooltip-error-text: #{variables.$input-tooltip-error-text};
--gitako-input-tooltip-error-bg: #{variables.$input-tooltip-error-bg};
--gitako-input-tooltip-error-border: #{variables.$input-tooltip-error-border};
--gitako-avatar-bg: #{variables.$avatar-bg};
--gitako-avatar-border: #{variables.$avatar-border};
--gitako-avatar-stack-fade: #{variables.$avatar-stack-fade};
--gitako-avatar-stack-fade-more: #{variables.$avatar-stack-fade-more};
--gitako-avatar-child-shadow: #{variables.$avatar-child-shadow};
--gitako-toast-text: #{variables.$toast-text};
--gitako-toast-bg: #{variables.$toast-bg};
--gitako-toast-border: #{variables.$toast-border};
--gitako-toast-shadow: #{variables.$toast-shadow};
--gitako-toast-icon: #{variables.$toast-icon};
--gitako-toast-icon-bg: #{variables.$toast-icon-bg};
--gitako-toast-icon-border: #{variables.$toast-icon-border};
--gitako-toast-success-text: #{variables.$toast-success-text};
--gitako-toast-success-border: #{variables.$toast-success-border};
--gitako-toast-success-icon: #{variables.$toast-success-icon};
--gitako-toast-success-icon-bg: #{variables.$toast-success-icon-bg};
--gitako-toast-success-icon-border: #{variables.$toast-success-icon-border};
--gitako-toast-warning-text: #{variables.$toast-warning-text};
--gitako-toast-warning-border: #{variables.$toast-warning-border};
--gitako-toast-warning-icon: #{variables.$toast-warning-icon};
--gitako-toast-warning-icon-bg: #{variables.$toast-warning-icon-bg};
--gitako-toast-warning-icon-border: #{variables.$toast-warning-icon-border};
--gitako-toast-danger-text: #{variables.$toast-danger-text};
--gitako-toast-danger-border: #{variables.$toast-danger-border};
--gitako-toast-danger-icon: #{variables.$toast-danger-icon};
--gitako-toast-danger-icon-bg: #{variables.$toast-danger-icon-bg};
--gitako-toast-danger-icon-border: #{variables.$toast-danger-icon-border};
--gitako-toast-loading-text: #{variables.$toast-loading-text};
--gitako-toast-loading-border: #{variables.$toast-loading-border};
--gitako-toast-loading-icon: #{variables.$toast-loading-icon};
--gitako-toast-loading-icon-bg: #{variables.$toast-loading-icon-bg};
--gitako-toast-loading-icon-border: #{variables.$toast-loading-icon-border};
--gitako-timeline-text: #{variables.$timeline-text};
--gitako-timeline-badge-bg: #{variables.$timeline-badge-bg};
--gitako-timeline-target-badge-border: #{variables.$timeline-target-badge-border};
--gitako-timeline-target-badge-shadow: #{variables.$timeline-target-badge-shadow};
--gitako-select-menu-border-secondary: #{variables.$select-menu-border-secondary};
--gitako-select-menu-shadow: #{variables.$select-menu-shadow};
--gitako-select-menu-backdrop-bg: #{variables.$select-menu-backdrop-bg};
--gitako-select-menu-backdrop-border: #{variables.$select-menu-backdrop-border};
--gitako-select-menu-tap-highlight: #{variables.$select-menu-tap-highlight};
--gitako-select-menu-tap-focus-bg: #{variables.$select-menu-tap-focus-bg};
--gitako-box-blue-border: #{variables.$box-blue-border};
--gitako-box-row-yellow-bg: #{variables.$box-row-yellow-bg};
--gitako-box-row-blue-bg: #{variables.$box-row-blue-bg};
--gitako-box-header-blue-bg: #{variables.$box-header-blue-bg};
--gitako-box-header-blue-border: #{variables.$box-header-blue-border};
--gitako-box-border-info: #{variables.$box-border-info};
--gitako-box-bg-info: #{variables.$box-bg-info};
--gitako-box-border-warning: #{variables.$box-border-warning};
--gitako-box-bg-warning: #{variables.$box-bg-warning};
--gitako-branch-name-text: #{variables.$branch-name-text};
--gitako-branch-name-icon: #{variables.$branch-name-icon};
--gitako-branch-name-bg: #{variables.$branch-name-bg};
--gitako-branch-name-link-text: #{variables.$branch-name-link-text};
--gitako-branch-name-link-icon: #{variables.$branch-name-link-icon};
--gitako-branch-name-link-bg: #{variables.$branch-name-link-bg};
--gitako-markdown-code-bg: #{variables.$markdown-code-bg};
--gitako-markdown-frame-border: #{variables.$markdown-frame-border};
--gitako-markdown-blockquote-border: #{variables.$markdown-blockquote-border};
--gitako-markdown-table-border: #{variables.$markdown-table-border};
--gitako-markdown-table-tr-border: #{variables.$markdown-table-tr-border};
--gitako-menu-heading-text: #{variables.$menu-heading-text};
--gitako-menu-border-active: #{variables.$menu-border-active};
--gitako-menu-bg-active: #{variables.$menu-bg-active};
--gitako-sidenav-selected-bg: #{variables.$sidenav-selected-bg};
--gitako-sidenav-border-active: #{variables.$sidenav-border-active};
--gitako-header-text: #{variables.$header-text};
--gitako-header-bg: #{variables.$header-bg};
--gitako-header-logo: #{variables.$header-logo};
--gitako-filter-item-bar-bg: #{variables.$filter-item-bar-bg};
--gitako-hidden-text-expander-bg: #{variables.$hidden-text-expander-bg};
--gitako-hidden-text-expander-bg-hover: #{variables.$hidden-text-expander-bg-hover};
--gitako-drag-and-drop-border: #{variables.$drag-and-drop-border};
--gitako-upload-enabled-border: #{variables.$upload-enabled-border};
--gitako-upload-enabled-border-focused: #{variables.$upload-enabled-border-focused};
--gitako-previewable-comment-form-border: #{variables.$previewable-comment-form-border};
--gitako-underlinenav-border: #{variables.$underlinenav-border};
--gitako-underlinenav-border-hover: #{variables.$underlinenav-border-hover};
--gitako-underlinenav-border-active: #{variables.$underlinenav-border-active};
--gitako-underlinenav-text: #{variables.$underlinenav-text};
--gitako-underlinenav-text-hover: #{variables.$underlinenav-text-hover};
--gitako-underlinenav-text-active: #{variables.$underlinenav-text-active};
--gitako-underlinenav-icon: #{variables.$underlinenav-icon};
--gitako-underlinenav-icon-hover: #{variables.$underlinenav-icon-hover};
--gitako-underlinenav-icon-active: #{variables.$underlinenav-icon-active};
--gitako-underlinenav-counter-text: #{variables.$underlinenav-counter-text};
--gitako-verified-badge-text: #{variables.$verified-badge-text};
--gitako-verified-badge-bg: #{variables.$verified-badge-bg};
--gitako-verified-badge-border: #{variables.$verified-badge-border};
--gitako-social-count-bg: #{variables.$social-count-bg};
--gitako-tooltip-text: #{variables.$tooltip-text};
--gitako-tooltip-bg: #{variables.$tooltip-bg};
--gitako-header-search-bg: #{variables.$header-search-bg};
--gitako-header-search-border: #{variables.$header-search-border};
--gitako-search-keyword-hl: #{variables.$search-keyword-hl};
--gitako-diffstat-neutral-bg: #{variables.$diffstat-neutral-bg};
--gitako-diffstat-neutral-border: #{variables.$diffstat-neutral-border};
--gitako-diffstat-deletion-bg: #{variables.$diffstat-deletion-bg};
--gitako-diffstat-deletion-border: #{variables.$diffstat-deletion-border};
--gitako-diffstat-addition-bg: #{variables.$diffstat-addition-bg};
--gitako-diffstat-addition-border: #{variables.$diffstat-addition-border};
--gitako-files-explorer-icon: #{variables.$files-explorer-icon};
--gitako-hl-author-bg: #{variables.$hl-author-bg};
--gitako-hl-author-border: #{variables.$hl-author-border};
--gitako-logo-subdued: #{variables.$logo-subdued};
--gitako-discussion-border: #{variables.$discussion-border};
--gitako-discussion-bg-success: #{variables.$discussion-bg-success};
--gitako-actions-workflow-table-sticky-bg: #{variables.$actions-workflow-table-sticky-bg};
--gitako-repo-language-color-border: #{variables.$repo-language-color-border};
--gitako-code-selection-bg: #{variables.$code-selection-bg};
--gitako-blob-line-highlight-bg: #{variables.$blob-line-highlight-bg};
--gitako-blob-line-highlight-border: #{variables.$blob-line-highlight-border};
--gitako-diff-addition-text: #{variables.$diff-addition-text};
--gitako-diff-addition-bg: #{variables.$diff-addition-bg};
--gitako-diff-addition-border: #{variables.$diff-addition-border};
--gitako-diff-deletion-text: #{variables.$diff-deletion-text};
--gitako-diff-deletion-bg: #{variables.$diff-deletion-bg};
--gitako-diff-deletion-border: #{variables.$diff-deletion-border};
--gitako-diff-change-text: #{variables.$diff-change-text};
--gitako-diff-change-bg: #{variables.$diff-change-bg};
--gitako-diff-change-border: #{variables.$diff-change-border};
--gitako-diff-blob-num-text: #{variables.$diff-blob-num-text};
--gitako-diff-blob-num-hover-text: #{variables.$diff-blob-num-hover-text};
--gitako-diff-blob-addition-num-text: #{variables.$diff-blob-addition-num-text};
--gitako-diff-blob-addition-num-hover-text: #{variables.$diff-blob-addition-num-hover-text};
--gitako-diff-blob-addition-num-bg: #{variables.$diff-blob-addition-num-bg};
--gitako-diff-blob-addition-line-bg: #{variables.$diff-blob-addition-line-bg};
--gitako-diff-blob-addition-word-bg: #{variables.$diff-blob-addition-word-bg};
--gitako-diff-blob-deletion-num-text: #{variables.$diff-blob-deletion-num-text};
--gitako-diff-blob-deletion-num-hover-text: #{variables.$diff-blob-deletion-num-hover-text};
--gitako-diff-blob-deletion-num-bg: #{variables.$diff-blob-deletion-num-bg};
--gitako-diff-blob-deletion-line-bg: #{variables.$diff-blob-deletion-line-bg};
--gitako-diff-blob-deletion-word-bg: #{variables.$diff-blob-deletion-word-bg};
--gitako-diff-blob-hunk-text: #{variables.$diff-blob-hunk-text};
--gitako-diff-blob-hunk-num-bg: #{variables.$diff-blob-hunk-num-bg};
--gitako-diff-blob-hunk-line-bg: #{variables.$diff-blob-hunk-line-bg};
--gitako-diff-blob-empty-block-bg: #{variables.$diff-blob-empty-block-bg};
--gitako-diff-blob-selected-line-highlight-bg: #{variables.$diff-blob-selected-line-highlight-bg};
--gitako-diff-blob-selected-line-highlight-border: #{variables.$diff-blob-selected-line-highlight-border};
--gitako-diff-blob-selected-line-highlight-mix-blend-mode: #{variables.$diff-blob-selected-line-highlight-mix-blend-mode};
--gitako-diff-blob-expander-icon: #{variables.$diff-blob-expander-icon};
--gitako-diff-blob-expander-hover-icon: #{variables.$diff-blob-expander-hover-icon};
--gitako-diff-blob-expander-hover-bg: #{variables.$diff-blob-expander-hover-bg};
--gitako-diff-blob-comment-button-icon: #{variables.$diff-blob-comment-button-icon};
--gitako-diff-blob-comment-button-bg: #{variables.$diff-blob-comment-button-bg};
--gitako-diff-blob-comment-button-gradient-bg: #{variables.$diff-blob-comment-button-gradient-bg};
--gitako-global-nav-logo: #{variables.$global-nav-logo};
--gitako-global-nav-bg: #{variables.$global-nav-bg};
--gitako-global-nav-text: #{variables.$global-nav-text};
--gitako-global-nav-icon: #{variables.$global-nav-icon};
--gitako-global-nav-input-bg: #{variables.$global-nav-input-bg};
--gitako-global-nav-input-border: #{variables.$global-nav-input-border};
--gitako-global-nav-input-icon: #{variables.$global-nav-input-icon};
--gitako-global-nav-input-placeholder: #{variables.$global-nav-input-placeholder};
--gitako-calendar-graph-day-bg: #{variables.$calendar-graph-day-bg};
--gitako-calendar-graph-day-border: #{variables.$calendar-graph-day-border};
--gitako-calendar-graph-day-l1-bg: #{variables.$calendar-graph-day-l1-bg};
--gitako-calendar-graph-day-l2-bg: #{variables.$calendar-graph-day-l2-bg};
--gitako-calendar-graph-day-l3-bg: #{variables.$calendar-graph-day-l3-bg};
--gitako-calendar-graph-day-l4-bg: #{variables.$calendar-graph-day-l4-bg};
--gitako-calendar-graph-day-l4-border: #{variables.$calendar-graph-day-l4-border};
--gitako-calendar-graph-day-l3-border: #{variables.$calendar-graph-day-l3-border};
--gitako-calendar-graph-day-l2-border: #{variables.$calendar-graph-day-l2-border};
--gitako-calendar-graph-day-l1-border: #{variables.$calendar-graph-day-l1-border};
--gitako-footer-invertocat-octicon: #{variables.$footer-invertocat-octicon};
--gitako-footer-invertocat-octicon-hover: #{variables.$footer-invertocat-octicon-hover};
--gitako-pr-state-draft-text: #{variables.$pr-state-draft-text};
--gitako-pr-state-draft-bg: #{variables.$pr-state-draft-bg};
--gitako-pr-state-draft-border: #{variables.$pr-state-draft-border};
--gitako-pr-state-open-text: #{variables.$pr-state-open-text};
--gitako-pr-state-open-bg: #{variables.$pr-state-open-bg};
--gitako-pr-state-open-border: #{variables.$pr-state-open-border};
--gitako-pr-state-merged-text: #{variables.$pr-state-merged-text};
--gitako-pr-state-merged-bg: #{variables.$pr-state-merged-bg};
--gitako-pr-state-merged-border: #{variables.$pr-state-merged-border};
--gitako-pr-state-closed-text: #{variables.$pr-state-closed-text};
--gitako-pr-state-closed-bg: #{variables.$pr-state-closed-bg};
--gitako-pr-state-closed-border: #{variables.$pr-state-closed-border};
--gitako-topic-tag-text: #{variables.$topic-tag-text};
--gitako-topic-tag-bg: #{variables.$topic-tag-bg};
--gitako-topic-tag-hover-bg: #{variables.$topic-tag-hover-bg};
--gitako-topic-tag-active-bg: #{variables.$topic-tag-active-bg};
--gitako-merge-box-success-icon-bg: #{variables.$merge-box-success-icon-bg};
--gitako-merge-box-success-icon-text: #{variables.$merge-box-success-icon-text};
--gitako-merge-box-success-icon-border: #{variables.$merge-box-success-icon-border};
--gitako-merge-box-success-indicator-bg: #{variables.$merge-box-success-indicator-bg};
--gitako-merge-box-success-indicator-border: #{variables.$merge-box-success-indicator-border};
--gitako-merge-box-merged-icon-bg: #{variables.$merge-box-merged-icon-bg};
--gitako-merge-box-merged-icon-text: #{variables.$merge-box-merged-icon-text};
--gitako-merge-box-merged-icon-border: #{variables.$merge-box-merged-icon-border};
--gitako-merge-box-merged-box-border: #{variables.$merge-box-merged-box-border};
--gitako-merge-box-neutral-icon-bg: #{variables.$merge-box-neutral-icon-bg};
--gitako-merge-box-neutral-icon-text: #{variables.$merge-box-neutral-icon-text};
--gitako-merge-box-neutral-icon-border: #{variables.$merge-box-neutral-icon-border};
--gitako-merge-box-neutral-indicator-bg: #{variables.$merge-box-neutral-indicator-bg};
--gitako-merge-box-neutral-indicator-border: #{variables.$merge-box-neutral-indicator-border};
--gitako-merge-box-warning-icon-bg: #{variables.$merge-box-warning-icon-bg};
--gitako-merge-box-warning-icon-text: #{variables.$merge-box-warning-icon-text};
--gitako-merge-box-warning-icon-border: #{variables.$merge-box-warning-icon-border};
--gitako-merge-box-warning-box-border: #{variables.$merge-box-warning-box-border};
--gitako-merge-box-warning-merge-highlight: #{variables.$merge-box-warning-merge-highlight};
--gitako-merge-box-error-icon-bg: #{variables.$merge-box-error-icon-bg};
--gitako-merge-box-error-icon-text: #{variables.$merge-box-error-icon-text};
--gitako-merge-box-error-icon-border: #{variables.$merge-box-error-icon-border};
--gitako-merge-box-error-indicator-bg: #{variables.$merge-box-error-indicator-bg};
--gitako-merge-box-error-indicator-border: #{variables.$merge-box-error-indicator-border};
--gitako-project-card-bg: #{variables.$project-card-bg};
--gitako-project-header-bg: #{variables.$project-header-bg};
--gitako-project-sidebar-bg: #{variables.$project-sidebar-bg};
--gitako-project-gradient-in: #{variables.$project-gradient-in};
--gitako-project-gradient-out: #{variables.$project-gradient-out};
--gitako-marketing-icon-primary: #{variables.$marketing-icon-primary};
--gitako-marketing-icon-secondary: #{variables.$marketing-icon-secondary};
--gitako-prettylights-syntax-comment: #{variables.$prettylights-syntax-comment};
--gitako-prettylights-syntax-constant: #{variables.$prettylights-syntax-constant};
--gitako-prettylights-syntax-entity: #{variables.$prettylights-syntax-entity};
--gitako-prettylights-syntax-storage-modifier-import: #{variables.$prettylights-syntax-storage-modifier-import};
--gitako-prettylights-syntax-entity-tag: #{variables.$prettylights-syntax-entity-tag};
--gitako-prettylights-syntax-keyword: #{variables.$prettylights-syntax-keyword};
--gitako-prettylights-syntax-string: #{variables.$prettylights-syntax-string};
--gitako-prettylights-syntax-variable: #{variables.$prettylights-syntax-variable};
--gitako-prettylights-syntax-brackethighlighter-unmatched: #{variables.$prettylights-syntax-brackethighlighter-unmatched};
--gitako-prettylights-syntax-invalid-illegal-text: #{variables.$prettylights-syntax-invalid-illegal-text};
--gitako-prettylights-syntax-invalid-illegal-bg: #{variables.$prettylights-syntax-invalid-illegal-bg};
--gitako-prettylights-syntax-carriage-return-text: #{variables.$prettylights-syntax-carriage-return-text};
--gitako-prettylights-syntax-carriage-return-bg: #{variables.$prettylights-syntax-carriage-return-bg};
--gitako-prettylights-syntax-string-regexp: #{variables.$prettylights-syntax-string-regexp};
--gitako-prettylights-syntax-markup-list: #{variables.$prettylights-syntax-markup-list};
--gitako-prettylights-syntax-markup-heading: #{variables.$prettylights-syntax-markup-heading};
--gitako-prettylights-syntax-markup-italic: #{variables.$prettylights-syntax-markup-italic};
--gitako-prettylights-syntax-markup-bold: #{variables.$prettylights-syntax-markup-bold};
--gitako-prettylights-syntax-markup-deleted-text: #{variables.$prettylights-syntax-markup-deleted-text};
--gitako-prettylights-syntax-markup-deleted-bg: #{variables.$prettylights-syntax-markup-deleted-bg};
--gitako-prettylights-syntax-markup-inserted-text: #{variables.$prettylights-syntax-markup-inserted-text};
--gitako-prettylights-syntax-markup-inserted-bg: #{variables.$prettylights-syntax-markup-inserted-bg};
--gitako-prettylights-syntax-markup-changed-text: #{variables.$prettylights-syntax-markup-changed-text};
--gitako-prettylights-syntax-markup-changed-bg: #{variables.$prettylights-syntax-markup-changed-bg};
--gitako-prettylights-syntax-markup-ignored-text: #{variables.$prettylights-syntax-markup-ignored-text};
--gitako-prettylights-syntax-markup-ignored-bg: #{variables.$prettylights-syntax-markup-ignored-bg};
--gitako-prettylights-syntax-meta-diff-range: #{variables.$prettylights-syntax-meta-diff-range};
--gitako-prettylights-syntax-brackethighlighter-angle: #{variables.$prettylights-syntax-brackethighlighter-angle};
--gitako-prettylights-syntax-sublimelinter-gutter-mark: #{variables.$prettylights-syntax-sublimelinter-gutter-mark};
--gitako-prettylights-syntax-constant-other-reference-link: #{variables.$prettylights-syntax-constant-other-reference-link};
--gitako-codemirror-text: #{variables.$codemirror-text};
--gitako-codemirror-bg: #{variables.$codemirror-bg};
--gitako-codemirror-gutters-bg: #{variables.$codemirror-gutters-bg};
--gitako-codemirror-guttermarker-text: #{variables.$codemirror-guttermarker-text};
--gitako-codemirror-guttermarker-subtle-text: #{variables.$codemirror-guttermarker-subtle-text};
--gitako-codemirror-linenumber-text: #{variables.$codemirror-linenumber-text};
--gitako-codemirror-cursor: #{variables.$codemirror-cursor};
--gitako-codemirror-selection-bg: #{variables.$codemirror-selection-bg};
--gitako-codemirror-activeline-bg: #{variables.$codemirror-activeline-bg};
--gitako-codemirror-matchingbracket-text: #{variables.$codemirror-matchingbracket-text};
--gitako-codemirror-lines-bg: #{variables.$codemirror-lines-bg};
--gitako-codemirror-syntax-comment: #{variables.$codemirror-syntax-comment};
--gitako-codemirror-syntax-constant: #{variables.$codemirror-syntax-constant};
--gitako-codemirror-syntax-entity: #{variables.$codemirror-syntax-entity};
--gitako-codemirror-syntax-keyword: #{variables.$codemirror-syntax-keyword};
--gitako-codemirror-syntax-storage: #{variables.$codemirror-syntax-storage};
--gitako-codemirror-syntax-string: #{variables.$codemirror-syntax-string};
--gitako-codemirror-syntax-support: #{variables.$codemirror-syntax-support};
--gitako-codemirror-syntax-variable: #{variables.$codemirror-syntax-variable};
--gitako-ansi-black: #{variables.$ansi-black};
--gitako-ansi-black-bright: #{variables.$ansi-black-bright};
--gitako-ansi-white: #{variables.$ansi-white};
--gitako-ansi-white-bright: #{variables.$ansi-white-bright};
--gitako-ansi-gray: #{variables.$ansi-gray};
--gitako-ansi-red: #{variables.$ansi-red};
--gitako-ansi-red-bright: #{variables.$ansi-red-bright};
--gitako-ansi-green: #{variables.$ansi-green};
--gitako-ansi-green-bright: #{variables.$ansi-green-bright};
--gitako-ansi-yellow: #{variables.$ansi-yellow};
--gitako-ansi-yellow-bright: #{variables.$ansi-yellow-bright};
--gitako-ansi-blue: #{variables.$ansi-blue};
--gitako-ansi-blue-bright: #{variables.$ansi-blue-bright};
--gitako-ansi-magenta: #{variables.$ansi-magenta};
--gitako-ansi-magenta-bright: #{variables.$ansi-magenta-bright};
--gitako-ansi-cyan: #{variables.$ansi-cyan};
--gitako-ansi-cyan-bright: #{variables.$ansi-cyan-bright};
}

File diff suppressed because it is too large Load diff

64
src/styles/themes.scss Normal file
View file

@ -0,0 +1,64 @@
@use './light/variables.scss' as light;
@use './dark/variables.scss' as dark;
@use './darkDimmed/variables.scss' as darkDimmed;
@mixin setVariables($variables) {
@each $name, $value in $variables {
--gitako-#{$name}: #{$value};
}
}
// Following rules are organized according to common variables to reduce output CSS size
:root {
// default, e.g. when not login
@include setVariables(light.$variables);
}
:root {
&[data-color-mode='light'][data-light-theme='light'],
&[data-color-mode='dark'][data-dark-theme='light'] {
@include setVariables(light.$variables);
}
&[data-color-mode='light'][data-light-theme='dark'],
&[data-color-mode='dark'][data-dark-theme='dark'] {
@include setVariables(dark.$variables);
}
&[data-color-mode='light'][data-light-theme='dark_dimmed'],
&[data-color-mode='dark'][data-dark-theme='dark_dimmed'] {
@include setVariables(darkDimmed.$variables);
}
}
@media (prefers-color-scheme: light) {
:root[data-color-mode='auto'] {
&[data-light-theme='light'] {
@include setVariables(light.$variables);
}
&[data-light-theme='dark'] {
@include setVariables(dark.$variables);
}
&[data-light-theme='dark_dimmed'] {
@include setVariables(darkDimmed.$variables);
}
}
}
@media (prefers-color-scheme: dark) {
:root[data-color-mode='auto'] {
&[data-dark-theme='light'] {
@include setVariables(light.$variables);
}
&[data-dark-theme='dark'] {
@include setVariables(dark.$variables);
}
&[data-dark-theme='dark_dimmed'] {
@include setVariables(darkDimmed.$variables);
}
}
}

View file

@ -0,0 +1,91 @@
import { SearchMode } from 'components/searchModes'
import { platform, platformName } from 'platforms'
import { storageHelper } from 'utils/storageHelper'
import { migrateConfig } from './migrations'
export type Config = {
sideBarWidth: number
shortcut: string | undefined
accessToken: string | undefined
compressSingletonFolder: boolean
copyFileButton: boolean
copySnippetButton: boolean
intelligentToggle: boolean | null // `null` stands for intelligent, boolean for sidebar open state
icons: 'rich' | 'dim' | 'native'
toggleButtonVerticalDistance: number
toggleButtonContent: 'logo' | 'octoface'
recursiveToggleFolder: 'shift' | 'alt'
searchMode: SearchMode
sidebarToggleMode: 'persistent' | 'float'
commentToggle: boolean
codeFolding: boolean
compactFileTree: boolean
}
enum configKeys {
sideBarWidth = 'sideBarWidth',
shortcut = 'shortcut',
accessToken = 'accessToken',
compressSingletonFolder = 'compressSingletonFolder',
copyFileButton = 'copyFileButton',
copySnippetButton = 'copySnippetButton',
intelligentToggle = 'intelligentToggle',
icons = 'icons',
toggleButtonVerticalDistance = 'toggleButtonVerticalDistance',
toggleButtonContent = 'toggleButtonContent',
recursiveToggleFolder = 'recursiveToggleFolder',
searchMode = 'searchMode',
sidebarToggleMode = 'sidebarToggleMode',
commentToggle = 'commentToggle',
codeFolding = 'codeFolding',
compactFileTree = 'compactFileTree',
}
export const defaultConfigs: Config = {
sideBarWidth: 260,
shortcut: undefined,
accessToken: '',
compressSingletonFolder: true,
copyFileButton: true,
copySnippetButton: !(platformName === 'GitHub' && !platform.isEnterprise()), // false when on github.com
intelligentToggle: null,
icons: 'rich',
toggleButtonVerticalDistance: 124, // align with GitHub's navbar items
toggleButtonContent: 'logo',
recursiveToggleFolder: 'shift',
searchMode: 'fuzzy',
sidebarToggleMode: 'float',
commentToggle: true,
codeFolding: true,
compactFileTree: false,
}
const configKeyArray = Object.values(configKeys)
function applyDefaultConfigs(configs: Partial<Config>) {
return configKeyArray.reduce((applied, key) => {
Object.assign(applied, { [key]: key in configs ? configs[key] : defaultConfigs[key] })
return applied
}, {} as Config)
}
export type VersionedConfig<SiteConfig> = Record<string, SiteConfig> & { configVersion: string }
// do NOT use platform name
const platformStorageKey = `platform_` + window.location.host.toLowerCase()
const prepareConfig = new Promise<void>(async resolve => {
await migrateConfig()
resolve()
})
async function get(): Promise<Config> {
await prepareConfig
const config = await storageHelper.get<Record<string, Config>>([platformStorageKey])
return applyDefaultConfigs(config?.[platformStorageKey] || {})
}
async function set(config: Config) {
return await storageHelper.set({ [platformStorageKey]: config })
}
export const configHelper = { get, set }

View file

@ -0,0 +1,23 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration } from '.'
import { Storage } from '../../storageHelper'
export const migration: Migration = {
version: '1.0.1',
async migrate(version) {
const config: any | void = await storageHelper.get<Storage>([
'configVersion',
'sideBarWidth',
'shortcut',
'access_token',
'compressSingletonFolder',
'copyFileButton',
'copySnippetButton',
'intelligentToggle',
'icons',
])
if (config && (!('configVersion' in config) || config.configVersion < version)) {
await storageHelper.set({ platform_GitHub: config, configVersion: version })
}
},
}

View file

@ -0,0 +1,28 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration } from '.'
import { Storage } from '../../storageHelper'
import { Config, VersionedConfig } from '../helper'
export const migration: Migration = {
version: '1.3.4',
async migrate(version) {
const config: any | void = await storageHelper.get<VersionedConfig<Config> & Storage>([
'configVersion',
'platform_undefined',
'platform_GitHub',
'platform_github.com',
])
if (
config &&
'configVersion' in config &&
config.configVersion < version &&
(config.platform_GitHub || config.platform_undefined) &&
!config['platform_github.com']
) {
await storageHelper.set({
['platform_github.com']: config.platform_GitHub || config.platform_undefined,
configVersion: version,
})
}
},
}

View file

@ -0,0 +1,34 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration, onConfigOutdated } from '.'
export const migration: Migration = {
version: '2.6.0',
async migrate(version) {
type ConfigBeforeMigrate = {
access_token?: string
}
type ConfigAfterMigrate = {
accessToken?: string
}
await onConfigOutdated(version, async configs => {
for (const key of Object.keys(configs)) {
if (
typeof configs[key] === 'object' &&
configs[key] !== null &&
'access_token' in configs[key]
) {
const configBeforeMigrate: ConfigBeforeMigrate = configs[key]
const { access_token: accessToken, ...rest } = configBeforeMigrate
const configAfterMigrate: ConfigAfterMigrate = {
...rest,
accessToken,
}
await storageHelper.set({
[key]: configAfterMigrate,
})
}
}
})
},
}

View file

@ -0,0 +1,33 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration, onConfigOutdated } from '.'
export const migration: Migration = {
version: '3.0.0',
async migrate(version) {
// disable copy snippet button for github.com
type ConfigBeforeMigrate = {
copySnippetButton: boolean
}
type ConfigAfterMigrate = {
copySnippetButton: boolean
}
await onConfigOutdated(version, async configs => {
const key = 'platform_github.com'
const config = configs[key]
if (typeof config === 'object' && config !== null && 'copySnippetButton' in config) {
const configBeforeMigrate: ConfigBeforeMigrate = config
const { copySnippetButton, ...rest } = configBeforeMigrate
if (copySnippetButton) {
const configAfterMigrate: ConfigAfterMigrate = {
...rest,
copySnippetButton: false,
}
await storageHelper.set({
[key]: configAfterMigrate,
})
}
}
})
},
}

View file

@ -0,0 +1,32 @@
import { storageHelper } from 'utils/storageHelper'
import { Storage } from '../../storageHelper'
import { migration as v1v0v1 } from './1.0.1'
import { migration as v1v3v4 } from './1.3.4'
import { migration as v2v6v0 } from './2.6.0'
import { migration as v3v0v0 } from './3.0.0'
export type Migration = {
version: string
migrate(version: Migration['version']): Async<void>
}
export async function migrateConfig() {
const migrations: Migration[] = [v1v0v1, v1v3v4, v2v6v0, v3v0v0]
for (const { version, migrate } of migrations) {
await migrate(version)
}
}
export async function onConfigOutdated<T extends { [key: string]: any }>(
configVersion: string,
runIfOutdated: (config: T) => Async<void>,
) {
const config = await storageHelper.get<Storage>()
if (config && config.configVersion < configVersion) {
const { configVersion: $configVersion, ...restConfig } = config
await runIfOutdated(restConfig as T)
await storageHelper.set({ configVersion })
}
}

View file

@ -1,175 +0,0 @@
import { SearchMode } from 'components/searchModes'
import * as storageHelper from 'utils/storageHelper'
export type Config = {
sideBarWidth: number
shortcut: string | undefined
accessToken: string | undefined
compressSingletonFolder: boolean
copyFileButton: boolean
copySnippetButton: boolean
intelligentToggle: boolean | null // `null` stands for intelligent, boolean for sidebar open status
icons: 'rich' | 'dim' | 'native'
toggleButtonVerticalDistance: number
toggleButtonContent: 'logo' | 'octoface'
recursiveToggleFolder: 'shift' | 'alt'
searchMode: SearchMode
}
enum configKeys {
sideBarWidth = 'sideBarWidth',
shortcut = 'shortcut',
accessToken = 'accessToken',
compressSingletonFolder = 'compressSingletonFolder',
copyFileButton = 'copyFileButton',
copySnippetButton = 'copySnippetButton',
intelligentToggle = 'intelligentToggle',
icons = 'icons',
toggleButtonVerticalDistance = 'toggleButtonVerticalDistance',
toggleButtonContent = 'toggleButtonContent',
recursiveToggleFolder = 'recursiveToggleFolder',
searchMode = 'searchMode',
}
const defaultConfigs: Config = {
sideBarWidth: 260,
shortcut: undefined,
accessToken: '',
compressSingletonFolder: true,
copyFileButton: true,
copySnippetButton: true,
intelligentToggle: null,
icons: 'rich',
toggleButtonVerticalDistance: 124, // align with GitHub's navbar items
toggleButtonContent: 'logo',
recursiveToggleFolder: 'shift',
searchMode: 'fuzzy',
}
const configKeyArray = Object.values(configKeys)
function applyDefaultConfigs(configs: Partial<Config>) {
return configKeyArray.reduce((applied, key) => {
Object.assign(applied, { [key]: key in configs ? configs[key] : defaultConfigs[key] })
return applied
}, {} as Config)
}
type VersionedConfig<SiteConfig> = Record<string, SiteConfig> & { configVersion: string }
type Storage = {
// save root level `configVersion` for easier future migrating
[key in 'configVersion' | string]: string
// separate different platform configs to simplify interactions with browser storage API
// e.g.
// platform_github.com?: Config
}
async function migrateConfig() {
// not referencing to enum above to prevent migrate future configs
const migrations: {
version: string
migrate(version: string): Promise<void>
}[] = [
{
version: '1.0.1',
async migrate(version) {
const config: any | void = await storageHelper.get<Storage>([
'configVersion',
'sideBarWidth',
'shortcut',
'access_token',
'compressSingletonFolder',
'copyFileButton',
'copySnippetButton',
'intelligentToggle',
'icons',
])
if (config && (!('configVersion' in config) || config.configVersion < version)) {
await storageHelper.set({ platform_GitHub: config, configVersion: version })
}
},
},
{
version: '1.3.4',
async migrate(version) {
const config: any | void = await storageHelper.get<VersionedConfig<Config> & Storage>([
'configVersion',
'platform_undefined', // this was a mistake :(
'platform_GitHub',
'platform_github.com',
])
if (
config &&
'configVersion' in config &&
config.configVersion < version &&
(config.platform_GitHub || config.platform_undefined) &&
!config['platform_github.com']
) {
await storageHelper.set({
['platform_github.com']: config.platform_GitHub || config.platform_undefined,
configVersion: version,
})
}
},
},
{
version: '2.6.0',
async migrate(version) {
type LegacySiteConfig = {
access_token?: string
}
type MigratedSiteConfig = {
accessToken?: string
}
const config = await storageHelper.get<VersionedConfig<LegacySiteConfig> & Storage>()
if (config && config.configVersion < version) {
const { configVersion, ...restConfig } = config
for (const key of Object.keys(restConfig)) {
if (
typeof restConfig[key] === 'object' &&
restConfig[key] &&
'access_token' in restConfig[key]
) {
const config: LegacySiteConfig = restConfig[key]
const { access_token: accessToken, ...legacy } = config
const migrated: MigratedSiteConfig = {
...legacy,
accessToken,
}
await storageHelper.set({
[key]: migrated,
})
}
}
await storageHelper.set({
configVersion: version,
})
}
},
},
]
for (const { version, migrate } of migrations) {
await migrate(version)
}
}
// do NOT use platform name
const platformStorageKey = `platform_` + window.location.host.toLowerCase()
const prepareConfig = new Promise(async resolve => {
await migrateConfig()
resolve()
})
export async function get(): Promise<Config> {
await prepareConfig
const config = await storageHelper.get<Record<string, Config>>([platformStorageKey])
return applyDefaultConfigs(config?.[platformStorageKey] || {})
}
export async function set(config: Config) {
return await storageHelper.set({ [platformStorageKey]: config })
}

View file

@ -1,18 +1,28 @@
type CXValue = string | boolean | null | undefined
/**
* cx('class1', { class2: true, class3: false }) --> 'class1 class2'
*/
export function cx(...classNames: any[]): string {
return classNames
.filter(Boolean)
.map(className => {
switch (typeof className) {
case 'string':
return className
case 'object':
return cx(...Object.entries(className).map(([key, value]) => (value ? key : null)))
default:
return ''
export function cx(
...classNames: (
| CXValue
| {
[className: string]: CXValue
}
})
.join(' ')
)[]
): string {
return classNames.map(handleClassName).filter(Boolean).join(' ')
}
function handleClassName(className: Parameters<typeof cx>[number]): string | false {
switch (typeof className) {
case 'string':
return className
case 'object':
return className === null
? false
: cx(...Object.entries(className).map(([key, value]) => (value ? key : false)))
default:
return false
}
}

View file

@ -105,23 +105,8 @@ export function setStyleSheetMedia(style: HTMLStyleElement, media: string) {
style.setAttribute('media', media)
}
export function parseURLSearch(search: string = window.location.search) {
const parsed: any = {}
if (search.startsWith('?')) {
const pairs = search
.slice(1)
.split('&')
.map(rawPair => rawPair.split('=').map(raw => decodeURIComponent(raw))) // [key, value?][]
pairs.forEach(([key, value]) => {
if (Object.prototype.hasOwnProperty.call(parsed, key)) {
if (Array.isArray(parsed[key])) parsed[key].push(value)
else parsed[key] = [parsed[key], value]
} else {
parsed[key] = value
}
})
}
return parsed
export function parseURLSearch(search = window.location.search) {
return new URLSearchParams(search)
}
export async function JSONRequest(url: string, data: any, extra: RequestInit = { method: 'post' }) {

View file

@ -0,0 +1,45 @@
import { useConfigs } from 'containers/ConfigsContext'
import { errors, platformName } from 'platforms'
import { useCallback } from 'react'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { SideBarErrorContext } from '../../containers/ErrorContext'
import { SideBarStateContext } from '../../containers/SideBarState'
export function useCatchNetworkError() {
const { accessToken } = useConfigs().value
const stateContext = useLoadedContext(SideBarStateContext)
const errorContext = useLoadedContext(SideBarErrorContext)
return useCallback(
async function <T>(fn: () => T) {
try {
return await fn() // keep the await so that catch block can catch async errors
} catch (err) {
if (err.message === errors.EMPTY_PROJECT) {
errorContext.onChange('This project seems to be empty.')
} else if (err.message === errors.BLOCKED_PROJECT) {
errorContext.onChange('Access to the project is blocked.')
} else if (
err.message === errors.NOT_FOUND ||
err.message === errors.BAD_CREDENTIALS ||
err.message === errors.API_RATE_LIMIT
) {
stateContext.onChange('error-due-to-auth')
} else if (err.message === errors.CONNECTION_BLOCKED) {
if (accessToken) {
errorContext.onChange(`Cannot connect to ${platformName}.`)
} else {
stateContext.onChange('error-due-to-auth')
}
} else if (err.message === errors.SERVER_FAULT) {
errorContext.onChange(`${platformName} server went down.`)
} else {
stateContext.onChange('disabled')
errorContext.onChange('Something unexpected happened.')
throw err
}
}
},
[accessToken /* , stateContext.value, errorContext.value */],
)
}

View file

@ -0,0 +1,9 @@
import * as React from 'react'
export function useEffectOnSerializableUpdates<T>(
value: T,
serialize: (value: T) => string,
onChange: (value: T) => void,
) {
React.useEffect(() => onChange(value), [onChange, serialize(value)])
}

View file

@ -0,0 +1,7 @@
import * as React from 'react'
export function useLoadedContext<T>(context: React.Context<T | null>): T {
const ctx = React.useContext(context)
if (ctx === null) throw new Error(`Context not loaded`)
return ctx
}

View file

@ -0,0 +1,27 @@
import { ConfigsContextShape } from 'containers/ConfigsContext'
import * as React from 'react'
import * as keyHelper from 'utils/keyHelper'
import { SideBarState } from '../../containers/SideBarState'
export function useToggleSideBarWithKeyboard(
state: SideBarState,
configContext: ConfigsContextShape,
toggleShowSideBar: () => void,
) {
const isDisabled = state === 'disabled'
React.useEffect(
function attachKeyDown() {
if (isDisabled || !configContext.value.shortcut) return
function onKeyDown(e: KeyboardEvent) {
const keys = keyHelper.parseEvent(e)
if (keys === configContext.value.shortcut) {
toggleShowSideBar()
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
},
[toggleShowSideBar, isDisabled, configContext.value.shortcut],
)
}

View file

@ -1,6 +1,15 @@
const localStorage = browser.storage.local
export async function get<
export type Storage = {
// save root level `configVersion` for easier future migrating
[key in EnumString<'configVersion'>]: string
// separate different platform configs to simplify interactions with browser storage API
// e.g.
// ['platform_github.com']?: Config
}
async function get<
T extends {
[key: string]: any
}
@ -10,8 +19,10 @@ export async function get<
} catch (err) {}
}
export function set(value: any): Promise<void> | void {
function set(value: any): Promise<void> | void {
try {
return localStorage.set(value)
} catch (err) {}
}
export const storageHelper = { get, set }

View file

@ -7,6 +7,7 @@
"strict": true,
"lib": ["dom", "es2017.object", "es2016", "ES2020.String"],
"baseUrl": "src",
"allowSyntheticDefaultImports": true,
"outDir": "dist"
},
"include": ["src"]

226
yarn.lock
View file

@ -1428,68 +1428,68 @@
resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.3.0.tgz#2098181aab751f2275cacf219b02a6a8b455be85"
integrity sha512-dQA1acyNpwqy5ia5yt1lNjaAhm9rrzSurFtlJPoz7ARVPMV1yZ0yfmNotMwwgVbE5q1HnONqszK7oagH86m7Qw==
"@sentry/browser@^5.12.1":
version "5.12.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.12.1.tgz#dc1f268595269fb7277f55eb625c7e92d76dc01b"
integrity sha512-Zl7VdppUxctyaoqMSEhnDJp2rrupx8n8N2n3PSooH74yhB2Z91nt84mouczprBsw3JU1iggGyUw9seRFzDI1hw==
"@sentry/browser@^6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.3.6.tgz#bba07033efded6c844de88dcc47f99548a29afed"
integrity sha512-l4323jxuBOArki6Wf+EHes39IEyJ2Zj/CIUaTY7GWh7CntpfHQAfFmZWQw3Ozq+ka1u8lVp25RPhb4Wng3azNA==
dependencies:
"@sentry/core" "5.12.0"
"@sentry/types" "5.12.0"
"@sentry/utils" "5.12.0"
"@sentry/core" "6.3.6"
"@sentry/types" "6.3.6"
"@sentry/utils" "6.3.6"
tslib "^1.9.3"
"@sentry/cli@^1.51.0":
version "1.51.0"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.51.0.tgz#2ce7d03b7aebf3f9e9a7186bac1a777fc3dc0e82"
integrity sha512-QXdW3smFW9Wjd5gYHuA9u9tCra87VqpeFljRKdD7D2CwnYCnFDeluVk3l9O8Me6IoVBuL9Uwxx8q1vPtob4n3Q==
"@sentry/cli@^1.64.2":
version "1.64.2"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.64.2.tgz#38a68d31692675061fdedd27d4006a249bc915a9"
integrity sha512-hBF8cswn878W7YqAgeg/vcQ51BgtpeiklGV6Q5MEXqGXtbjaKTBYBcKTkhzpkkcUtTu5bFkVcdGzVtpl4oBe+g==
dependencies:
fs-copy-file-sync "^1.1.1"
https-proxy-agent "^4.0.0"
mkdirp "^0.5.1"
node-fetch "^2.1.2"
progress "2.0.0"
proxy-from-env "^1.0.0"
https-proxy-agent "^5.0.0"
mkdirp "^0.5.5"
node-fetch "^2.6.0"
npmlog "^4.1.2"
progress "^2.0.3"
proxy-from-env "^1.1.0"
"@sentry/core@5.12.0":
version "5.12.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.12.0.tgz#d6380c4ef7beee5f418ac1d0e5be86a2de2af449"
integrity sha512-wY4rsoX71QsGpcs9tF+OxKgDPKzIFMRvFiSRcJoPMfhFsTilQ/CBMn/c3bDtWQd9Bnr/ReQIL6NbnIjUsPHA4Q==
"@sentry/core@6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.3.6.tgz#e2ec6ae7e456e61f28000bab2d8ce85f58c59c66"
integrity sha512-w6BRizAqh7BaiM9oeKzO6aACXwRijUPacYaVLX/OfhqCSueF9uDxpMRT7+4D/eCeDVqgJYhBJ4Vsu2NSstkk4A==
dependencies:
"@sentry/hub" "5.12.0"
"@sentry/minimal" "5.12.0"
"@sentry/types" "5.12.0"
"@sentry/utils" "5.12.0"
"@sentry/hub" "6.3.6"
"@sentry/minimal" "6.3.6"
"@sentry/types" "6.3.6"
"@sentry/utils" "6.3.6"
tslib "^1.9.3"
"@sentry/hub@5.12.0":
version "5.12.0"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.12.0.tgz#5e8c8f249f5bdbeb8cc4ec02c2ccc53a67f2cc02"
integrity sha512-3k7yE8BEVJsKx8mR4LcI4IN0O8pngmq44OcJ/fRUUBAPqsT38jsJdP2CaWhdlM1jiNUzUDB1ktBv6/lY+VgcoQ==
"@sentry/hub@6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.3.6.tgz#e7bc6960e30d8731e23c6e77f31af0bfb1d5af3c"
integrity sha512-foBZ3ilMnm9Gf9OolrAxYHK8jrA6IF72faDdJ3Al+1H27qcpnBaMdrdEp2/jzwu/dgmwuLmbBaMjEPXaGH/0JQ==
dependencies:
"@sentry/types" "5.12.0"
"@sentry/utils" "5.12.0"
"@sentry/types" "6.3.6"
"@sentry/utils" "6.3.6"
tslib "^1.9.3"
"@sentry/minimal@5.12.0":
version "5.12.0"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.12.0.tgz#2611e2aa520c1edb7999e6de51bd65ec66341757"
integrity sha512-fk73meyz4k4jCg9yzbma+WkggsfEIQWI2e2TWfYsRGcrV3RnlSrXyM4D91/A8Bjx10SNezHPUFHjasjlHXOkyA==
"@sentry/minimal@6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.3.6.tgz#aebcebd2ee9007b0ec505b9fcefd10f10fc5d43d"
integrity sha512-uM2/dH0a6zfvI5f+vg+/mST+uTBdN6Jgpm585ipH84ckCYQwIIDRg6daqsen4S1sy/xgg1P1YyC3zdEC4G6b1Q==
dependencies:
"@sentry/hub" "5.12.0"
"@sentry/types" "5.12.0"
"@sentry/hub" "6.3.6"
"@sentry/types" "6.3.6"
tslib "^1.9.3"
"@sentry/types@5.12.0":
version "5.12.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.12.0.tgz#5367e53c74261beea01502e3f7b6f3d822682a31"
integrity sha512-aZbBouBLrKB8wXlztriIagZNmsB+wegk1Jkl6eprqRW/w24Sl/47tiwH8c5S4jYTxdAiJk+SAR10AAuYmIN3zg==
"@sentry/types@6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.3.6.tgz#aa3687051af1dc04ebc4eaf7f9562872da67aa5c"
integrity sha512-93cFJdJkWyCfyZeWFARSU11qnoHVOS/R2h5WIsEf+jbQmkqG2C+TXVz/19s6nHVsfDrwpvYpwALPv4/nrxfU7g==
"@sentry/utils@5.12.0":
version "5.12.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.12.0.tgz#62967f934a3ee6d21472eac0219084e37225933e"
integrity sha512-fYUadGLbfTCbs4OG5hKCOtv2jrNE4/8LHNABy9DwNJ/t5DVtGqWAZBnxsC+FG6a3nVqCpxjFI9AHlYsJ2wsf7Q==
"@sentry/utils@6.3.6":
version "6.3.6"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.3.6.tgz#6f619a525f2a94fa6b160500f63f4bd5bd171055"
integrity sha512-HnYlDBf8Dq8MEv7AulH7B6R1D/2LAooVclGdjg48tSrr9g+31kmtj+SAj2WWVHP9+bp29BWaC7i5nkfKrOibWw==
dependencies:
"@sentry/types" "5.12.0"
"@sentry/types" "6.3.6"
tslib "^1.9.3"
"@sheerun/mutationobserver-shim@^0.3.2":
@ -1759,6 +1759,11 @@
resolved "https://registry.yarnpkg.com/@types/js-base64/-/js-base64-2.3.1.tgz#c39f14f129408a3d96a1105a650d8b2b6eeb4168"
integrity sha512-4RKbhIDGC87s4EBy2Cp2/5S2O6kmCRcZnD5KRCq1q9z2GhBte1+BdsfVKCpG8yKpDGNyEE2G6IqFIh6W2YwWPA==
"@types/js-cookie@2.2.6":
version "2.2.6"
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f"
integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==
"@types/minimatch@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@ -2044,6 +2049,11 @@
"@webassemblyjs/wast-parser" "1.8.5"
"@xtuc/long" "4.2.2"
"@xobotyi/scrollbar-width@1.9.5":
version "1.9.5"
resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -2209,6 +2219,13 @@ agent-base@5:
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
ajv-errors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
@ -4898,6 +4915,11 @@ fast-deep-equal@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-json-patch@^2.0.6:
version "2.2.1"
resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-2.2.1.tgz#18150d36c9ab65c7209e7d4eb113f4f8eaabe6d9"
@ -4925,6 +4947,11 @@ fast-safe-stringify@^2.0.7:
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
fast-shallow-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b"
integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==
fastest-stable-stringify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028"
@ -5222,11 +5249,6 @@ fs-constants@^1.0.0:
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
fs-copy-file-sync@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz#11bf32c096c10d126e5f6b36d06eece776062918"
integrity sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ==
fs-exists-sync@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add"
@ -5776,6 +5798,14 @@ https-proxy-agent@^4.0.0:
agent-base "5"
debug "4"
https-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
dependencies:
agent-base "6"
debug "4"
human-signals@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
@ -6758,6 +6788,11 @@ js-base64@^2.5.1:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
js-cookie@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
js-levenshtein@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
@ -7500,7 +7535,7 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1:
dependencies:
minimist "0.0.8"
mkdirp@^0.5.3:
mkdirp@^0.5.3, mkdirp@^0.5.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@ -7658,12 +7693,7 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
node-fetch@^2.1.2:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@^2.6.1:
node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
@ -7829,7 +7859,7 @@ npm-run-path@^4.0.0:
dependencies:
path-key "^3.0.0"
npmlog@^4.0.2:
npmlog@^4.0.2, npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@ -8492,17 +8522,12 @@ process@^0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
progress@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=
progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
progress@^2.0.0, progress@^2.0.1:
progress@^2.0.0, progress@^2.0.1, progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
@ -8542,6 +8567,11 @@ proxy-from-env@^1.0.0:
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
@ -8715,19 +8745,14 @@ react-clientside-effect@^1.2.2:
dependencies:
"@babel/runtime" "^7.0.0"
react-dom@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==
react-dom@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.1"
react-fast-compare@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
scheduler "^0.20.2"
react-focus-lock@^2.1.0:
version "2.2.1"
@ -8783,20 +8808,30 @@ react-style-singleton@^2.0.0:
invariant "^2.2.4"
tslib "^1.0.0"
react-use@^13.8.0:
version "13.8.0"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.8.0.tgz#5e8badaaa5614a1925fd28ad22d01cc1c52e0ff1"
integrity sha512-J4ZWIqC1h2eSS6ObmHJUWDQeJSKZvHdBEINCu51plz7RS+ijrt6JEHfDWbNXx+aAoR5AmbRK+mzYwd3jiu3xpA==
react-universal-interface@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==
react-use@^15.3.0:
version "15.3.8"
resolved "https://registry.yarnpkg.com/react-use/-/react-use-15.3.8.tgz#ca839ac7fb3d696e5ccbeabbc8dadc2698969d30"
integrity sha512-GeGcrmGuUvZrY5wER3Lnph9DSYhZt5nEjped4eKDq8BRGr2CnLf9bDQWG9RFc7oCPphnscUUdOovzq0E5F2c6Q==
dependencies:
"@types/js-cookie" "2.2.6"
"@xobotyi/scrollbar-width" "1.9.5"
copy-to-clipboard "^3.2.0"
fast-deep-equal "^3.1.3"
fast-shallow-equal "^1.0.0"
js-cookie "^2.2.1"
nano-css "^5.2.1"
react-fast-compare "^2.0.4"
react-universal-interface "^0.6.2"
resize-observer-polyfill "^1.5.1"
screenfull "^5.0.0"
set-harmonic-interval "^1.0.1"
throttle-debounce "^2.1.0"
ts-easing "^0.2.0"
tslib "^1.10.0"
tslib "^2.0.0"
react-window@^1.8.5:
version "1.8.5"
@ -8807,9 +8842,9 @@ react-window@^1.8.5:
memoize-one ">=3.1.1 <6"
react@^16.10.2, react@^17, react@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -9354,10 +9389,10 @@ sax@>=0.6.0, sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
scheduler@^0.20.1:
version "0.20.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c"
integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -10471,11 +10506,16 @@ tslib@^1.0.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3:
tslib@^1.9.0, tslib@^1.9.3:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
tslib@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@ -10540,10 +10580,10 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5"
integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==
typescript@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
uglify-js@^3.6.0:
version "3.6.0"