Merge branch 'development' into releases/3.3.10

This commit is contained in:
tidy-dev 2024-03-07 07:24:20 -05:00 committed by GitHub
commit 28d95056d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
129 changed files with 6866 additions and 2411 deletions

View file

@ -216,6 +216,9 @@ overrides:
- files: 'script/**/*'
rules:
'@typescript-eslint/no-non-null-assertion': off
- files: 'app/src/ui/octicons/octicons.generated.ts'
rules:
'@typescript-eslint/naming-convention': off
parserOptions:
sourceType: module

View file

@ -37,7 +37,7 @@ jobs:
private_key: ${{ secrets.DESKTOP_RELEASES_APP_PRIVATE_KEY }}
- name: Create Release Pull Request
uses: peter-evans/create-pull-request@v6.0.0
uses: peter-evans/create-pull-request@v6.0.1
if: |
startsWith(github.ref, 'refs/heads/releases/') && !contains(github.ref, 'test')
with:

View file

@ -3,7 +3,7 @@
"productName": "GitHub Desktop",
"bundleID": "com.github.GitHubClient",
"companyName": "GitHub, Inc.",
"version": "3.3.10",
"version": "3.3.10-beta1",
"main": "./main.js",
"repository": {
"type": "git",
@ -54,7 +54,7 @@
"react-dom": "^16.8.4",
"react-transition-group": "^4.4.1",
"react-virtualized": "^9.20.0",
"registry-js": "^1.15.0",
"registry-js": "^1.16.0",
"source-map-support": "^0.4.15",
"strip-ansi": "^4.0.0",
"textarea-caret": "^3.0.2",

View file

@ -4,7 +4,7 @@ import { TitleBar } from '../ui/window/title-bar'
import { encodePathAsUrl } from '../lib/path'
import { WindowState } from '../lib/window-state'
import { Octicon } from '../ui/octicons'
import * as OcticonSymbol from '../ui/octicons/octicons.generated'
import * as octicons from '../ui/octicons/octicons.generated'
import { Button } from '../ui/lib/button'
import { LinkButton } from '../ui/lib/link-button'
import { getVersion } from '../ui/lib/app-proxy'
@ -141,7 +141,7 @@ export class CrashApp extends React.Component<ICrashAppProps, ICrashAppState> {
return (
<header>
<Octicon symbol={OcticonSymbol.stop} className="error-icon" />
<Octicon symbol={octicons.stop} className="error-icon" />
<h1>{message}</h1>
</header>
)

View file

@ -340,6 +340,8 @@ export interface IAppState {
* rulesets to check their bypass status.
*/
readonly cachedRepoRulesets: ReadonlyMap<number, IAPIRepoRuleset>
readonly underlineLinks: boolean
}
export enum FoldoutType {

View file

@ -105,3 +105,6 @@ export const enableCommitDetailsHeaderExpansion = () => true
export const enableDiffCheckMarksAndLinkUnderlines = enableBetaFeatures
export const enableDiffCheckMarks = enableDiffCheckMarksAndLinkUnderlines
export const enableGroupDiffCheckmarks = enableDiffCheckMarksAndLinkUnderlines
export const enableLinkUnderlines = enableDiffCheckMarksAndLinkUnderlines

View file

@ -81,6 +81,8 @@ export type RequestChannels = {
'notification-event': NotificationCallback<DesktopAliveEvent>
'set-window-zoom-factor': (zoomFactor: number) => void
'show-installing-update': () => void
'install-windows-cli': () => void
'uninstall-windows-cli': () => void
}
/**

View file

@ -1,11 +1,11 @@
import { spawn as spawnInternal } from 'child_process'
import * as Path from 'path'
import {
HKEY,
RegistryValueType,
RegistryValue,
RegistryStringEntry,
enumerateValues,
setValue,
} from 'registry-js'
function isStringRegistryValue(rv: RegistryValue): rv is RegistryStringEntry {
@ -15,31 +15,49 @@ function isStringRegistryValue(rv: RegistryValue): rv is RegistryStringEntry {
)
}
/** Get the path segments in the user's `Path`. */
export function getPathSegments(): ReadonlyArray<string> {
export function getPathRegistryValue(): RegistryStringEntry | null {
for (const value of enumerateValues(HKEY.HKEY_CURRENT_USER, 'Environment')) {
if (value.name === 'Path' && isStringRegistryValue(value)) {
return value.data.split(';').filter(x => x.length > 0)
return value
}
}
throw new Error('Could not find PATH environment variable')
return null
}
/** Get the path segments in the user's `Path`. */
export function getPathSegments(): ReadonlyArray<string> {
const value = getPathRegistryValue()
if (value === null) {
throw new Error('Could not find PATH environment variable')
}
return value.data.split(';').filter(x => x.length > 0)
}
/** Set the user's `Path`. */
export async function setPathSegments(
paths: ReadonlyArray<string>
): Promise<void> {
let setxPath: string
const systemRoot = process.env['SystemRoot']
if (systemRoot) {
const system32Path = Path.join(systemRoot, 'System32')
setxPath = Path.join(system32Path, 'setx.exe')
} else {
setxPath = 'setx.exe'
const value = getPathRegistryValue()
if (value === null) {
throw new Error('Could not find PATH environment variable')
}
await spawn(setxPath, ['Path', paths.join(';')])
try {
setValue(
HKEY.HKEY_CURRENT_USER,
'Environment',
'Path',
value.type,
paths.join(';')
)
} catch (e) {
log.error('Failed setting PATH environment variable', e)
throw new Error('Could not set the PATH environment variable')
}
}
/** Spawn a command with arguments and capture its output. */

View file

@ -238,7 +238,11 @@ import {
} from './updates/changes-state'
import { ManualConflictResolution } from '../../models/manual-conflict-resolution'
import { BranchPruner } from './helpers/branch-pruner'
import { enableMoveStash } from '../feature-flag'
import {
enableDiffCheckMarks,
enableLinkUnderlines,
enableMoveStash,
} from '../feature-flag'
import { Banner, BannerType } from '../../models/banner'
import { ComputedAction } from '../../models/computed-action'
import {
@ -407,6 +411,9 @@ const lastThankYouKey = 'version-and-users-of-last-thank-you'
const pullRequestSuggestedNextActionKey =
'pull-request-suggested-next-action-key'
const underlineLinksKey = 'underline-links'
const underlineLinksDefault = true
const showDiffCheckMarksDefault = true
const showDiffCheckMarksKey = 'diff-check-marks-visible'
@ -544,6 +551,8 @@ export class AppStore extends TypedBaseStore<IAppState> {
private cachedRepoRulesets = new Map<number, IAPIRepoRuleset>()
private underlineLinks: boolean = underlineLinksDefault
public constructor(
private readonly gitHubUserStore: GitHubUserStore,
private readonly cloningRepositoriesStore: CloningRepositoriesStore,
@ -1023,6 +1032,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
pullRequestSuggestedNextAction: this.pullRequestSuggestedNextAction,
resizablePaneActive: this.resizablePaneActive,
cachedRepoRulesets: this.cachedRepoRulesets,
underlineLinks: this.underlineLinks,
showDiffCheckMarks: this.showDiffCheckMarks,
}
}
@ -2212,10 +2222,14 @@ export class AppStore extends TypedBaseStore<IAppState> {
PullRequestSuggestedNextAction
) ?? defaultPullRequestSuggestedNextAction
this.showDiffCheckMarks = getBoolean(
showDiffCheckMarksKey,
showDiffCheckMarksDefault
)
// Always false if the feature flag is disabled.
this.underlineLinks = enableLinkUnderlines()
? getBoolean(underlineLinksKey, underlineLinksDefault)
: false
this.showDiffCheckMarks = enableDiffCheckMarks()
? getBoolean(showDiffCheckMarksKey, showDiffCheckMarksDefault)
: false
this.emitUpdateNow()
@ -7929,6 +7943,22 @@ export class AppStore extends TypedBaseStore<IAppState> {
this.emitUpdate()
}
}
public _updateUnderlineLinks(underlineLinks: boolean) {
if (underlineLinks !== this.underlineLinks) {
this.underlineLinks = underlineLinks
setBoolean(underlineLinksKey, underlineLinks)
this.emitUpdate()
}
}
public _updateShowDiffCheckMarks(showDiffCheckMarks: boolean) {
if (showDiffCheckMarks !== this.showDiffCheckMarks) {
this.showDiffCheckMarks = showDiffCheckMarks
setBoolean(showDiffCheckMarksKey, showDiffCheckMarks)
this.emitUpdate()
}
}
}
/**

View file

@ -16,7 +16,11 @@ import { AppWindow } from './app-window'
import { buildDefaultMenu, getAllMenuItems } from './menu'
import { shellNeedsPatching, updateEnvironmentForProcess } from '../lib/shell'
import { parseAppURL } from '../lib/parse-app-url'
import { handleSquirrelEvent } from './squirrel-updater'
import {
handleSquirrelEvent,
installWindowsCLI,
uninstallWindowsCLI,
} from './squirrel-updater'
import { fatalError } from '../lib/fatal-error'
import { log as writeLog } from './log'
@ -522,6 +526,11 @@ app.on('ready', () => {
mainWindow?.setWindowZoomFactor(zoomFactor)
)
if (__WIN32__) {
ipcMain.on('install-windows-cli', installWindowsCLI)
ipcMain.on('uninstall-windows-cli', uninstallWindowsCLI)
}
/**
* An event sent by the renderer asking for a copy of the current
* application menu.

View file

@ -78,7 +78,7 @@ export function buildDefaultMenu({
{
label: 'Install Command Line Tool…',
id: 'install-cli',
click: emit('install-cli'),
click: emit('install-darwin-cli'),
},
separator,
{
@ -574,26 +574,9 @@ export function buildDefaultMenu({
label: 'Show App Error',
click: emit('show-app-error'),
},
],
},
{
label: 'Show banner',
submenu: [
{
label: 'Reorder Successful',
click: emit('show-test-reorder-banner'),
},
{
label: 'Reorder Undone',
click: emit('show-test-undone-banner'),
},
{
label: 'Cherry Pick Conflicts',
click: emit('show-test-cherry-pick-conflicts-banner'),
},
{
label: 'Merge Successful',
click: emit('show-test-merge-successful-banner'),
label: 'Octicons',
click: emit('show-icon-test-dialog'),
},
],
},
@ -605,6 +588,22 @@ export function buildDefaultMenu({
}
if (__RELEASE_CHANNEL__ === 'development' || __RELEASE_CHANNEL__ === 'test') {
if (__WIN32__) {
helpItems.push(separator, {
label: 'Command Line Tool',
submenu: [
{
label: 'Install',
click: emit('install-windows-cli'),
},
{
label: 'Uninstall',
click: emit('uninstall-windows-cli'),
},
],
})
}
helpItems.push(
{
label: 'Show notification',
@ -629,6 +628,22 @@ export function buildDefaultMenu({
label: 'Thank you',
click: emit('show-thank-you-banner'),
},
{
label: 'Reorder Successful',
click: emit('show-test-reorder-banner'),
},
{
label: 'Reorder Undone',
click: emit('show-test-undone-banner'),
},
{
label: 'Cherry Pick Conflicts',
click: emit('show-test-cherry-pick-conflicts-banner'),
},
{
label: 'Merge Successful',
click: emit('show-test-merge-successful-banner'),
},
],
}
)

View file

@ -32,7 +32,9 @@ export type MenuEvent =
| 'go-to-commit-message'
| 'boomtown'
| 'open-pull-request'
| 'install-cli'
| 'install-darwin-cli'
| 'install-windows-cli'
| 'uninstall-windows-cli'
| 'open-external-editor'
| 'select-all'
| 'show-release-notes-popup'
@ -55,3 +57,4 @@ export type MenuEvent =
| 'show-test-undone-banner'
| 'show-test-cherry-pick-conflicts-banner'
| 'show-test-merge-successful-banner'
| 'show-icon-test-dialog'

View file

@ -38,15 +38,15 @@ export function handleSquirrelEvent(eventName: string): Promise<void> | null {
async function handleInstalled(): Promise<void> {
await createShortcut(['StartMenu', 'Desktop'])
await installCLI()
await installWindowsCLI()
}
async function handleUpdated(): Promise<void> {
await updateShortcut()
await installCLI()
await installWindowsCLI()
}
async function installCLI(): Promise<void> {
export async function installWindowsCLI(): Promise<void> {
const binPath = getBinPath()
await mkdir(binPath, { recursive: true })
await writeBatchScriptCLITrampoline(binPath)
@ -61,6 +61,17 @@ async function installCLI(): Promise<void> {
}
}
export async function uninstallWindowsCLI() {
try {
const paths = getPathSegments()
const binPath = getBinPath()
const pathsWithoutBinPath = paths.filter(p => p !== binPath)
return setPathSegments(pathsWithoutBinPath)
} catch (e) {
log.error('Failed removing bin path from PATH environment variable', e)
}
}
/**
* Get the path for the `bin` directory which exists in our `AppData` but
* outside path which includes the installed app version.
@ -135,15 +146,7 @@ function createShortcut(locations: ShortcutLocations): Promise<void> {
async function handleUninstall(): Promise<void> {
await removeShortcut()
try {
const paths = getPathSegments()
const binPath = getBinPath()
const pathsWithoutBinPath = paths.filter(p => p !== binPath)
return setPathSegments(pathsWithoutBinPath)
} catch (e) {
log.error('Failed removing bin path from PATH environment variable', e)
}
return uninstallWindowsCLI()
}
function removeShortcut(): Promise<void> {

View file

@ -135,6 +135,54 @@ export class DiffSelection {
}
}
/**
* Returns a value indicating whether the range is all selected, partially
* selected, or not selected.
*
* @param from The line index (inclusive) from where to checking the range.
*
* @param length The number of lines to check from the start point of
* 'from', Assumes positive number, returns None if length is <= 0.
*/
public isRangeSelected(from: number, length: number): DiffSelectionType {
if (length <= 0) {
// This shouldn't happen? But if it does we'll log it and return None.
return DiffSelectionType.None
}
const computedSelectionType = this.getSelectionType()
if (computedSelectionType !== DiffSelectionType.Partial) {
// Nothing for us to do here. If all lines are selected or none, then any
// range of lines will be the same.
return computedSelectionType
}
if (length === 1) {
return this.isSelected(from)
? DiffSelectionType.All
: DiffSelectionType.None
}
const to = from + length
let foundSelected = false
let foundDeselected = false
for (let i = from; i < to; i++) {
if (this.isSelected(i)) {
foundSelected = true
}
if (!this.isSelected(i)) {
foundDeselected = true
}
if (foundSelected && foundDeselected) {
return DiffSelectionType.Partial
}
}
return foundSelected ? DiffSelectionType.All : DiffSelectionType.None
}
/**
* Returns a value indicating wether the given line number is selectable.
* A line not being selectable usually means it's a hunk header or a context

View file

@ -95,6 +95,7 @@ export enum PopupType {
PullRequestComment = 'PullRequestComment',
UnknownAuthors = 'UnknownAuthors',
ConfirmRepoRulesBypass = 'ConfirmRepoRulesBypass',
TestIcons = 'TestIcons',
}
interface IBasePopup {
@ -420,5 +421,8 @@ export type PopupDetail =
branch: string
onConfirm: () => void
}
| {
type: PopupType.TestIcons
}
export type Popup = IBasePopup & PopupDetail

View file

@ -6,4 +6,5 @@ export enum PreferencesTab {
Notifications,
Prompts,
Advanced,
Accessibility,
}

View file

@ -24,7 +24,7 @@ import { writeGitAttributes } from './git-attributes'
import { getDefaultDir, setDefaultDir } from '../lib/default-dir'
import { Dialog, DialogContent, DialogFooter, DialogError } from '../dialog'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { LinkButton } from '../lib/link-button'
import { PopupType } from '../../models/popup'
import { Ref } from '../lib/ref'
@ -443,7 +443,7 @@ export class CreateRepository extends React.Component<
return (
<Row className="warning-helper-text">
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
Will be created as {sanitizedName}
</Row>
)

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import classNames from 'classnames'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { MenuItem } from '../../models/app-menu'
import { AccessText } from '../lib/access-text'
import { getPlatformSpecificNameOrSymbolForModifier } from '../../lib/menu-item'
@ -95,9 +95,9 @@ export class MenuListItem extends React.Component<IMenuListItemProps, {}> {
private getIcon(item: MenuItem): JSX.Element | null {
if (item.type === 'checkbox' && item.checked) {
return <Octicon className="icon" symbol={OcticonSymbol.check} />
return <Octicon className="icon" symbol={octicons.check} />
} else if (item.type === 'radio' && item.checked) {
return <Octicon className="icon" symbol={OcticonSymbol.dotFill} />
return <Octicon className="icon" symbol={octicons.dotFill} />
}
return null
@ -153,10 +153,7 @@ export class MenuListItem extends React.Component<IMenuListItemProps, {}> {
const arrow =
item.type === 'submenuItem' && this.props.renderSubMenuArrow !== false ? (
<Octicon
className="submenu-arrow"
symbol={OcticonSymbol.triangleRight}
/>
<Octicon className="submenu-arrow" symbol={octicons.triangleRight} />
) : null
const accelerator =

View file

@ -51,13 +51,15 @@ import {
BranchDropdown,
RevertProgress,
} from './toolbar'
import { iconForRepository, OcticonSymbolType } from './octicons'
import * as OcticonSymbol from './octicons/octicons.generated'
import { iconForRepository, OcticonSymbol } from './octicons'
import * as octicons from './octicons/octicons.generated'
import {
showCertificateTrustDialog,
sendReady,
isInApplicationFolder,
selectAllWindowContents,
installWindowsCLI,
uninstallWindowsCLI,
} from './main-process-proxy'
import { DiscardChanges } from './discard-changes'
import { Welcome } from './welcome'
@ -172,6 +174,7 @@ import { UnsupportedOSBannerDismissedAtKey } from './banners/windows-version-no-
import { offsetFromNow } from '../lib/offset-from'
import { getNumber } from '../lib/local-storage'
import { RepoRulesBypassConfirmation } from './repository-rules/repo-rules-bypass-confirmation'
import { IconPreviewDialog } from './octicons/icon-preview-dialog'
const MinuteInMilliseconds = 1000 * 60
const HourInMilliseconds = MinuteInMilliseconds * 60
@ -451,8 +454,12 @@ export class App extends React.Component<IAppProps, IAppState> {
return this.openPullRequest()
case 'preview-pull-request':
return this.startPullRequest()
case 'install-cli':
return this.props.dispatcher.installCLI()
case 'install-darwin-cli':
return this.props.dispatcher.installDarwinCLI()
case 'install-windows-cli':
return installWindowsCLI()
case 'uninstall-windows-cli':
return uninstallWindowsCLI()
case 'open-external-editor':
return this.openCurrentRepositoryInExternalEditor()
case 'select-all':
@ -495,6 +502,8 @@ export class App extends React.Component<IAppProps, IAppState> {
return this.showFakeCherryPickConflictBanner()
case 'show-test-merge-successful-banner':
return this.showFakeMergeSuccessfulBanner()
case 'show-icon-test-dialog':
return this.showIconTestDialog()
default:
return assertNever(name, `Unknown menu event name: ${name}`)
}
@ -612,6 +621,14 @@ export class App extends React.Component<IAppProps, IAppState> {
}
}
private async showIconTestDialog() {
if (__DEV__) {
this.props.dispatcher.showPopup({
type: PopupType.TestIcons,
})
}
}
private testShowNotification() {
if (
__RELEASE_CHANNEL__ !== 'development' &&
@ -1660,6 +1677,8 @@ export class App extends React.Component<IAppProps, IAppState> {
selectedTheme={this.state.selectedTheme}
repositoryIndicatorsEnabled={this.state.repositoryIndicatorsEnabled}
onOpenFileInExternalEditor={this.openFileInExternalEditor}
underlineLinks={this.state.underlineLinks}
showDiffCheckMarks={this.state.showDiffCheckMarks}
/>
)
case PopupType.RepositorySettings: {
@ -1917,6 +1936,7 @@ export class App extends React.Component<IAppProps, IAppState> {
emoji={this.state.emoji}
newReleases={popup.newReleases}
onDismissed={onPopupDismissedFn}
underlineLinks={this.state.underlineLinks}
/>
)
case PopupType.DeletePullRequest:
@ -2405,6 +2425,7 @@ export class App extends React.Component<IAppProps, IAppState> {
emoji={this.state.emoji}
onSubmit={onPopupDismissedFn}
onDismissed={onPopupDismissedFn}
underlineLinks={this.state.underlineLinks}
accounts={this.state.accounts}
/>
)
@ -2532,6 +2553,7 @@ export class App extends React.Component<IAppProps, IAppState> {
emoji={this.state.emoji}
onSubmit={onPopupDismissedFn}
onDismissed={onPopupDismissedFn}
underlineLinks={this.state.underlineLinks}
accounts={this.state.accounts}
/>
)
@ -2557,6 +2579,14 @@ export class App extends React.Component<IAppProps, IAppState> {
/>
)
}
case PopupType.TestIcons: {
return (
<IconPreviewDialog
key="octicons-preview"
onDismissed={onPopupDismissedFn}
/>
)
}
default:
return assertNever(popup, `Unknown popup type: ${popup}`)
}
@ -2882,17 +2912,17 @@ export class App extends React.Component<IAppProps, IAppState> {
const repository = selection ? selection.repository : null
let icon: OcticonSymbolType
let icon: OcticonSymbol
let title: string
if (repository) {
const alias = repository instanceof Repository ? repository.alias : null
icon = iconForRepository(repository)
title = alias ?? repository.name
} else if (this.state.repositories.length > 0) {
icon = OcticonSymbol.repo
icon = octicons.repo
title = __DARWIN__ ? 'Select a Repository' : 'Select a repository'
} else {
icon = OcticonSymbol.repo
icon = octicons.repo
title = __DARWIN__ ? 'No Repositories' : 'No repositories'
}
@ -3156,6 +3186,7 @@ export class App extends React.Component<IAppProps, IAppState> {
showCIStatusPopover={this.state.showCIStatusPopover}
emoji={this.state.emoji}
enableFocusTrap={enableFocusTrap}
underlineLinks={this.state.underlineLinks}
/>
)
}
@ -3350,7 +3381,12 @@ export class App extends React.Component<IAppProps, IAppState> {
return null
}
const className = this.state.appIsFocused ? 'focused' : 'blurred'
const className = classNames(
this.state.appIsFocused ? 'focused' : 'blurred',
{
'underline-links': this.state.underlineLinks,
}
)
const currentTheme = this.state.showWelcomeFlow
? ApplicationTheme.Light

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
interface IBannerProps {
readonly id?: string
@ -37,7 +37,7 @@ export class Banner extends React.Component<IBannerProps, {}> {
return (
<div className="close">
<button onClick={onDismissed} aria-label="Dismiss this message">
<Octicon symbol={OcticonSymbol.x} />
<Octicon symbol={octicons.x} />
</button>
</div>
)

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
export function BranchAlreadyUpToDate({
@ -29,7 +29,7 @@ export function BranchAlreadyUpToDate({
return (
<Banner id="successful-merge" timeout={5000} onDismissed={onDismissed}>
<div className="green-circle">
<Octicon className="check-icon" symbol={OcticonSymbol.check} />
<Octicon className="check-icon" symbol={octicons.check} />
</div>
<div className="banner-message">{message}</div>
</Banner>

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
import { LinkButton } from '../lib/link-button'
@ -35,7 +35,7 @@ export class CherryPickConflictsBanner extends React.Component<
dismissable={false}
onDismissed={this.onDismissed}
>
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
<Octicon className="alert-icon" symbol={octicons.alert} />
<div className="banner-message">
<span>
Resolve conflicts to continue cherry-picking onto{' '}

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
import { LinkButton } from '../lib/link-button'
@ -41,7 +41,7 @@ export class ConflictsFoundBanner extends React.Component<
dismissable={false}
onDismissed={this.onDismissed}
>
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
<Octicon className="alert-icon" symbol={octicons.alert} />
<div className="banner-message">
<span>
Resolve conflicts to continue {this.props.operationDescription}.

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
import { Dispatcher } from '../dispatcher'
import { Popup } from '../../models/popup'
@ -31,7 +31,7 @@ export class MergeConflictsBanner extends React.Component<
dismissable={false}
onDismissed={this.props.onDismissed}
>
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
<Octicon className="alert-icon" symbol={octicons.alert} />
<div className="banner-message">
<span>
Resolve conflicts and commit to merge into{' '}

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
import { Dispatcher } from '../dispatcher'
import { LinkButton } from '../lib/link-button'
@ -38,7 +38,7 @@ export class RebaseConflictsBanner extends React.Component<
dismissable={false}
onDismissed={this.onDismissed}
>
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
<Octicon className="alert-icon" symbol={octicons.alert} />
<div className="banner-message">
<span>
Resolve conflicts to continue rebasing{' '}

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import { LinkButton } from '../lib/link-button'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
interface ISuccessBannerProps {
@ -36,7 +36,7 @@ export class SuccessBanner extends React.Component<ISuccessBannerProps, {}> {
onDismissed={this.props.onDismissed}
>
<div className="green-circle">
<Octicon className="check-icon" symbol={OcticonSymbol.check} />
<Octicon className="check-icon" symbol={octicons.checkCircleFill} />
</div>
<div className="banner-message">
<span className="success-contents">{this.props.children}</span>

View file

@ -7,7 +7,7 @@ import {
updateStore,
} from '../lib/update-store'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { PopupType } from '../../models/popup'
import { shell } from '../../lib/app-shell'
@ -39,7 +39,7 @@ export class UpdateAvailable extends React.Component<
{!this.props.isUpdateShowcaseVisible && (
<Octicon
className="download-icon"
symbol={OcticonSymbol.desktopDownload}
symbol={octicons.desktopDownload}
/>
)}

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Banner } from './banner'
import { LinkButton } from '../lib/link-button'
import { setNumber } from '../../lib/local-storage'
@ -23,7 +23,7 @@ export class WindowsVersionNoLongerSupportedBanner extends React.Component<{
dismissable={true}
onDismissed={this.onDismissed}
>
<Octicon className="alert-icon" symbol={OcticonSymbol.alert} />
<Octicon className="alert-icon" symbol={octicons.alert} />
<div className="banner-message">
<span>
This operating system is no longer supported. Software updates have

View file

@ -3,7 +3,7 @@ import * as React from 'react'
import { IMatches } from '../../lib/fuzzy-find'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { HighlightText } from '../lib/highlight-text'
import { dragAndDropManager } from '../../lib/drag-and-drop-manager'
import { DragType, DropTargetType } from '../../models/drag-drop'
@ -92,7 +92,7 @@ export class BranchListItem extends React.Component<
public render() {
const { lastCommitDate, isCurrentBranch, name } = this.props
const icon = isCurrentBranch ? OcticonSymbol.check : OcticonSymbol.gitBranch
const icon = isCurrentBranch ? octicons.check : octicons.gitBranch
const className = classNames('branches-list-item', {
'drop-target': this.state.isDragInProgress,
})

View file

@ -17,7 +17,7 @@ import { TabBar } from '../tab-bar'
import { Row } from '../lib/row'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Button } from '../lib/button'
import { BranchList } from './branch-list'
@ -54,6 +54,8 @@ interface IBranchesContainerProps {
/** Map from the emoji shortcut (e.g., :+1:) to the image's local path. */
readonly emoji: Map<string, string>
readonly underlineLinks: boolean
}
interface IBranchesContainerState {
@ -138,6 +140,7 @@ export class BranchesContainer extends React.Component<
pullRequestItemTop={prListItemTop}
onMouseEnter={this.onMouseEnterPullRequestQuickView}
onMouseLeave={this.onMouseLeavePullRequestQuickView}
underlineLinks={this.props.underlineLinks}
/>
)
}
@ -169,7 +172,7 @@ export class BranchesContainer extends React.Component<
onClick={this.onMergeClick}
tooltip={`Choose a branch to merge into ${currentBranch.name}`}
>
<Octicon className="icon" symbol={OcticonSymbol.gitMerge} />
<Octicon className="icon" symbol={octicons.gitMerge} />
Choose a branch to merge into <strong>{currentBranch.name}</strong>
</Button>
</Row>
@ -294,7 +297,7 @@ export class BranchesContainer extends React.Component<
onMouseLeave={this.onMouseLeaveNewBranchDrop}
onMouseUp={this.onMouseUpNewBranchDrop}
>
<Octicon className="icon" symbol={OcticonSymbol.plus} />
<Octicon className="icon" symbol={octicons.plus} />
<div className="name">{label}</div>
</div>
)

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon, OcticonSymbolType } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { Octicon, OcticonSymbol } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
import classNames from 'classnames'
import { GitHubRepository } from '../../models/github-repository'
import { DisposableLike } from 'event-kit'
@ -123,28 +123,28 @@ export class CIStatus extends React.PureComponent<
export function getSymbolForCheck(
check: ICombinedRefCheck | IRefCheck | IAPIWorkflowJobStep
): OcticonSymbolType {
): OcticonSymbol {
switch (check.conclusion) {
case 'timed_out':
return OcticonSymbol.x
return octicons.x
case 'failure':
return OcticonSymbol.x
return octicons.x
case 'neutral':
return OcticonSymbol.squareFill
return octicons.squareFill
case 'success':
return OcticonSymbol.check
return octicons.check
case 'cancelled':
return OcticonSymbol.stop
return octicons.stop
case 'action_required':
return OcticonSymbol.alert
return octicons.alert
case 'skipped':
return OcticonSymbol.skip
return octicons.skip
case 'stale':
return OcticonSymbol.issueReopened
return octicons.issueReopened
}
// Pending
return OcticonSymbol.dotFill
return octicons.dotFill
}
export function getClassNameForCheck(
@ -170,12 +170,12 @@ export function getClassNameForCheck(
export function getSymbolForLogStep(
logStep: IAPIWorkflowJobStep
): OcticonSymbolType {
): OcticonSymbol {
switch (logStep.conclusion) {
case 'success':
return OcticonSymbol.checkCircleFill
return octicons.checkCircleFill
case 'failure':
return OcticonSymbol.xCircleFill
return octicons.xCircleFill
}
return getSymbolForCheck(logStep)

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import classNames from 'classnames'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { CIStatus } from './ci-status'
import { HighlightText } from '../lib/highlight-text'
import { IMatches } from '../../lib/fuzzy-find'
@ -139,8 +139,8 @@ export class PullRequestListItem extends React.Component<
className="icon"
symbol={
this.props.draft
? OcticonSymbol.gitPullRequestDraft
: OcticonSymbol.gitPullRequest
? octicons.gitPullRequestDraft
: octicons.gitPullRequest
}
/>
</div>

View file

@ -44,8 +44,8 @@ import {
Foldout,
} from '../../lib/app-state'
import { ContinueRebase } from './continue-rebase'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { Octicon, OcticonSymbolVariant } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
import { IStashEntry } from '../../models/stash-entry'
import classNames from 'classnames'
import { hasWritePermission } from '../../models/github-repository'
@ -59,19 +59,19 @@ import { RepoRulesInfo } from '../../models/repo-rules'
import { IAheadBehind } from '../../models/branch'
const RowHeight = 29
const StashIcon: OcticonSymbol.OcticonSymbolType = {
const StashIcon: OcticonSymbolVariant = {
w: 16,
h: 16,
d:
p: [
'M10.5 1.286h-9a.214.214 0 0 0-.214.214v9a.214.214 0 0 0 .214.214h9a.214.214 0 0 0 ' +
'.214-.214v-9a.214.214 0 0 0-.214-.214zM1.5 0h9A1.5 1.5 0 0 1 12 1.5v9a1.5 1.5 0 0 1-1.5 ' +
'1.5h-9A1.5 1.5 0 0 1 0 10.5v-9A1.5 1.5 0 0 1 1.5 0zm5.712 7.212a1.714 1.714 0 1 ' +
'1-2.424-2.424 1.714 1.714 0 0 1 2.424 2.424zM2.015 12.71c.102.729.728 1.29 1.485 ' +
'1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H2.015zm2 2c.102.729.728 ' +
'1.29 1.485 1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H4.015z',
fr: 'evenodd',
'.214-.214v-9a.214.214 0 0 0-.214-.214zM1.5 0h9A1.5 1.5 0 0 1 12 1.5v9a1.5 1.5 0 0 1-1.5 ' +
'1.5h-9A1.5 1.5 0 0 1 0 10.5v-9A1.5 1.5 0 0 1 1.5 0zm5.712 7.212a1.714 1.714 0 1 ' +
'1-2.424-2.424 1.714 1.714 0 0 1 2.424 2.424zM2.015 12.71c.102.729.728 1.29 1.485 ' +
'1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H2.015zm2 2c.102.729.728 ' +
'1.29 1.485 1.29h9a1.5 1.5 0 0 0 1.5-1.5v-9a1.5 1.5 0 0 0-1.29-1.485v1.442a.216.216 0 0 1 ' +
'.004.043v9a.214.214 0 0 1-.214.214h-9a.216.216 0 0 1-.043-.004H4.015z',
],
}
const GitIgnoreFileName = '.gitignore'
@ -937,7 +937,7 @@ export class ChangesList extends React.Component<
>
<Octicon className="stack-icon" symbol={StashIcon} />
<div className="text">Stashed Changes</div>
<Octicon symbol={OcticonSymbol.chevronRight} />
<Octicon symbol={octicons.chevronRight} />
</button>
)
}

View file

@ -10,7 +10,7 @@ import {
import { IAvatarUser } from '../../models/avatar'
import { Avatar } from '../lib/avatar'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { LinkButton } from '../lib/link-button'
import { OkCancelButtonGroup } from '../dialog'
import { getConfigValue } from '../../lib/git/config'
@ -190,7 +190,7 @@ export class CommitMessageAvatar extends React.Component<
error: isError,
warning: !isError,
})
const symbol = isError ? OcticonSymbol.stop : OcticonSymbol.alert
const symbol = isError ? octicons.stop : octicons.alert
return (
<div className={classes} ref={this.warningBadgeRef}>

View file

@ -13,8 +13,8 @@ import { Button } from '../lib/button'
import { Loading } from '../lib/loading'
import { AuthorInput } from '../lib/author-input/author-input'
import { FocusContainer } from '../lib/focus-container'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { Octicon, OcticonSymbolVariant } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
import { Author, UnknownAuthor, isKnownAuthor } from '../../models/author'
import { IMenuItem } from '../../lib/menu-item'
import { Commit, ICommitContext } from '../../models/commit'
@ -57,16 +57,17 @@ import { Dispatcher } from '../dispatcher'
import { formatCommitMessage } from '../../lib/format-commit-message'
import { useRepoRulesLogic } from '../../lib/helpers/repo-rules'
const addAuthorIcon = {
const addAuthorIcon: OcticonSymbolVariant = {
w: 18,
h: 13,
d:
p: [
'M14 6V4.25a.75.75 0 0 1 1.5 0V6h1.75a.75.75 0 1 1 0 1.5H15.5v1.75a.75.75 0 0 ' +
'1-1.5 0V7.5h-1.75a.75.75 0 1 1 0-1.5H14zM8.5 4a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 ' +
'0zm.063 3.064a3.995 3.995 0 0 0 1.2-4.429A3.996 3.996 0 0 0 8.298.725a4.01 4.01 0 0 ' +
'0-6.064 1.91 3.987 3.987 0 0 0 1.2 4.43A5.988 5.988 0 0 0 0 12.2a.748.748 0 0 0 ' +
'.716.766.751.751 0 0 0 .784-.697 4.49 4.49 0 0 1 1.39-3.04 4.51 4.51 0 0 1 6.218 ' +
'0 4.49 4.49 0 0 1 1.39 3.04.748.748 0 0 0 .786.73.75.75 0 0 0 .714-.8 5.989 5.989 0 0 0-3.435-5.136z',
'1-1.5 0V7.5h-1.75a.75.75 0 1 1 0-1.5H14zM8.5 4a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 ' +
'0zm.063 3.064a3.995 3.995 0 0 0 1.2-4.429A3.996 3.996 0 0 0 8.298.725a4.01 4.01 0 0 ' +
'0-6.064 1.91 3.987 3.987 0 0 0 1.2 4.43A5.988 5.988 0 0 0 0 12.2a.748.748 0 0 0 ' +
'.716.766.751.751 0 0 0 .784-.697 4.49 4.49 0 0 1 1.39-3.04 4.51 4.51 0 0 1 6.218 ' +
'0 4.49 4.49 0 0 1 1.39 3.04.748.748 0 0 0 .786.73.75.75 0 0 0 .714-.8 5.989 5.989 0 0 0-3.435-5.136z',
],
}
interface ICommitMessageProps {
@ -1279,7 +1280,7 @@ export class CommitMessage extends React.Component<
tooltipClassName="length-hint-tooltip"
ariaLabel="Open Summary Length Info"
>
<Octicon symbol={OcticonSymbol.lightBulb} />
<Octicon symbol={octicons.lightBulb} />
</ToggledtippedContent>
)
}
@ -1313,7 +1314,7 @@ export class CommitMessage extends React.Component<
onClick={this.toggleRuleFailurePopover}
>
<Octicon
symbol={canBypass ? OcticonSymbol.alert : OcticonSymbol.stop}
symbol={canBypass ? octicons.alert : octicons.stop}
className={canBypass ? 'warning-icon' : 'error-icon'}
/>
</button>

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import { assertNever } from '../../lib/fatal-error'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
export enum CommitWarningIcon {
Warning,
@ -11,20 +11,20 @@ export enum CommitWarningIcon {
const renderIcon = (icon: CommitWarningIcon) => {
let className = ''
let symbol = OcticonSymbol.alert
let symbol = octicons.alert
switch (icon) {
case CommitWarningIcon.Warning:
className = 'warning-icon'
symbol = OcticonSymbol.alert
symbol = octicons.alert
break
case CommitWarningIcon.Information:
className = 'information-icon'
symbol = OcticonSymbol.info
symbol = octicons.info
break
case CommitWarningIcon.Error:
className = 'error-icon'
symbol = OcticonSymbol.stop
symbol = octicons.stop
break
default:
assertNever(icon, `Unexpected icon value ${icon}`)

View file

@ -4,7 +4,7 @@ import { IRefCheck } from '../../lib/ci-checks/ci-checks'
import { IMenuItem, showContextualMenu } from '../../lib/menu-item'
import { Button } from '../lib/button'
import { Octicon, syncClockwise } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
interface ICICheckReRunButtonProps {
readonly disabled: boolean
@ -44,7 +44,7 @@ export class CICheckReRunButton extends React.PureComponent<ICICheckReRunButtonP
const text =
this.props.canReRunFailed && this.failedChecksExist ? (
<>
Re-run <Octicon symbol={OcticonSymbol.triangleDown} />
Re-run <Octicon symbol={octicons.triangleDown} />
</>
) : (
'Re-run Checks'

View file

@ -5,7 +5,7 @@ import { IRefCheck } from '../../lib/ci-checks/ci-checks'
import { Octicon } from '../octicons'
import { getClassNameForCheck, getSymbolForCheck } from '../branches/ci-status'
import classNames from 'classnames'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { TooltippedContent } from '../lib/tooltipped-content'
import { CICheckRunActionsJobStepList } from './ci-check-run-actions-job-step-list'
import { IAPIWorkflowJobStep } from '../../lib/api'
@ -123,9 +123,7 @@ export class CICheckRunListItem extends React.PureComponent<
<div className="job-step-toggled-indicator">
<Octicon
symbol={
isCheckRunExpanded
? OcticonSymbol.chevronUp
: OcticonSymbol.chevronDown
isCheckRunExpanded ? octicons.chevronUp : octicons.chevronDown
}
/>
</div>
@ -181,7 +179,7 @@ export class CICheckRunListItem extends React.PureComponent<
return (
<div className={classes} onClick={this.rerunJob}>
<TooltippedContent tooltip={tooltip}>
<Octicon symbol={OcticonSymbol.sync} />
<Octicon symbol={octicons.sync} />
</TooltippedContent>
</div>
)

View file

@ -24,7 +24,7 @@ import {
import { CICheckRunList } from './ci-check-run-list'
import { encodePathAsUrl } from '../../lib/path'
import { PopupType } from '../../models/popup'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Donut } from '../donut'
import {
supportsRerunningChecks,
@ -253,14 +253,14 @@ export class CICheckRunPopover extends React.PureComponent<
return (
<Octicon
className={'completeness-indicator-success'}
symbol={OcticonSymbol.checkCircleFill}
symbol={octicons.checkCircleFill}
/>
)
case allFailure: {
return (
<Octicon
className={'completeness-indicator-error'}
symbol={OcticonSymbol.xCircleFill}
symbol={octicons.xCircleFill}
/>
)
}

View file

@ -11,7 +11,7 @@ import {
IAPICheckSuite,
} from '../../lib/api'
import { Octicon } from '../octicons'
import * as OcticonSymbol from './../octicons/octicons.generated'
import * as octicons from './../octicons/octicons.generated'
import { encodePathAsUrl } from '../../lib/path'
import { offsetFromNow } from '../../lib/offset-from'
@ -188,7 +188,7 @@ export class CICheckRunRerunDialog extends React.Component<
}${pluralize} that cannot be re-run`
return (
<div className="non-re-run-info warning-helper-text">
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
{`${warningPrefix}. A check run cannot be re-run if the check is more than one month old,
the check or its dependent has not completed, or the check is not configured to be

View file

@ -1,7 +1,7 @@
import { IAPIRepository } from '../../lib/api'
import { IFilterListGroup, IFilterListItem } from '../lib/filter-list'
import { OcticonSymbolType } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { OcticonSymbol } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
import entries from 'lodash/entries'
import groupBy from 'lodash/groupBy'
import { caseInsensitiveEquals, compare } from '../../lib/compare'
@ -20,7 +20,7 @@ export interface ICloneableRepositoryListItem extends IFilterListItem {
readonly name: string
/** The icon for the repo. */
readonly icon: OcticonSymbolType
readonly icon: OcticonSymbol
/** The clone URL. */
readonly url: string
@ -29,15 +29,15 @@ export interface ICloneableRepositoryListItem extends IFilterListItem {
readonly archived?: boolean
}
function getIcon(gitHubRepo: IAPIRepository): OcticonSymbolType {
function getIcon(gitHubRepo: IAPIRepository): OcticonSymbol {
if (gitHubRepo.private) {
return OcticonSymbol.lock
return octicons.lock
}
if (gitHubRepo.fork) {
return OcticonSymbol.repoForked
return octicons.repoForked
}
return OcticonSymbol.repo
return octicons.repo
}
const toListItems = (repositories: ReadonlyArray<IAPIRepository>) =>

View file

@ -3,7 +3,7 @@ import * as React from 'react'
import { CloningRepository } from '../models/cloning-repository'
import { ICloneProgress } from '../models/progress'
import { Octicon } from './octicons'
import * as OcticonSymbol from './octicons/octicons.generated'
import * as octicons from './octicons/octicons.generated'
import { UiView } from './ui-view'
import { TooltippedContent } from './lib/tooltipped-content'
@ -25,7 +25,7 @@ export class CloningRepositoryView extends React.Component<
return (
<UiView id="cloning-repository-view">
<div className="title-container">
<Octicon symbol={OcticonSymbol.desktopDownload} />
<Octicon symbol={octicons.desktopDownload} />
<div className="title">Cloning {this.props.repository.name}</div>
</div>
<progress value={progressValue} />

View file

@ -1,6 +1,6 @@
import { clipboard } from 'electron'
import React from 'react'
import * as OcticonSymbol from './octicons/octicons.generated'
import * as octicons from './octicons/octicons.generated'
import { Octicon } from './octicons'
import { sleep } from '../lib/promise'
import { Button } from './lib/button'
@ -41,9 +41,9 @@ export class CopyButton extends React.Component<
public renderSymbol() {
const { showCopied } = this.state
const symbol = showCopied ? OcticonSymbol.check : OcticonSymbol.copy
const symbol = showCopied ? octicons.check : octicons.copy
return <Octicon symbol={symbol} />
return <Octicon symbol={symbol} height={24} />
}
public render() {

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
/**
* A component used for displaying short error messages inline
@ -17,7 +17,7 @@ export class DialogError extends React.Component {
public render() {
return (
<div className="dialog-banner dialog-error" role="alert">
<Octicon symbol={OcticonSymbol.stop} />
<Octicon symbol={octicons.stop} />
<div>{this.props.children}</div>
</div>
)

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon, syncClockwise } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
interface IDialogHeaderProps {
/**
@ -67,7 +67,7 @@ export class DialogHeader extends React.Component<IDialogHeaderProps, {}> {
onClick={this.onCloseButtonClick}
aria-label="Close"
>
<Octicon symbol={OcticonSymbol.x} />
<Octicon symbol={octicons.x} />
</button>
)
}

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
/**
* A component used for displaying short success messages inline
@ -17,7 +17,7 @@ export class DialogSuccess extends React.Component {
public render() {
return (
<div className="dialog-banner dialog-success" role="alert">
<Octicon symbol={OcticonSymbol.check} />
<Octicon symbol={octicons.check} />
<div>{this.props.children}</div>
</div>
)

View file

@ -1,6 +1,6 @@
import React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { LinkButton } from '../lib/link-button'
import { ITextDiff, LineEndingsChange } from '../../models/diff'
@ -34,7 +34,7 @@ export class DiffContentsWarning extends React.Component<IDiffContentsWarningPro
<div className="diff-contents-warning-container">
{items.map((item, i) => (
<div className="diff-contents-warning" key={i}>
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
{this.getWarningMessageForItem(item)}
</div>
))}

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import { Checkbox, CheckboxValue } from '../lib/checkbox'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { RadioButton } from '../lib/radio-button'
import {
Popover,
@ -99,9 +99,9 @@ export class DiffOptions extends React.Component<
{buttonLabel}
</Tooltip>
<span ref={this.gearIconRef}>
<Octicon symbol={OcticonSymbol.gear} />
<Octicon symbol={octicons.gear} />
</span>
<Octicon symbol={OcticonSymbol.triangleDown} />
<Octicon symbol={octicons.triangleDown} />
</button>
{this.state.isPopoverOpen && this.renderPopover()}
</div>

View file

@ -10,16 +10,19 @@ import {
import { ILineTokens } from '../../lib/highlighter/types'
import classNames from 'classnames'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { narrowNoNewlineSymbol } from './text-diff'
import { shallowEquals, structuralEquals } from '../../lib/equality'
import { DiffHunkExpansionType } from '../../models/diff'
import { DiffHunkExpansionType, DiffSelectionType } from '../../models/diff'
import { PopoverAnchorPosition } from '../lib/popover'
import { WhitespaceHintPopover } from './whitespace-hint-popover'
import { TooltipDirection } from '../lib/tooltip'
import { Button } from '../lib/button'
import { diffCheck } from '../octicons/diff-check'
import { enableDiffCheckMarks } from '../../lib/feature-flag'
import { diffCheck, diffDash } from '../octicons/diff'
import {
enableDiffCheckMarks,
enableGroupDiffCheckmarks,
} from '../../lib/feature-flag'
enum DiffRowPrefix {
Added = '+',
@ -27,6 +30,56 @@ enum DiffRowPrefix {
Nothing = '\u{A0}',
}
/**
* This interface is used to pass information about a continuous group of
* selectable rows to the row component as it pertains to a given row.
*
* Primarily used for the styling of the row and it's check all control.
*/
export interface IRowSelectableGroup {
/**
* Whether or not the row is the first in the selectable group
*/
isFirst: boolean
/**
* Whether or not the row is the last in the selectable group
*/
isLast: boolean
/**
* Whether or not the group is hovered by the mouse
*/
isHovered: boolean
/**
* The selection state of the group - 'All', 'Partial', or 'None'
*/
selectionState: DiffSelectionType
/** The group's diff type, all 'added', all 'deleted', or a mix = 'modified */
diffType: DiffRowType
/**
* The height of the rows in the group
*/
height: number
/**
* The line numbers associated with the group
*/
lineNumbers: ReadonlyArray<number>
/**
* The line numbers identifiers associated with the group.
*
* If the line numbers are [4, 5, 6] then the lineNumbersIdentifiers could be
* something like [`4-before`, `4-after`, `5-after`, `6-after`] as the line
* number is not unique without the "before" or "after" suffix
*/
lineNumbersIdentifiers: ReadonlyArray<string>
}
interface ISideBySideDiffRowProps {
/**
* The row data. This contains most of the information used to render the row.
@ -154,6 +207,9 @@ interface ISideBySideDiffRowProps {
/** Whether or not to show the diff check marks indicating inclusion in a commit */
readonly showDiffCheckMarks: boolean
/** The selectable group details */
readonly rowSelectableGroup: IRowSelectableGroup | null
}
interface ISideBySideDiffRowState {
@ -170,30 +226,39 @@ export class SideBySideDiffRow extends React.Component<
}
public render() {
const { row, showSideBySideDiff, beforeClassNames, afterClassNames } =
this.props
const {
row,
showSideBySideDiff,
beforeClassNames,
afterClassNames,
isDiffSelectable,
} = this.props
const baseRowClasses = classNames('row', {
'has-check-all-control':
enableGroupDiffCheckmarks() &&
this.props.showDiffCheckMarks &&
isDiffSelectable,
})
const beforeClasses = classNames('before', ...beforeClassNames)
const afterClasses = classNames('after', ...afterClassNames)
switch (row.type) {
case DiffRowType.Hunk: {
const className = ['row', 'hunk-info']
if (row.expansionType === DiffHunkExpansionType.Both) {
className.push('expandable-both')
}
const rowClasses = classNames('hunk-info', baseRowClasses, {
'expandable-both': row.expansionType === DiffHunkExpansionType.Both,
})
return (
<div className={classNames(className)}>
<div className={rowClasses}>
{this.renderHunkHeaderGutter(row.hunkIndex, row.expansionType)}
{this.renderContentFromString(row.content)}
</div>
)
}
case DiffRowType.Context:
const rowClasses = classNames('context', baseRowClasses)
const { beforeLineNumber, afterLineNumber } = row
if (!showSideBySideDiff) {
return (
<div className="row context">
<div className={rowClasses}>
<div className="before">
{this.renderLineNumbers(
[beforeLineNumber, afterLineNumber],
@ -206,7 +271,7 @@ export class SideBySideDiffRow extends React.Component<
}
return (
<div className="row context">
<div className={rowClasses}>
<div className="before">
{this.renderLineNumber(beforeLineNumber, DiffColumn.Before)}
{this.renderContentFromString(row.content, row.beforeTokens)}
@ -220,16 +285,17 @@ export class SideBySideDiffRow extends React.Component<
case DiffRowType.Added: {
const { lineNumber, isSelected } = row.data
const rowClasses = classNames('added', baseRowClasses)
if (!showSideBySideDiff) {
return (
<div className="row added">
<div className={rowClasses}>
{this.renderHunkHandle()}
<div className={afterClasses}>
{this.renderLineNumbers(
[undefined, lineNumber],
DiffColumn.After,
isSelected
)}
{this.renderHunkHandle()}
{this.renderContent(row.data, DiffRowPrefix.Added)}
{this.renderWhitespaceHintPopover(DiffColumn.After)}
</div>
@ -238,33 +304,34 @@ export class SideBySideDiffRow extends React.Component<
}
return (
<div className="row added">
<div className={rowClasses}>
<div className={beforeClasses}>
{this.renderLineNumber(undefined, DiffColumn.Before)}
{this.renderContentFromString('')}
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
</div>
{this.renderHunkHandle()}
<div className={afterClasses}>
{this.renderLineNumber(lineNumber, DiffColumn.After, isSelected)}
{this.renderContent(row.data, DiffRowPrefix.Added)}
{this.renderWhitespaceHintPopover(DiffColumn.After)}
</div>
{this.renderHunkHandle()}
</div>
)
}
case DiffRowType.Deleted: {
const { lineNumber, isSelected } = row.data
const rowClasses = classNames('deleted', baseRowClasses)
if (!showSideBySideDiff) {
return (
<div className="row deleted">
<div className={rowClasses}>
{this.renderHunkHandle()}
<div className={beforeClasses}>
{this.renderLineNumbers(
[lineNumber, undefined],
DiffColumn.Before,
isSelected
)}
{this.renderHunkHandle()}
{this.renderContent(row.data, DiffRowPrefix.Deleted)}
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
</div>
@ -273,25 +340,26 @@ export class SideBySideDiffRow extends React.Component<
}
return (
<div className="row deleted">
<div className={rowClasses}>
<div className={beforeClasses}>
{this.renderLineNumber(lineNumber, DiffColumn.Before, isSelected)}
{this.renderContent(row.data, DiffRowPrefix.Deleted)}
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
</div>
{this.renderHunkHandle()}
<div className={afterClasses}>
{this.renderLineNumber(undefined, DiffColumn.After)}
{this.renderContentFromString('', [])}
{this.renderWhitespaceHintPopover(DiffColumn.After)}
</div>
{this.renderHunkHandle()}
</div>
)
}
case DiffRowType.Modified: {
const { beforeData: before, afterData: after } = row
const rowClasses = classNames('modified', baseRowClasses)
return (
<div className="row modified">
<div className={rowClasses}>
<div className={beforeClasses}>
{this.renderLineNumber(
before.lineNumber,
@ -301,6 +369,7 @@ export class SideBySideDiffRow extends React.Component<
{this.renderContent(before, DiffRowPrefix.Deleted)}
{this.renderWhitespaceHintPopover(DiffColumn.Before)}
</div>
{this.renderHunkHandle()}
<div className={afterClasses}>
{this.renderLineNumber(
after.lineNumber,
@ -310,7 +379,6 @@ export class SideBySideDiffRow extends React.Component<
{this.renderContent(after, DiffRowPrefix.Added)}
{this.renderWhitespaceHintPopover(DiffColumn.After)}
</div>
{this.renderHunkHandle()}
</div>
)
}
@ -374,7 +442,7 @@ export class SideBySideDiffRow extends React.Component<
// This can only be the first hunk
case DiffHunkExpansionType.Up:
return {
icon: OcticonSymbol.foldUp,
icon: octicons.foldUp,
title: 'Expand Up',
handler: this.onExpandHunk(hunkIndex, expansionType),
}
@ -382,13 +450,13 @@ export class SideBySideDiffRow extends React.Component<
// second to last hunk down.
case DiffHunkExpansionType.Down:
return {
icon: OcticonSymbol.foldDown,
icon: octicons.foldDown,
title: 'Expand Down',
handler: this.onExpandHunk(hunkIndex - 1, expansionType),
}
case DiffHunkExpansionType.Short:
return {
icon: OcticonSymbol.fold,
icon: octicons.fold,
title: 'Expand All',
handler: this.onExpandHunk(hunkIndex, expansionType),
}
@ -421,12 +489,10 @@ export class SideBySideDiffRow extends React.Component<
hunkIndex: number,
expansionType: DiffHunkExpansionType
) {
const width = this.lineGutterWidth
if (expansionType === DiffHunkExpansionType.None) {
return (
<div
className="hunk-expansion-handle"
style={{ width: this.lineGutterWidth }}
>
<div className="hunk-expansion-handle" style={{ width }}>
<div className="hunk-expansion-placeholder" />
</div>
)
@ -440,7 +506,7 @@ export class SideBySideDiffRow extends React.Component<
return (
<div
className="hunk-expansion-handle selectable hoverable"
style={{ width: this.lineGutterWidth }}
style={{ width }}
>
<Button
onClick={elementInfo.handler}
@ -482,27 +548,120 @@ export class SideBySideDiffRow extends React.Component<
}
private renderHunkHandle() {
if (!this.props.isDiffSelectable) {
const { isDiffSelectable, rowSelectableGroup } = this.props
if (!isDiffSelectable || rowSelectableGroup === null) {
return null
}
// In unified mode, the hunk handle left position depends on the line gutter
// width.
const style: React.CSSProperties = this.props.showSideBySideDiff
? {}
: { left: this.lineGutterWidth }
const placeHolder = <div className="hunk-handle-place-holder"></div>
return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
className="hunk-handle hoverable"
if (!rowSelectableGroup.isFirst) {
return placeHolder
}
const {
height,
selectionState,
lineNumbers,
lineNumbersIdentifiers,
diffType,
} = rowSelectableGroup
const style = { height }
const onlyOneLine = lineNumbers.length === 1
const hunkHandleClassName = classNames('hunk-handle', 'hoverable', {
// selected is a class if any line in the group is selected
selected: selectionState !== DiffSelectionType.None,
})
const checkAllId = lineNumbersIdentifiers.join('-')
/* The hunk-handle is a a single element with a calculated height of all the
rows in the selectable group (See `getRowSelectableGroupHeight` in
`side-by-side-diff.tsx`). This gives us a single element to be our control
of the check all functionality. It is positioned absolutely over the
hunk-handle-place-holders in each row in order to provide one element that
is interactive.
Other notes: I originally attempted to just use a single hunk-handle
for the first row in a group as the heights of the rows are calculated and
the rows do not clip overflow. However, that messes with the initial
measurement for cache of the first row's height as the cell measurer will
include the hunk handles initially calculated height (num rows times
default row height) in it's measurement. (Resulting in the first row in a
group heights = to however many lines in the group x 20) Thus, I decided to
use the place holder element in each row to define the width of the hunk
handle in the row and just position the hunk handle over them. A bit on the
hacky side.
*/
const hunkHandle = (
<label
htmlFor={checkAllId}
onMouseEnter={this.onMouseEnterHunk}
onMouseLeave={this.onMouseLeaveHunk}
onClick={this.onClickHunk}
onContextMenu={this.onContextMenuHunk}
className={hunkHandleClassName}
style={style}
></div>
>
<span className="focus-handle">
{(!enableGroupDiffCheckmarks() || !this.props.showDiffCheckMarks) && (
<div className="increased-hover-surface" style={{ height }} />
)}
{!onlyOneLine && this.getCheckAllOcticon(selectionState)}
{!onlyOneLine && (
<span className="sr-only">
{' '}
Lines {lineNumbers.at(0)} to {lineNumbers.at(-1)}{' '}
{diffType === DiffRowType.Added
? 'added'
: diffType === DiffRowType.Deleted
? 'deleted'
: 'modified'}
</span>
)}
</span>
</label>
)
const checkAllControl = (
<input
className="sr-only"
id={checkAllId}
type="checkbox"
aria-controls={lineNumbersIdentifiers.join(' ')}
aria-checked={
selectionState === DiffSelectionType.All
? true
: selectionState === DiffSelectionType.Partial
? 'mixed'
: false
}
onChange={this.onClickHunk}
onFocus={this.onHunkFocus}
onBlur={this.onHunkBlur}
/>
)
return (
<>
{!onlyOneLine && checkAllControl}
{hunkHandle}
{placeHolder}
</>
)
}
private getCheckAllOcticon = (selectionState: DiffSelectionType) => {
if (!enableGroupDiffCheckmarks() || !this.props.showDiffCheckMarks) {
return null
}
if (selectionState === DiffSelectionType.All) {
return <Octicon symbol={diffCheck} />
}
if (selectionState === DiffSelectionType.Partial) {
return <Octicon symbol={diffDash} />
}
return null
}
private getLineNumbersContainerID(column: DiffColumn) {
@ -532,7 +691,12 @@ export class SideBySideDiffRow extends React.Component<
'line-selected': isSelected,
hover: this.props.isHunkHovered,
})
const checkboxId = wrapperID ? wrapperID + '-checkbox' : undefined
// Note: This id is used by the check all aria-controls attribute,
// modification of this should be reflected there.
const checkboxId = `${lineNumbers.filter(ln => ln !== undefined).at(0)}-${
column === DiffColumn.After ? 'after' : 'before'
}`
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
@ -548,7 +712,15 @@ export class SideBySideDiffRow extends React.Component<
<label htmlFor={checkboxId}>
{this.renderLineNumberCheck(isSelected)}
{lineNumbers.map((lineNumber, index) => (
<span key={index}>{lineNumber}</span>
<span key={index}>
{lineNumber && <span className="sr-only">Line </span>}
{lineNumber}
{lineNumber && (
<span className="sr-only">
{column === DiffColumn.After ? ' added' : ' deleted'}
</span>
)}
</span>
))}
</label>
</div>
@ -734,6 +906,18 @@ export class SideBySideDiffRow extends React.Component<
}
}
private onHunkFocus = () => {
if ('hunkStartLine' in this.props.row) {
this.props.onMouseEnterHunk(this.props.row.hunkStartLine)
}
}
private onHunkBlur = () => {
if ('hunkStartLine' in this.props.row) {
this.props.onMouseLeaveHunk(this.props.row.hunkStartLine)
}
}
private onExpandHunk =
(hunkIndex: number, kind: DiffHunkExpansionType) => () => {
this.props.onExpandHunk(hunkIndex, kind)

View file

@ -29,7 +29,7 @@ import {
OverscanIndicesGetterParams,
defaultOverscanIndicesGetter,
} from 'react-virtualized'
import { SideBySideDiffRow } from './side-by-side-diff-row'
import { IRowSelectableGroup, SideBySideDiffRow } from './side-by-side-diff-row'
import memoize from 'memoize-one'
import {
findInteractiveOriginalDiffRange,
@ -580,6 +580,7 @@ export class SideBySideDiff extends React.Component<
afterTokens={this.state.afterTokens}
temporarySelection={this.state.temporarySelection}
hoveredHunk={this.state.hoveredHunk}
showDiffCheckMarks={this.props.showDiffCheckMarks}
isSelectable={canSelect(this.props.file)}
fileSelection={this.getSelection()}
// rows are memoized and include things like the
@ -609,6 +610,134 @@ export class SideBySideDiff extends React.Component<
return defaultOverscanIndicesGetter({ ...params, startIndex, stopIndex })
}
/**
* Gathers information about if the row is in a selectable group. This
* information is used to facilitate the use of check all feature for the
* selectable group.
*
* This will return null if the row is not in a selectable group. A group is
* more than one row.
*/
private getRowSelectableGroupDetails(
row: SimplifiedDiffRow,
prev: SimplifiedDiffRow,
next: SimplifiedDiffRow
): IRowSelectableGroup | null {
if (!('hunkStartLine' in row)) {
// can't be a selection hunk without a hunkStartLine
return null
}
const { diff, hoveredHunk } = this.state
const selectableType = [
DiffRowType.Added,
DiffRowType.Deleted,
DiffRowType.Modified,
]
if (!selectableType.includes(row.type)) {
// We only care about selectable rows
return null
}
const range = findInteractiveOriginalDiffRange(
diff.hunks,
row.hunkStartLine
)
if (range === null) {
// We only care about ranges with more than one line
return null
}
const selection = this.getSelection()
if (selection === undefined) {
// We only care about selectable rows.. so if no selection, no selectable rows
return null
}
const { from, to } = range
const { lineNumbers, lineNumbersIdentifiers, diffType } =
this.getRowGroupLineNumberData(row.hunkStartLine)
return {
isFirst: prev === undefined || !selectableType.includes(prev.type),
isLast: next === undefined || !selectableType.includes(next.type),
isHovered: hoveredHunk === row.hunkStartLine,
selectionState: selection.isRangeSelected(from, to - from + 1),
height: this.getRowSelectableGroupHeight(row.hunkStartLine),
lineNumbers: Array.from(lineNumbers),
lineNumbersIdentifiers,
diffType,
}
}
private getRowGroupLineNumberData = (hunkStartLine: number) => {
const rows = getDiffRows(
this.state.diff,
this.props.showSideBySideDiff,
this.canExpandDiff()
)
const lineNumbers = new Set<number>()
let hasAfter = false
let hasBefore = false
const lineNumbersIdentifiers = rows.flatMap(r => {
if (!('hunkStartLine' in r) || r.hunkStartLine !== hunkStartLine) {
return []
}
if (r.type === DiffRowType.Added) {
lineNumbers.add(r.data.lineNumber)
hasAfter = true
return `${r.data.lineNumber}-after`
}
if (r.type === DiffRowType.Deleted) {
lineNumbers.add(r.data.lineNumber)
hasBefore = true
return `${r.data.lineNumber}-before`
}
if (r.type === DiffRowType.Modified) {
hasAfter = true
hasBefore = true
lineNumbers.add(r.beforeData.lineNumber)
lineNumbers.add(r.afterData.lineNumber)
return [
`${r.beforeData.lineNumber}-before`,
`${r.afterData.lineNumber}-after`,
]
}
return []
})
const diffType =
hasAfter && hasBefore
? DiffRowType.Modified
: hasAfter
? DiffRowType.Added
: DiffRowType.Deleted
return { lineNumbersIdentifiers, lineNumbers, diffType }
}
private getRowSelectableGroupHeight = (hunkStartLine: number) => {
const rows = getDiffRows(
this.state.diff,
this.props.showSideBySideDiff,
this.canExpandDiff()
)
return rows.reduce((acc, r, index) => {
if (!('hunkStartLine' in r) || r.hunkStartLine !== hunkStartLine) {
return acc
}
return acc + this.getRowHeight({ index })
}, 0)
}
private renderRow = ({ index, parent, style, key }: ListRowProps) => {
const { diff } = this.state
const rows = getDiffRows(
@ -644,8 +773,14 @@ export class SideBySideDiff extends React.Component<
const rowWithTokens = this.createFullRow(row, index)
const isHunkHovered =
'hunkStartLine' in row && this.state.hoveredHunk === row.hunkStartLine
const rowSelectableGroupDetails = this.getRowSelectableGroupDetails(
row,
prev,
next
)
// Just temporary until pass the whole row group data down.
const isHunkHovered = !!rowSelectableGroupDetails?.isHovered
return (
<CellMeasurer
@ -662,6 +797,7 @@ export class SideBySideDiff extends React.Component<
numRow={index}
isDiffSelectable={canSelect(this.props.file)}
isHunkHovered={isHunkHovered}
rowSelectableGroup={rowSelectableGroupDetails}
showSideBySideDiff={this.props.showSideBySideDiff}
hideWhitespaceInDiff={this.props.hideWhitespaceInDiff}
showDiffCheckMarks={this.props.showDiffCheckMarks}

View file

@ -3,7 +3,7 @@ import { parseRepositoryIdentifier } from '../../lib/remote-parsing'
import { ISubmoduleDiff } from '../../models/diff'
import { LinkButton } from '../lib/link-button'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { SuggestedAction } from '../suggested-actions'
import { Ref } from '../lib/ref'
import { CopyButton } from '../copy-button'
@ -11,23 +11,23 @@ import { shortenSHA } from '../../models/commit'
type SubmoduleItemIcon =
| {
readonly octicon: typeof OcticonSymbol.info
readonly octicon: typeof octicons.info
readonly className: 'info-icon'
}
| {
readonly octicon: typeof OcticonSymbol.diffModified
readonly octicon: typeof octicons.diffModified
readonly className: 'modified-icon'
}
| {
readonly octicon: typeof OcticonSymbol.diffAdded
readonly octicon: typeof octicons.diffAdded
readonly className: 'added-icon'
}
| {
readonly octicon: typeof OcticonSymbol.diffRemoved
readonly octicon: typeof octicons.diffRemoved
readonly className: 'removed-icon'
}
| {
readonly octicon: typeof OcticonSymbol.fileDiff
readonly octicon: typeof octicons.fileDiff
readonly className: 'untracked-icon'
}
@ -82,7 +82,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
: ` (${repoIdentifier.hostname})`
return this.renderSubmoduleDiffItem(
{ octicon: OcticonSymbol.info, className: 'info-icon' },
{ octicon: octicons.info, className: 'info-icon' },
<>
This is a submodule based on the repository{' '}
<LinkButton
@ -107,7 +107,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
if (oldSHA !== null && newSHA !== null) {
return this.renderSubmoduleDiffItem(
{ octicon: OcticonSymbol.diffModified, className: 'modified-icon' },
{ octicon: octicons.diffModified, className: 'modified-icon' },
<>
This submodule changed its commit from{' '}
{this.renderCommitSHA(oldSHA, 'previous')} to{' '}
@ -116,7 +116,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
)
} else if (oldSHA === null && newSHA !== null) {
return this.renderSubmoduleDiffItem(
{ octicon: OcticonSymbol.diffAdded, className: 'added-icon' },
{ octicon: octicons.diffAdded, className: 'added-icon' },
<>
This submodule {verb} added pointing at commit{' '}
{this.renderCommitSHA(newSHA)}.{suffix}
@ -124,7 +124,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
)
} else if (oldSHA !== null && newSHA === null) {
return this.renderSubmoduleDiffItem(
{ octicon: OcticonSymbol.diffRemoved, className: 'removed-icon' },
{ octicon: octicons.diffRemoved, className: 'removed-icon' },
<>
This submodule {verb} removed while it was pointing at commit{' '}
{this.renderCommitSHA(oldSHA)}.{suffix}
@ -164,7 +164,7 @@ export class SubmoduleDiff extends React.Component<ISubmoduleDiffProps> {
: 'modified'
return this.renderSubmoduleDiffItem(
{ octicon: OcticonSymbol.fileDiff, className: 'untracked-icon' },
{ octicon: octicons.fileDiff, className: 'untracked-icon' },
<>
This submodule has {changes} changes. Those changes must be committed
inside of the submodule before they can be part of the parent

View file

@ -52,18 +52,21 @@ import {
DiffExpansionKind,
expandWholeTextDiff,
} from './text-diff-expansion'
import { OcticonSymbolVariant } from '../octicons'
import { createOcticonElement } from '../octicons/octicon'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { WhitespaceHintPopover } from './whitespace-hint-popover'
import { PopoverAnchorPosition } from '../lib/popover'
import { DiffContentsWarning } from './diff-contents-warning'
// This is a custom version of the no-newline octicon that's exactly as
// tall as it needs to be (8px) which helps with aligning it on the line.
export const narrowNoNewlineSymbol = {
export const narrowNoNewlineSymbol: OcticonSymbolVariant = {
w: 16,
h: 8,
d: 'm 16,1 0,3 c 0,0.55 -0.45,1 -1,1 l -3,0 0,2 -3,-3 3,-3 0,2 2,0 0,-2 2,0 z M 8,4 C 8,6.2 6.2,8 4,8 1.8,8 0,6.2 0,4 0,1.8 1.8,0 4,0 6.2,0 8,1.8 8,4 Z M 1.5,5.66 5.66,1.5 C 5.18,1.19 4.61,1 4,1 2.34,1 1,2.34 1,4 1,4.61 1.19,5.17 1.5,5.66 Z M 7,4 C 7,3.39 6.81,2.83 6.5,2.34 L 2.34,6.5 C 2.82,6.81 3.39,7 4,7 5.66,7 7,5.66 7,4 Z',
p: [
'm 16,1 0,3 c 0,0.55 -0.45,1 -1,1 l -3,0 0,2 -3,-3 3,-3 0,2 2,0 0,-2 2,0 z M 8,4 C 8,6.2 6.2,8 4,8 1.8,8 0,6.2 0,4 0,1.8 1.8,0 4,0 6.2,0 8,1.8 8,4 Z M 1.5,5.66 5.66,1.5 C 5.18,1.19 4.61,1 4,1 2.34,1 1,2.34 1,4 1,4.61 1.19,5.17 1.5,5.66 Z M 7,4 C 7,3.39 6.81,2.83 6.5,2.34 L 2.34,6.5 C 2.82,6.81 3.39,7 4,7 5.66,7 7,5.66 7,4 Z',
],
}
type ChangedFile = WorkingDirectoryFileChange | CommittedFileChange
@ -101,7 +104,7 @@ function createNoNewlineIndicatorWidget() {
const widget = document.createElement('span')
const titleId = uuid()
const { w, h, d } = narrowNoNewlineSymbol
const { w, h, p } = narrowNoNewlineSymbol
const xmlns = 'http://www.w3.org/2000/svg'
const svgElem = document.createElementNS(xmlns, 'svg')
@ -119,7 +122,7 @@ function createNoNewlineIndicatorWidget() {
const pathElem = document.createElementNS(xmlns, 'path')
pathElem.setAttribute('role', 'presentation')
pathElem.setAttribute('d', d)
pathElem.setAttribute('d', p[0])
pathElem.textContent = 'No newline at end of file'
svgElem.appendChild(pathElem)
@ -1127,7 +1130,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
marker.appendChild(hunkExpandUpHandle)
hunkExpandUpHandle.appendChild(
createOcticonElement(OcticonSymbol.foldUp, 'hunk-expand-icon')
createOcticonElement(octicons.foldUp, 'hunk-expand-icon')
)
const hunkExpandDownHandle = document.createElement('button')
@ -1143,7 +1146,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
marker.appendChild(hunkExpandDownHandle)
hunkExpandDownHandle.appendChild(
createOcticonElement(OcticonSymbol.foldDown, 'hunk-expand-icon')
createOcticonElement(octicons.foldDown, 'hunk-expand-icon')
)
const hunkExpandWholeHandle = document.createElement('button')
@ -1160,7 +1163,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
hunkExpandWholeHandle.appendChild(
createOcticonElement(
OcticonSymbol.foldDown,
octicons.foldDown,
'hunk-expand-icon',
'hunk-expand-down-icon'
)
@ -1168,7 +1171,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
hunkExpandWholeHandle.appendChild(
createOcticonElement(
OcticonSymbol.foldUp,
octicons.foldUp,
'hunk-expand-icon',
'hunk-expand-up-icon'
)
@ -1176,7 +1179,7 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
hunkExpandWholeHandle.appendChild(
createOcticonElement(
OcticonSymbol.unfold,
octicons.unfold,
'hunk-expand-icon',
'hunk-expand-short-icon'
)

View file

@ -2031,7 +2031,7 @@ export class Dispatcher {
*
* This is used only on macOS.
*/
public async installCLI() {
public async installDarwinCLI() {
try {
await installCLI()
@ -3913,4 +3913,12 @@ export class Dispatcher {
) {
this.appStore.onChecksFailedNotification(repository, pullRequest, checks)
}
public setUnderlineLinksSetting(underlineLinks: boolean) {
return this.appStore._updateUnderlineLinks(underlineLinks)
}
public setDiffCheckMarksSetting(diffCheckMarks: boolean) {
return this.appStore._updateShowDiffCheckMarks(diffCheckMarks)
}
}

View file

@ -8,7 +8,7 @@ import { DragType, DropTarget, DropTargetType } from '../../models/drag-drop'
import { GitHubRepository } from '../../models/github-repository'
import { CommitListItem } from '../history/commit-list-item'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Account } from '../../models/account'
interface ICommitDragElementProps {
@ -79,7 +79,7 @@ export class CommitDragElement extends React.Component<
switch (currentDropTarget.type) {
case DropTargetType.Branch:
const copyToPlus = __DARWIN__ ? null : (
<Octicon className="copy-to-icon" symbol={OcticonSymbol.plus} />
<Octicon className="copy-to-icon" symbol={octicons.plus} />
)
toolTipContents = (
<>

View file

@ -2,7 +2,7 @@ import classNames from 'classnames'
import React from 'react'
import { Button } from './lib/button'
import { Octicon } from './octicons'
import * as OcticonSymbol from './octicons/octicons.generated'
import * as octicons from './octicons/octicons.generated'
import { MenuPane } from './app-menu'
import { ICheckboxMenuItem, MenuItem } from '../models/app-menu'
import { ClickSource, SelectionSource } from './lib/list'
@ -383,7 +383,7 @@ export class DropdownSelectButton extends React.Component<
ariaHaspopup={true}
ariaLabel={dropdownAriaLabel}
>
<Octicon symbol={OcticonSymbol.triangleDown} />
<Octicon symbol={octicons.triangleDown} />
</Button>
</div>
{this.renderSplitButtonOptions()}

View file

@ -8,7 +8,7 @@ import { RelativeTime } from '../relative-time'
import { CommitAttribution } from '../lib/commit-attribution'
import { AvatarStack } from '../lib/avatar-stack'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Draggable } from '../lib/draggable'
import { dragAndDropManager } from '../../lib/drag-and-drop-manager'
import {
@ -197,7 +197,7 @@ export class CommitListItem extends React.PureComponent<
className="unpushed-indicator"
tooltip={this.props.unpushedIndicatorTitle}
>
<Octicon symbol={OcticonSymbol.arrowUp} />
<Octicon symbol={octicons.arrowUp} />
</TooltippedContent>
)
}

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import classNames from 'classnames'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { RichText } from '../lib/rich-text'
import { Repository } from '../../models/repository'
import { Commit } from '../../models/commit'
@ -247,7 +247,7 @@ export class CommitSummary extends React.Component<
const expanded = this.props.isExpanded
const onClick = expanded ? this.onCollapse : this.onExpand
const icon = expanded ? OcticonSymbol.fold : OcticonSymbol.unfold
const icon = expanded ? octicons.fold : octicons.unfold
return (
<button onClick={onClick} className="expander">
@ -393,7 +393,7 @@ export class CommitSummary extends React.Component<
onMouseOver={this.onHighlightShasNotInDiff}
onMouseOut={this.onRemoveHighlightOfShas}
>
<Octicon symbol={OcticonSymbol.info} />
<Octicon symbol={octicons.info} />
<LinkButton onClick={this.showUnreachableCommits}>
{excludedCommitsCount} unreachable {commitsPluralized}
</LinkButton>{' '}
@ -431,7 +431,7 @@ export class CommitSummary extends React.Component<
className="commit-summary-meta-item without-truncation"
aria-label="SHA"
>
<Octicon symbol={OcticonSymbol.gitCommit} />
<Octicon symbol={octicons.gitCommit} />
<TooltippedCommitSHA
className="selectable"
commit={selectedCommits[0]}
@ -559,10 +559,7 @@ export class CommitSummary extends React.Component<
<>
{filesAdded > 0 ? (
<span>
<Octicon
className="files-added-icon"
symbol={OcticonSymbol.diffAdded}
/>
<Octicon className="files-added-icon" symbol={octicons.diffAdded} />
{filesAdded} added
</span>
) : null}
@ -570,7 +567,7 @@ export class CommitSummary extends React.Component<
<span>
<Octicon
className="files-modified-icon"
symbol={OcticonSymbol.diffModified}
symbol={octicons.diffModified}
/>
{filesModified} modified
</span>
@ -579,7 +576,7 @@ export class CommitSummary extends React.Component<
<span>
<Octicon
className="files-deleted-icon"
symbol={OcticonSymbol.diffRemoved}
symbol={octicons.diffRemoved}
/>
{filesRemoved} deleted
</span>
@ -588,7 +585,7 @@ export class CommitSummary extends React.Component<
<span>
<Octicon
className="files-renamed-icon"
symbol={OcticonSymbol.diffRenamed}
symbol={octicons.diffRenamed}
/>
{filesRenamed} renamed
</span>
@ -604,7 +601,7 @@ export class CommitSummary extends React.Component<
fileCount > 0 && hasFileDescription ? filesLongDescription : undefined
}
>
<Octicon symbol={OcticonSymbol.diff} />
<Octicon symbol={octicons.diff} />
{filesShortDescription}
</TooltippedContent>
)
@ -657,7 +654,7 @@ export class CommitSummary extends React.Component<
return (
<li className="commit-summary-meta-item" title={tags.join('\n')}>
<span>
<Octicon symbol={OcticonSymbol.tag} />
<Octicon symbol={octicons.tag} />
</span>
<span className="tags selectable">{tags.join(', ')}</span>

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { HighlightText } from '../lib/highlight-text'
import { Branch, IAheadBehind } from '../../models/branch'
import { IMatches } from '../../lib/fuzzy-find'
@ -108,18 +108,18 @@ export class CompareBranchListItem extends React.Component<
const { currentBranch, branch } = this.props
const { aheadBehind } = this.state
const isCurrentBranch = branch.name === currentBranch?.name
const icon = isCurrentBranch ? OcticonSymbol.check : OcticonSymbol.gitBranch
const icon = isCurrentBranch ? octicons.check : octicons.gitBranch
const aheadBehindElement = aheadBehind ? (
<div className="branch-commit-counter">
<span className="branch-commit-counter-item">
{aheadBehind.behind}
<Octicon className="icon" symbol={OcticonSymbol.arrowDown} />
<Octicon className="icon" symbol={octicons.arrowDown} />
</span>
<span className="branch-commit-counter-item">
{aheadBehind.ahead}
<Octicon className="icon" symbol={OcticonSymbol.arrowUp} />
<Octicon className="icon" symbol={octicons.arrowUp} />
</span>
</div>
) : null

View file

@ -19,7 +19,7 @@ import { IBranchListItem } from '../branches/group-branches'
import { TabBar } from '../tab-bar'
import { CompareBranchListItem } from './compare-branch-list-item'
import { FancyTextBox } from '../lib/fancy-text-box'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { SelectionSource } from '../lib/filter-list'
import { IMatches } from '../../lib/fuzzy-find'
import { Ref } from '../lib/ref'
@ -166,7 +166,7 @@ export class CompareSidebar extends React.Component<
<div className="compare-form">
<FancyTextBox
ariaLabel="Branch filter"
symbol={OcticonSymbol.gitBranch}
symbol={octicons.gitBranch}
displayClearButton={true}
placeholder={placeholderText}
onFocus={this.onTextBoxFocused}

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import classNames from 'classnames'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { RichText } from '../lib/rich-text'
import { Repository } from '../../models/repository'
import { Commit } from '../../models/commit'
@ -240,9 +240,7 @@ export class ExpandableCommitSummary extends React.Component<
}
ariaControls="expandable-commit-summary"
>
<Octicon
symbol={isExpanded ? OcticonSymbol.fold : OcticonSymbol.unfold}
/>
<Octicon symbol={isExpanded ? octicons.fold : octicons.unfold} />
</Button>
)
}
@ -382,7 +380,7 @@ export class ExpandableCommitSummary extends React.Component<
onMouseOver={this.onHighlightShasNotInDiff}
onMouseOut={this.onRemoveHighlightOfShas}
>
<Octicon symbol={OcticonSymbol.info} />
<Octicon symbol={octicons.info} />
<LinkButton onClick={this.showUnreachableCommits}>
{excludedCommitsCount} unreachable {commitsPluralized}
</LinkButton>{' '}
@ -459,7 +457,7 @@ export class ExpandableCommitSummary extends React.Component<
return (
<div className="ecs-meta-item commit-ref">
<Octicon symbol={OcticonSymbol.gitCommit} />
<Octicon symbol={octicons.gitCommit} />
<div className="ref selectable">{isExpanded ? sha : shortSha}</div>
<CopyButton ariaLabel="Copy the full SHA" copyContent={sha} />
</div>
@ -569,7 +567,7 @@ export class ExpandableCommitSummary extends React.Component<
return (
<div className="ecs-meta-item lines-added-deleted">
{isExpanded ? <Octicon symbol={OcticonSymbol.diff} /> : null}
{isExpanded ? <Octicon symbol={octicons.diff} /> : null}
<div className="lines-added">
{!isExpanded ? <>+{linesAdded}</> : <>{linesAdded} added lines</>}
</div>
@ -598,7 +596,7 @@ export class ExpandableCommitSummary extends React.Component<
return (
<div className="ecs-meta-item tags selectable">
<Octicon symbol={OcticonSymbol.tag} />
<Octicon symbol={octicons.tag} />
<span>{tags.join(', ')}</span>
</div>
)

View file

@ -3,8 +3,8 @@ import classNames from 'classnames'
import { ComputedAction } from '../../models/computed-action'
import { assertNever } from '../../lib/fatal-error'
import { Octicon, OcticonSymbolType } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { Octicon, OcticonSymbol } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
interface IActionStatusIconProps {
/** The status to display to the user */
@ -52,16 +52,16 @@ export class ActionStatusIcon extends React.Component<IActionStatusIconProps> {
}
}
function getSymbolForState(status: ComputedAction): OcticonSymbolType {
function getSymbolForState(status: ComputedAction): OcticonSymbol {
switch (status) {
case ComputedAction.Loading:
return OcticonSymbol.dotFill
return octicons.dotFill
case ComputedAction.Conflicts:
return OcticonSymbol.alert
return octicons.alert
case ComputedAction.Invalid:
return OcticonSymbol.x
return octicons.x
case ComputedAction.Clean:
return OcticonSymbol.check
return octicons.check
default:
return assertNever(status, `Unknown state: ${JSON.stringify(status)}`)
}

View file

@ -1,7 +1,7 @@
import * as React from 'react'
import { LinkButton } from '../lib/link-button'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Loading } from './loading'
import { Form } from './form'
import { Button } from './button'
@ -196,9 +196,10 @@ export class AuthenticationForm extends React.Component<
className="button-with-icon"
onClick={this.signInWithBrowser}
autoFocus={true}
role="link"
>
Sign in using your browser
<Octicon symbol={OcticonSymbol.linkExternal} />
<Octicon symbol={octicons.linkExternal} />
</Button>
)
}

View file

@ -2,7 +2,7 @@ import classNames from 'classnames'
import React from 'react'
import { Author, isKnownAuthor } from '../../../models/author'
import { Octicon, syncClockwise } from '../../octicons'
import * as OcticonSymbol from '../../octicons/octicons.generated'
import * as octicons from '../../octicons/octicons.generated'
import { getFullTextForAuthor, getDisplayTextForAuthor } from './author-text'
interface IAuthorHandleProps {
@ -132,13 +132,11 @@ export class AuthorHandle extends React.Component<IAuthorHandleProps> {
{!isKnownAuthor(author) && (
<Octicon
className={classNames('icon', { spin: author.state !== 'error' })}
symbol={
author.state === 'error' ? OcticonSymbol.stop : syncClockwise
}
symbol={author.state === 'error' ? octicons.stop : syncClockwise}
/>
)}
<button onClick={this.onRemoveClick} tabIndex={-1}>
<Octicon className="delete" symbol={OcticonSymbol.x} />
<Octicon className="delete" symbol={octicons.x} />
</button>
</div>
)

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { IAvatarUser } from '../../models/avatar'
import { Octicon } from '../octicons'
import { Octicon, OcticonSymbolVariant } from '../octicons'
import { API, getDotComAPIEndpoint, getHTMLURL } from '../../lib/api'
import { TooltippedContent } from './tooltipped-content'
import { TooltipDirection } from './tooltip'
@ -182,10 +182,12 @@ interface IAvatarState {
* The octicon has been tweaked to add some padding and so that it scales nicely in
* a square aspect ratio.
*/
const DefaultAvatarSymbol = {
const DefaultAvatarSymbol: OcticonSymbolVariant = {
w: 16,
h: 16,
d: 'M13 13.145a.844.844 0 0 1-.832.855H3.834A.846.846 0 0 1 3 13.142v-.856c0-2.257 3.333-3.429 3.333-3.429s.191-.35 0-.857c-.7-.531-.786-1.363-.833-3.429C5.644 2.503 7.056 2 8 2s2.356.502 2.5 2.571C10.453 6.637 10.367 7.47 9.667 8c-.191.506 0 .857 0 .857S13 10.03 13 12.286v.859z',
p: [
'M13 13.145a.844.844 0 0 1-.832.855H3.834A.846.846 0 0 1 3 13.142v-.856c0-2.257 3.333-3.429 3.333-3.429s.191-.35 0-.857c-.7-.531-.786-1.363-.833-3.429C5.644 2.503 7.056 2 8 2s2.356.502 2.5 2.571C10.453 6.637 10.367 7.47 9.667 8c-.191.506 0 .857 0 .857S13 10.03 13 12.286v.859z',
],
}
function getEmailAvatarUrl(ep: string) {

View file

@ -3,7 +3,7 @@ import { Branch, BranchType } from '../../models/branch'
import { Row } from './row'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Ref } from './ref'
import { IStashEntry } from '../../models/stash-entry'
import { enableMoveStash } from '../../lib/feature-flag'
@ -12,7 +12,7 @@ export function renderBranchHasRemoteWarning(branch: Branch) {
if (branch.upstream != null) {
return (
<Row className="warning-helper-text">
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
<p>
This branch is tracking <Ref>{branch.upstream}</Ref> and renaming this
branch will not change the branch name on the remote.
@ -39,7 +39,7 @@ export function renderBranchNameExistsOnRemoteWarning(
return (
<Row className="warning-helper-text">
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
<p>
A branch named <Ref>{sanitizedName}</Ref> already exists on the remote.
</p>
@ -53,7 +53,7 @@ export function renderStashWillBeLostWarning(stash: IStashEntry | null) {
}
return (
<Row className="warning-helper-text">
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
<p>
Your current stashed changes on this branch will no longer be visible in
GitHub Desktop if the branch is renamed.

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../../octicons'
import * as OcticonSymbol from '../../octicons/octicons.generated'
import * as octicons from '../../octicons/octicons.generated'
import { LinkButton } from '../link-button'
export function renderUnmergedFilesSummary(conflictedFilesCount: number) {
@ -16,7 +16,7 @@ export function renderAllResolved() {
return (
<div className="all-conflicts-resolved">
<div className="green-circle">
<Octicon symbol={OcticonSymbol.check} />
<Octicon symbol={octicons.check} />
</div>
<div className="message">All conflicts resolved</div>
</div>

View file

@ -12,7 +12,7 @@ import { Repository } from '../../../models/repository'
import { Dispatcher } from '../../dispatcher'
import { showContextualMenu } from '../../../lib/menu-item'
import { Octicon } from '../../octicons'
import * as OcticonSymbol from '../../octicons/octicons.generated'
import * as octicons from '../../octicons/octicons.generated'
import { PathText } from '../path-text'
import { ManualConflictResolution } from '../../../models/manual-conflict-resolution'
import {
@ -141,7 +141,7 @@ const renderResolvedFile: React.FunctionComponent<{
)
return (
<li key={props.path} className="unmerged-file-status-resolved">
<Octicon symbol={OcticonSymbol.fileCode} className="file-octicon" />
<Octicon symbol={octicons.fileCode} className="file-octicon" />
<div className="column-left" id={props.path}>
<PathText path={props.path} />
<div className="file-conflicts-status">{fileStatusSummary}</div>
@ -160,7 +160,7 @@ const renderResolvedFile: React.FunctionComponent<{
</Button>
)}
<div className="green-circle">
<Octicon symbol={OcticonSymbol.check} />
<Octicon symbol={octicons.check} />
</div>
</li>
)
@ -212,7 +212,7 @@ const renderManualConflictedFile: React.FunctionComponent<{
onClick={onDropdownClick}
>
Resolve
<Octicon symbol={OcticonSymbol.triangleDown} />
<Octicon symbol={octicons.triangleDown} />
</Button>
</div>
</>
@ -227,7 +227,7 @@ function renderConflictedFileWrapper(
): JSX.Element {
return (
<li key={path} className="unmerged-file-status-conflicts">
<Octicon symbol={OcticonSymbol.fileCode} className="file-octicon" />
<Octicon symbol={octicons.fileCode} className="file-octicon" />
{content}
</li>
)
@ -289,7 +289,7 @@ const renderConflictedFileWithConflictMarkers: React.FunctionComponent<{
ariaHaspopup="menu"
ariaExpanded={props.isFileResolutionOptionsMenuOpen}
>
<Octicon symbol={OcticonSymbol.triangleDown} />
<Octicon symbol={octicons.triangleDown} />
</Button>
</div>
</>

View file

@ -1,11 +1,11 @@
import * as React from 'react'
import { Octicon, OcticonSymbolType } from '../octicons'
import { Octicon, OcticonSymbol } from '../octicons'
import { TextBox, ITextBoxProps } from './text-box'
import classNames from 'classnames'
interface IFancyTextBoxProps extends ITextBoxProps {
/** Icon to render */
readonly symbol: OcticonSymbolType
readonly symbol: OcticonSymbol
/** Callback used to get a reference to internal TextBox */
readonly onRef: (textbox: TextBox) => void

View file

@ -4,7 +4,7 @@ import { LinkButton } from './link-button'
import { getDotComAPIEndpoint } from '../../lib/api'
import { isAttributableEmailFor } from '../../lib/email'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { AriaLiveContainer } from '../accessibility/aria-live-container'
interface IGitEmailNotFoundWarningProps {
@ -25,7 +25,7 @@ export class GitEmailNotFoundWarning extends React.Component<IGitEmailNotFoundWa
<span className="warning-icon"></span>
) : (
<span className="green-circle">
<Octicon className="check-icon" symbol={OcticonSymbol.check} />
<Octicon className="check-icon" symbol={octicons.check} />
</span>
)

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import { Octicon } from '../../octicons'
import * as OcticonSymbol from '../../octicons/octicons.generated'
import * as octicons from '../../octicons/octicons.generated'
import classNames from 'classnames'
import { AriaLiveContainer } from '../../accessibility/aria-live-container'
import { assertNever } from '../../../lib/fatal-error'
@ -85,9 +85,9 @@ export class InputDescription extends React.Component<IInputDescriptionProps> {
case InputDescriptionType.Caption:
return null
case InputDescriptionType.Warning:
return <Octicon symbol={OcticonSymbol.alert} />
return <Octicon symbol={octicons.alert} />
case InputDescriptionType.Error:
return <Octicon symbol={OcticonSymbol.stop} />
return <Octicon symbol={octicons.stop} />
default:
return assertNever(type, `Unknown input type ${type}`)
}

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import { ITextBoxProps, TextBox } from './text-box'
import { Button } from './button'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
interface IPasswordTextBoxState {
/**
@ -32,8 +32,8 @@ export class PasswordTextBox extends React.Component<
public render() {
const buttonIcon = this.state.showPassword
? OcticonSymbol.eye
: OcticonSymbol.eyeClosed
? octicons.eye
: octicons.eyeClosed
const type = this.state.showPassword ? 'text' : 'password'
const props: ITextBoxProps = { ...this.props, ...{ type } }
return (

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import { AppFileStatus, AppFileStatusKind } from '../../models/status'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { PathText } from './path-text'
interface IPathLabelProps {
@ -46,7 +46,7 @@ export class PathLabel extends React.Component<IPathLabelProps, {}> {
return (
<span {...props} aria-hidden={this.props.ariaHidden}>
<PathText path={status.oldPath} availableWidth={segmentWidth} />
<Octicon className="rename-arrow" symbol={OcticonSymbol.arrowRight} />
<Octicon className="rename-arrow" symbol={octicons.arrowRight} />
<PathText path={this.props.path} availableWidth={segmentWidth} />
</span>
)

View file

@ -2,7 +2,7 @@ import * as React from 'react'
import { Button } from './button'
import { Popover, PopoverAnchorPosition, PopoverDecoration } from './popover'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import classNames from 'classnames'
const maxPopoverContentHeight = 500
@ -74,7 +74,7 @@ export class PopoverDropdown extends React.Component<
onClick={this.closePopover}
aria-label="Close"
>
<Octicon symbol={OcticonSymbol.x} />
<Octicon symbol={octicons.x} />
</button>
</div>
<div className="popover-dropdown-content">{this.props.children}</div>
@ -95,7 +95,7 @@ export class PopoverDropdown extends React.Component<
>
<span className="popover-dropdown-button-label">{label}</span>
<span className="button-content">{buttonContent}</span>
<Octicon symbol={OcticonSymbol.triangleDown} />
<Octicon symbol={octicons.triangleDown} />
</Button>
{this.renderPopover()}
</div>

View file

@ -40,6 +40,8 @@ interface ISandboxedMarkdownProps {
/** The context of which markdown resides - such as PullRequest, PullRequestComment, Commit */
readonly markdownContext?: MarkdownContext
readonly underlineLinks: boolean
}
interface ISandboxedMarkdownState {
@ -202,7 +204,12 @@ export class SandboxedMarkdown extends React.PureComponent<
${scrapeVariable('--text-color')}
${scrapeVariable('--background-color')}
}
${css}
.markdown-body a {
text-decoration: ${this.props.underlineLinks ? 'underline' : 'inherit'};
}
</style>`
}

View file

@ -3,7 +3,7 @@ import classNames from 'classnames'
import { createUniqueId, releaseUniqueId } from './id-pool'
import { showContextualMenu } from '../../lib/menu-item'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { AriaLiveContainer } from '../accessibility/aria-live-container'
export interface ITextBoxProps {
@ -328,7 +328,7 @@ export class TextBox extends React.Component<ITextBoxProps, ITextBoxState> {
aria-label="Clear"
onClick={this.clearSearchText}
>
<Octicon symbol={OcticonSymbol.x} />
<Octicon symbol={octicons.x} />
</button>
)}
{this.state.valueCleared && (

View file

@ -393,3 +393,7 @@ export const requestNotificationsPermission = invokeProxy(
'request-notifications-permission',
0
)
/** Tell the main process to (un)install the CLI on Windows */
export const installWindowsCLI = sendProxy('install-windows-cli', 0)
export const uninstallWindowsCLI = sendProxy('uninstall-windows-cli', 0)

View file

@ -3,7 +3,7 @@ import { formatRebaseValue } from '../../../lib/rebase'
import { RichText } from '../../lib/rich-text'
import { Dialog, DialogContent } from '../../dialog'
import { Octicon } from '../../octicons'
import * as OcticonSymbol from '../../octicons/octicons.generated'
import * as octicons from '../../octicons/octicons.generated'
import { IMultiCommitOperationProgress } from '../../../models/progress'
interface IProgressDialogProps {
@ -39,7 +39,7 @@ export class ProgressDialog extends React.Component<IProgressDialogProps> {
<div className="details">
<div className="green-circle">
<Octicon symbol={OcticonSymbol.check} />
<Octicon symbol={octicons.check} />
</div>
<div className="summary">
<div className="message">

View file

@ -1,8 +1,8 @@
import * as React from 'react'
import { UiView } from '../ui-view'
import { Button } from '../lib/button'
import { Octicon, OcticonSymbolType } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { Octicon, OcticonSymbol } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
import {
WelcomeLeftTopImageUri,
WelcomeLeftBottomImageUri,
@ -351,7 +351,7 @@ export class NoRepositoriesView extends React.Component<
private onShowClone = () => this.props.onClone()
private renderButtonGroupButton(
symbol: OcticonSymbolType,
symbol: OcticonSymbol,
title: string,
onClick: () => void,
type?: 'submit',
@ -375,7 +375,7 @@ export class NoRepositoriesView extends React.Component<
if (this.props.tutorialPaused) {
return this.renderButtonGroupButton(
OcticonSymbol.mortarBoard,
octicons.mortarBoard,
__DARWIN__
? 'Return to In Progress Tutorial'
: 'Return to in progress tutorial',
@ -384,7 +384,7 @@ export class NoRepositoriesView extends React.Component<
)
} else {
return this.renderButtonGroupButton(
OcticonSymbol.mortarBoard,
octicons.mortarBoard,
__DARWIN__
? 'Create a Tutorial Repository…'
: 'Create a tutorial repository…',
@ -396,7 +396,7 @@ export class NoRepositoriesView extends React.Component<
private renderCloneButton() {
return this.renderButtonGroupButton(
OcticonSymbol.repoClone,
octicons.repoClone,
__DARWIN__
? 'Clone a Repository from the Internet…'
: 'Clone a repository from the Internet…',
@ -408,20 +408,20 @@ export class NoRepositoriesView extends React.Component<
private renderCreateRepositoryButton() {
return this.renderButtonGroupButton(
OcticonSymbol.plus,
octicons.plus,
__DARWIN__
? 'Create a New Repository on your Hard Drive…'
: 'Create a New Repository on your hard drive…',
? 'Create a New Repository on your Local Drive…'
: 'Create a New Repository on your local drive…',
this.props.onCreate
)
}
private renderAddExistingRepositoryButton() {
return this.renderButtonGroupButton(
OcticonSymbol.fileDirectory,
octicons.fileDirectory,
__DARWIN__
? 'Add an Existing Repository from your Hard Drive…'
: 'Add an Existing Repository from your hard drive…',
? 'Add an Existing Repository from your Local Drive…'
: 'Add an Existing Repository from your local drive…',
this.props.onAdd
)
}
@ -437,7 +437,7 @@ export class NoRepositoriesView extends React.Component<
</div>
<div className="drag-drop-info">
<Octicon symbol={OcticonSymbol.lightBulb} />
<Octicon symbol={octicons.lightBulb} />
<div>
<strong>ProTip!</strong> You can drag &amp; drop an existing
repository folder here to add it to Desktop

View file

@ -15,7 +15,7 @@ import {
import { Account } from '../../models/account'
import { API, IAPIWorkflowJobStep } from '../../lib/api'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { RepositoryWithGitHubRepository } from '../../models/repository'
import { CICheckRunActionsJobStepList } from '../check-runs/ci-check-run-actions-job-step-list'
import { LinkButton } from '../lib/link-button'
@ -101,7 +101,7 @@ export class PullRequestChecksFailed extends React.Component<
const header = (
<div className="ci-check-run-dialog-header">
<Octicon symbol={OcticonSymbol.xCircleFill} />
<Octicon symbol={octicons.xCircleFill} />
<div className="title-container">
<div className="summary">
{failedChecks.length} {pluralChecks} failed in your pull request

View file

@ -2,9 +2,8 @@ import * as React from 'react'
import { Dialog, DialogContent, DialogFooter } from '../dialog'
import { PullRequest } from '../../models/pull-request'
import { Dispatcher } from '../dispatcher'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { OcticonSymbolType } from '../octicons/octicons.generated'
import { Octicon, OcticonSymbol } from '../octicons'
import * as octicons from '../octicons/octicons.generated'
import { RepositoryWithGitHubRepository } from '../../models/repository'
import { SandboxedMarkdown } from '../lib/sandboxed-markdown'
import { LinkButton } from '../lib/link-button'
@ -22,7 +21,7 @@ interface IPullRequestCommentLikeProps {
readonly pullRequest: PullRequest
readonly eventDate: Date
readonly eventVerb: string
readonly eventIconSymbol: OcticonSymbolType
readonly eventIconSymbol: OcticonSymbol
readonly eventIconClass: string
readonly externalURL: string
readonly user: IAPIIdentity
@ -33,6 +32,8 @@ interface IPullRequestCommentLikeProps {
readonly switchingToPullRequest: boolean
readonly underlineLinks: boolean
readonly renderFooterContent: () => JSX.Element
readonly onSubmit: () => void
@ -174,6 +175,7 @@ export abstract class PullRequestCommentLike extends React.Component<IPullReques
repository={base.gitHubRepository}
onMarkdownLinkClicked={this.onMarkdownLinkClicked}
markdownContext={'PullRequestComment'}
underlineLinks={this.props.underlineLinks}
/>
)
}
@ -190,8 +192,8 @@ export abstract class PullRequestCommentLike extends React.Component<IPullReques
className={cls}
symbol={
pullRequest.draft
? OcticonSymbol.gitPullRequestDraft
: OcticonSymbol.gitPullRequest
? octicons.gitPullRequestDraft
: octicons.gitPullRequest
}
/>
)

View file

@ -19,6 +19,8 @@ interface IPullRequestCommentProps {
/** Map from the emoji shortcut (e.g., :+1:) to the image's local path. */
readonly emoji: Map<string, string>
readonly underlineLinks: boolean
/**
* Whether or not the dialog should offer to switch to the PR's repository or
* to checkout the PR branch when applicable (e.g. non-approved reviews).
@ -83,6 +85,7 @@ export class PullRequestComment extends React.Component<
renderFooterContent={this.renderFooterContent}
onSubmit={onSubmit}
onDismissed={onDismissed}
underlineLinks={this.props.underlineLinks}
accounts={accounts}
/>
)

View file

@ -2,8 +2,8 @@ import {
ValidNotificationPullRequestReview,
ValidNotificationPullRequestReviewState,
} from '../../lib/valid-notification-pull-request-review'
import * as OcticonSymbol from '../octicons/octicons.generated'
import { OcticonSymbolType } from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { OcticonSymbol } from '../octicons'
/** Returns the user-facing verb for a given review's state. */
export function getVerbForPullRequestReview(
@ -20,7 +20,7 @@ export function getVerbForPullRequestReview(
}
type ReviewStateIcon = {
symbol: OcticonSymbolType
symbol: OcticonSymbol
className: string
}
@ -31,17 +31,17 @@ export function getPullRequestReviewStateIcon(
switch (state) {
case 'APPROVED':
return {
symbol: OcticonSymbol.check,
symbol: octicons.check,
className: 'pr-review-approved',
}
case 'CHANGES_REQUESTED':
return {
symbol: OcticonSymbol.fileDiff,
symbol: octicons.fileDiff,
className: 'pr-review-changes-requested',
}
case 'COMMENTED':
return {
symbol: OcticonSymbol.eye,
symbol: octicons.eye,
className: 'pr-review-commented',
}
}

View file

@ -22,6 +22,8 @@ interface IPullRequestReviewProps {
/** Map from the emoji shortcut (e.g., :+1:) to the image's local path. */
readonly emoji: Map<string, string>
readonly underlineLinks: boolean
/**
* Whether or not the dialog should offer to switch to the PR's repository or
* to checkout the PR branch when applicable (e.g. non-approved reviews).
@ -85,6 +87,7 @@ export class PullRequestReview extends React.Component<
renderFooterContent={this.renderFooterContent}
onSubmit={onSubmit}
onDismissed={onDismissed}
underlineLinks={this.props.underlineLinks}
accounts={this.props.accounts}
/>
)

View file

@ -1,12 +1,13 @@
import { OcticonSymbolType } from './octicons.generated'
import { OcticonSymbolVariant } from '.'
/**
* An check mark produced by Gavin that is scaled for 12x12 as opposed to the
* regular 16x16 and is thicker for better visibility.
*/
export const diffCheck: OcticonSymbolType = {
export const diffCheck: OcticonSymbolVariant = {
w: 12,
h: 12,
d: 'M10.5303 2.96967C10.8232 3.26256 10.8232 3.73744 10.5303 4.03033L5.03033 9.53033C4.73744 9.82322 4.26256 9.82322 3.96967 9.53033L1.46967 7.03033C1.17678 6.73744 1.17678 6.26256 1.46967 5.96967C1.76256 5.67678 2.23744 5.67678 2.53033 5.96967L4.5 7.93934L9.46967 2.96967C9.76256 2.67678 10.2374 2.67678 10.5303 2.96967Z',
fr: 'evenodd',
p: [
'M10.5303 2.96967C10.8232 3.26256 10.8232 3.73744 10.5303 4.03033L5.03033 9.53033C4.73744 9.82322 4.26256 9.82322 3.96967 9.53033L1.46967 7.03033C1.17678 6.73744 1.17678 6.26256 1.46967 5.96967C1.76256 5.67678 2.23744 5.67678 2.53033 5.96967L4.5 7.93934L9.46967 2.96967C9.76256 2.67678 10.2374 2.67678 10.5303 2.96967Z',
],
}

View file

@ -0,0 +1,26 @@
import { OcticonSymbolVariant } from '.'
/**
* An check mark produced by Gavin that is scaled for 12x12 as opposed to the
* regular 16x16 and is thicker for better visibility.
*/
export const diffCheck: OcticonSymbolVariant = {
w: 12,
h: 12,
p: [
'M10.5303 2.96967C10.8232 3.26256 10.8232 3.73744 10.5303 4.03033L5.03033 9.53033C4.73744 9.82322 4.26256 9.82322 3.96967 9.53033L1.46967 7.03033C1.17678 6.73744 1.17678 6.26256 1.46967 5.96967C1.76256 5.67678 2.23744 5.67678 2.53033 5.96967L4.5 7.93934L9.46967 2.96967C9.76256 2.67678 10.2374 2.67678 10.5303 2.96967Z',
],
}
/**
* An dash for the "mixed" state of the diff hunk handle check all produced by
* Gavin that is scaled for 12x12 as opposed to the regular 16x16 and is thicker
* for better visibility.
*/
export const diffDash: OcticonSymbolVariant = {
w: 12,
h: 12,
p: [
'm1.3125 6c0-.41421.33579-.75.75-.75h7.875c.4142 0 .75.33579.75.75s-.3358.75-.75.75h-7.875c-.41421 0-.75-.33579-.75-.75z',
],
}

View file

@ -0,0 +1,54 @@
import * as React from 'react'
import { Dialog, DialogContent } from '../dialog'
import * as octicons from './octicons.generated'
import { Octicon, OcticonSymbolVariant, OcticonSymbolVariants } from '.'
interface IIconPreviewDialogProps {
readonly onDismissed: () => void
}
export class IconPreviewDialog extends React.Component<IIconPreviewDialogProps> {
public render() {
return (
<Dialog
id="octicons-preview-dialog"
className="octicons-preview-dialog"
title="Icon Preview"
onDismissed={this.props.onDismissed}
>
<DialogContent>
<ul className="octicons-preview-list">
{Object.entries(octicons).map(([name, variants]) =>
this.renderIconVariants(name, variants)
)}
</ul>
</DialogContent>
</Dialog>
)
}
private renderIconVariants(name: string, icons: OcticonSymbolVariants) {
return (
<li key={name}>
<h2>{name}</h2>
<ul>
{Object.entries(icons).map(([size, icon]) =>
this.renderIcon(name, size, icon)
)}
</ul>
</li>
)
}
private renderIcon(name: string, height: string, icon: OcticonSymbolVariant) {
const sizeText = `${icon.h}x${icon.w}`
const title = `${name} - ${sizeText}`
return (
<li key={`name-${sizeText}`}>
<Octicon height={parseInt(height)} symbol={icon} title={title} />
<small>{sizeText}</small>
</li>
)
}
}

View file

@ -1,4 +1,4 @@
export { OcticonSymbolType } from './octicons.generated'
export * from './octicons.generated'
export { Octicon } from './octicon'
export { iconForRepository } from './repository'
export { iconForStatus } from './status'

View file

@ -1,5 +1,5 @@
import * as React from 'react'
import { OcticonSymbolType } from './octicons.generated'
import { OcticonSymbol, OcticonSymbolVariant } from '.'
import classNames from 'classnames'
import ReactDOM from 'react-dom'
import { createObservableRef } from '../lib/observable-ref'
@ -7,11 +7,11 @@ import { Tooltip, TooltipDirection } from '../lib/tooltip'
interface IOcticonProps {
/**
* An instance of an object conforming to the OcticonSymbol
* type. Supports custom paths as well as those provided
* through the static properties of the OcticonSymbol class.
* An instance of an object conforming to the OcticonSymbol type. Supports
* custom paths as well as those provided through the static properties of
* the OcticonSymbol class.
*/
readonly symbol: OcticonSymbolType
readonly symbol: OcticonSymbol
/**
* An optional classname that will be appended to the default
@ -25,6 +25,8 @@ interface IOcticonProps {
readonly title?: string
readonly tooltipDirection?: TooltipDirection
readonly height?: number
}
/**
@ -34,14 +36,40 @@ interface IOcticonProps {
* which is why the width and height properties specify the maximum and
* not the minimum size.
*
* Usage: `<Octicon symbol={OcticonSymbol.mark_github} />`
* Usage: `<Octicon symbol={octicons.markGithub} />`
*/
export class Octicon extends React.Component<IOcticonProps, {}> {
private svgRef = createObservableRef<SVGSVGElement>()
public render() {
const { symbol, title, tooltipDirection } = this.props
const viewBox = `0 0 ${symbol.w} ${symbol.h}`
const { symbol } = this.props
if (this.isSingleVariant(symbol)) {
return this.renderIcon(symbol.p, symbol.h, symbol.w)
} else {
const height = this.props.height ?? 16
const naturalHeight = this.closestNaturalHeight(
Object.keys(symbol).map(h => parseInt(h, 10)) as Array<number>,
height
)
const scaledSymbol = symbol[naturalHeight]
if (scaledSymbol === undefined) {
// Should never happen, but if it does the app should still be usable
return null
}
const naturalWidth = scaledSymbol.w
const width = height * (naturalWidth / naturalHeight)
return this.renderIcon(scaledSymbol.p, height, width)
}
}
private renderIcon(paths: string[], height: number, width: number) {
const { title, tooltipDirection } = this.props
const viewBox = `0 0 ${width} ${height}`
const className = classNames('octicon', this.props.className)
// Hide the octicon from screen readers when it's only being used
@ -62,16 +90,53 @@ export class Octicon extends React.Component<IOcticonProps, {}> {
viewBox={viewBox}
ref={this.svgRef}
tabIndex={-1}
height={height}
width={width}
>
{title !== undefined && (
<Tooltip target={this.svgRef} direction={direction}>
{title}
</Tooltip>
)}
<path fillRule={symbol.fr} d={symbol.d} />
{paths.map((d, i) => (
<path key={i} d={d} />
))}
</svg>
)
}
/**
* Determine if the given symbol is a single variant or a set of variants.
*
* @param symbol The symbol to check.
* @returns True if the symbol is a single variant, false if it's a set.
*/
private isSingleVariant(
symbol: OcticonSymbol
): symbol is OcticonSymbolVariant {
return (
symbol instanceof Object &&
symbol.hasOwnProperty('p') &&
symbol.hasOwnProperty('h') &&
symbol.hasOwnProperty('w')
)
}
/**
* Find the closest natural height to the given height. Falls back to the
* first height in the list if none are larger or equal than the given height.
*
* @param naturalHeights The list of natural heights to choose from.
* @param height The height to find the closest natural height to.
* @returns The closest natural height to the given height.
*/
private closestNaturalHeight(naturalHeights: Array<number>, height: number) {
return naturalHeights.reduce(
(acc, naturalHeight) => (naturalHeight <= height ? naturalHeight : acc),
naturalHeights[0]
)
}
}
/**
@ -82,7 +147,7 @@ export class Octicon extends React.Component<IOcticonProps, {}> {
* @param id Optional identifier to set to the wrapper element.
*/
export function createOcticonElement(
symbol: OcticonSymbolType,
symbol: OcticonSymbol,
className?: string,
id?: string
) {

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { Repository } from '../../models/repository'
import { CloningRepository } from '../../models/cloning-repository'
@ -7,24 +7,24 @@ import { CloningRepository } from '../../models/cloning-repository'
*/
export function iconForRepository(repository: Repository | CloningRepository) {
if (repository instanceof CloningRepository) {
return OcticonSymbol.desktopDownload
return octicons.desktopDownload
}
if (repository.missing) {
return OcticonSymbol.alert
return octicons.alert
}
const gitHubRepo = repository.gitHubRepository
if (!gitHubRepo) {
return OcticonSymbol.deviceDesktop
return octicons.deviceDesktop
}
if (gitHubRepo.isPrivate) {
return OcticonSymbol.lock
return octicons.lock
}
if (gitHubRepo.fork) {
return OcticonSymbol.repoForked
return octicons.repoForked
}
return OcticonSymbol.repo
return octicons.repo
}

View file

@ -3,8 +3,8 @@ import {
AppFileStatus,
isConflictWithMarkers,
} from '../../models/status'
import * as OcticonSymbol from './octicons.generated'
import { OcticonSymbolType } from '../octicons'
import * as octicons from './octicons.generated'
import { OcticonSymbol } from '../octicons'
import { assertNever } from '../../lib/fatal-error'
/**
@ -13,25 +13,25 @@ import { assertNever } from '../../lib/fatal-error'
*
* Used in file lists.
*/
export function iconForStatus(status: AppFileStatus): OcticonSymbolType {
export function iconForStatus(status: AppFileStatus): OcticonSymbol {
switch (status.kind) {
case AppFileStatusKind.New:
case AppFileStatusKind.Untracked:
return OcticonSymbol.diffAdded
return octicons.diffAdded
case AppFileStatusKind.Modified:
return OcticonSymbol.diffModified
return octicons.diffModified
case AppFileStatusKind.Deleted:
return OcticonSymbol.diffRemoved
return octicons.diffRemoved
case AppFileStatusKind.Renamed:
return OcticonSymbol.diffRenamed
return octicons.diffRenamed
case AppFileStatusKind.Conflicted:
if (isConflictWithMarkers(status)) {
const conflictsCount = status.conflictMarkerCount
return conflictsCount > 0 ? OcticonSymbol.alert : OcticonSymbol.check
return conflictsCount > 0 ? octicons.alert : octicons.check
}
return OcticonSymbol.alert
return octicons.alert
case AppFileStatusKind.Copied:
return OcticonSymbol.diffAdded
return octicons.diffAdded
default:
return assertNever(status, `Unknown file status ${status}`)
}

View file

@ -1,17 +1,17 @@
import { OcticonSymbolType } from './octicons.generated'
import { OcticonSymbolVariant } from '.'
/**
* An horizontally flipped (i.e `scaleX(-1)`) but otherwise unmodified version of
* the `sync` octicon.
*/
export const syncClockwise: OcticonSymbolType = {
export const syncClockwise: OcticonSymbolVariant = {
w: 16,
h: 16,
d:
p: [
'M8 2.5c1.645 0 3.123.722 4.131 1.869l-1.204 1.204a.25.25 0 0 0 .177.427h3.646a.25.25 ' +
'0 0 0 .25-.25V2.104a.25.25 0 0 0-.427-.177l-1.38 1.38A7.001 7.001 0 0 0 1.05 7.16a.75.75 ' +
'0 1 0 1.49.178A5.501 5.501 0 0 1 8 2.5zm6.294 5.505a.75.75 0 0 0-.833.656 5.501 5.501 ' +
'0 0 1-9.592 2.97l1.204-1.204A.25.25 0 0 0 4.896 10H1.25a.25.25 0 0 0-.25.25v3.646c0 ' +
'.223.27.335.427.177l1.38-1.38A7.001 7.001 0 0 0 14.95 8.84a.75.75 0 0 0-.657-.834z',
fr: 'evenodd',
'0 0 0 .25-.25V2.104a.25.25 0 0 0-.427-.177l-1.38 1.38A7.001 7.001 0 0 0 1.05 7.16a.75.75 ' +
'0 1 0 1.49.178A5.501 5.501 0 0 1 8 2.5zm6.294 5.505a.75.75 0 0 0-.833.656 5.501 5.501 ' +
'0 0 1-9.592 2.97l1.204-1.204A.25.25 0 0 0 4.896 10H1.25a.25.25 0 0 0-.25.25v3.646c0 ' +
'.223.27.335.427.177l1.38-1.38A7.001 7.001 0 0 0 14.95 8.84a.75.75 0 0 0-.657-.834z',
],
}

View file

@ -8,7 +8,7 @@ import { DialogFooter, OkCancelButtonGroup, Dialog } from '../dialog'
import { Dispatcher } from '../dispatcher'
import { Ref } from '../lib/ref'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { OpenPullRequestDialogHeader } from './open-pull-request-header'
import { PullRequestFilesChanged } from './pull-request-files-changed'
import { PullRequestMergeStatus } from './pull-request-merge-status'
@ -207,7 +207,7 @@ export class OpenPullRequestDialog extends React.Component<IOpenPullRequestDialo
return (
<div className="open-pull-request-message">
<div>
<Octicon symbol={OcticonSymbol.gitPullRequest} />
<Octicon symbol={octicons.gitPullRequest} />
<h3>There are no changes.</h3>
{message}
</div>
@ -225,7 +225,7 @@ export class OpenPullRequestDialog extends React.Component<IOpenPullRequestDialo
return (
<div className="open-pull-request-message">
<div>
<Octicon symbol={OcticonSymbol.gitPullRequest} />
<Octicon symbol={octicons.gitPullRequest} />
<h3>Could not find a default branch to compare against.</h3>
Select a base branch above.
</div>
@ -249,7 +249,7 @@ export class OpenPullRequestDialog extends React.Component<IOpenPullRequestDialo
const okButton = (
<>
{currentBranchHasPullRequest && (
<Octicon symbol={OcticonSymbol.linkExternal} />
<Octicon symbol={octicons.linkExternal} />
)}
{__DARWIN__
? `${viewCreate} Pull Request`

View file

@ -3,7 +3,7 @@ import { assertNever } from '../../lib/fatal-error'
import { ComputedAction } from '../../models/computed-action'
import { MergeTreeResult } from '../../models/merge'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
interface IPullRequestMergeStatusProps {
/** The result of merging the pull request branch into the base branch */
@ -39,7 +39,7 @@ export class PullRequestMergeStatus extends React.Component<IPullRequestMergeSta
return (
<span className="pr-merge-status-clean">
<strong>
<Octicon symbol={OcticonSymbol.check} /> Able to merge.
<Octicon symbol={octicons.check} /> Able to merge.
</strong>{' '}
These branches can be automatically merged.
</span>
@ -48,7 +48,7 @@ export class PullRequestMergeStatus extends React.Component<IPullRequestMergeSta
return (
<span className="pr-merge-status-conflicts">
<strong>
<Octicon symbol={OcticonSymbol.x} /> Can't automatically merge.
<Octicon symbol={octicons.x} /> Can't automatically merge.
</strong>{' '}
Dont worry, you can still create the pull request.
</span>

View file

@ -0,0 +1,90 @@
import * as React from 'react'
import { DialogContent } from '../dialog'
import { Checkbox, CheckboxValue } from '../lib/checkbox'
interface IAccessibilityPreferencesProps {
readonly underlineLinks: boolean
readonly onUnderlineLinksChanged: (value: boolean) => void
readonly showDiffCheckMarks: boolean
readonly onShowDiffCheckMarksChanged: (value: boolean) => void
}
export class Accessibility extends React.Component<
IAccessibilityPreferencesProps,
{}
> {
public constructor(props: IAccessibilityPreferencesProps) {
super(props)
}
public render() {
return (
<DialogContent>
<div className="advanced-section">
<h2>Accessibility</h2>
<Checkbox
label="Underline links"
value={
this.props.underlineLinks ? CheckboxValue.On : CheckboxValue.Off
}
onChange={this.onUnderlineLinksChanged}
ariaDescribedBy="underline-setting-description"
/>
<p
id="underline-setting-description"
className="git-settings-description"
>
When enabled, GitHub Desktop will underline links in commit
messages, comments, and other text fields. This can help make links
easier to distinguish. {this.renderExampleLink()}
</p>
<Checkbox
label="Show check marks in the diff"
value={
this.props.showDiffCheckMarks
? CheckboxValue.On
: CheckboxValue.Off
}
onChange={this.onShowDiffCheckMarksChanged}
ariaDescribedBy="diff-checkmarks-setting-description"
/>
<p
id="diff-checkmarks-setting-description"
className="git-settings-description"
>
When enabled, check marks will be displayed along side the line
numbers and groups of line numbers in the diff when committing. When
disabled, the line number controls will be less prominent.
</p>
</div>
</DialogContent>
)
}
private renderExampleLink() {
// The example link is rendered with inline style to override the global setting.
const style = {
textDecoration: this.props.underlineLinks ? 'underline' : 'none',
}
return (
<span className="link-button-component" style={style}>
This is an example link
</span>
)
}
private onUnderlineLinksChanged = (
event: React.FormEvent<HTMLInputElement>
) => {
this.props.onUnderlineLinksChanged(event.currentTarget.checked)
}
private onShowDiffCheckMarksChanged = (
event: React.FormEvent<HTMLInputElement>
) => {
this.props.onShowDiffCheckMarksChanged(event.currentTarget.checked)
}
}

View file

@ -29,7 +29,7 @@ import {
defaultUncommittedChangesStrategy,
} from '../../models/uncommitted-changes-strategy'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import {
isConfigFileLockError,
parseConfigLockFilePathFromError,
@ -42,6 +42,8 @@ import {
import { Prompts } from './prompts'
import { Repository } from '../../models/repository'
import { Notifications } from './notifications'
import { Accessibility } from './accessibility'
import { enableLinkUnderlines } from '../../lib/feature-flag'
interface IPreferencesProps {
readonly dispatcher: Dispatcher
@ -67,6 +69,8 @@ interface IPreferencesProps {
readonly selectedTheme: ApplicationTheme
readonly repositoryIndicatorsEnabled: boolean
readonly onOpenFileInExternalEditor: (path: string) => void
readonly underlineLinks: boolean
readonly showDiffCheckMarks: boolean
}
interface IPreferencesState {
@ -94,6 +98,7 @@ interface IPreferencesState {
readonly selectedExternalEditor: string | null
readonly availableShells: ReadonlyArray<Shell>
readonly selectedShell: Shell
/**
* If unable to save Git configuration values (name, email)
* due to an existing configuration lock file this property
@ -108,6 +113,10 @@ interface IPreferencesState {
readonly isLoadingGitConfig: boolean
readonly globalGitConfigPath: string | null
readonly underlineLinks: boolean
readonly showDiffCheckMarks: boolean
}
/** The app-level preferences component. */
@ -147,6 +156,8 @@ export class Preferences extends React.Component<
initiallySelectedTheme: this.props.selectedTheme,
isLoadingGitConfig: true,
globalGitConfigPath: null,
underlineLinks: this.props.underlineLinks,
showDiffCheckMarks: this.props.showDiffCheckMarks,
}
}
@ -236,33 +247,39 @@ export class Preferences extends React.Component<
type={TabBarType.Vertical}
>
<span>
<Octicon className="icon" symbol={OcticonSymbol.home} />
<Octicon className="icon" symbol={octicons.home} />
Accounts
</span>
<span>
<Octicon className="icon" symbol={OcticonSymbol.person} />
<Octicon className="icon" symbol={octicons.person} />
Integrations
</span>
<span>
<Octicon className="icon" symbol={OcticonSymbol.gitCommit} />
<Octicon className="icon" symbol={octicons.gitCommit} />
Git
</span>
<span>
<Octicon className="icon" symbol={OcticonSymbol.paintbrush} />
<Octicon className="icon" symbol={octicons.paintbrush} />
Appearance
</span>
<span>
<Octicon className="icon" symbol={OcticonSymbol.bell} />
<Octicon className="icon" symbol={octicons.bell} />
Notifications
</span>
<span>
<Octicon className="icon" symbol={OcticonSymbol.question} />
<Octicon className="icon" symbol={octicons.question} />
Prompts
</span>
<span>
<Octicon className="icon" symbol={OcticonSymbol.settings} />
<Octicon className="icon" symbol={octicons.gear} />
Advanced
</span>
{enableLinkUnderlines() && (
<span>
<Octicon className="icon" symbol={octicons.accessibility} />
Accessibility
</span>
)}
</TabBar>
{this.renderActiveTab()}
@ -423,6 +440,16 @@ export class Preferences extends React.Component<
)
break
}
case PreferencesTab.Accessibility:
View = (
<Accessibility
underlineLinks={this.state.underlineLinks}
showDiffCheckMarks={this.state.showDiffCheckMarks}
onShowDiffCheckMarksChanged={this.onShowDiffCheckMarksChanged}
onUnderlineLinksChanged={this.onUnderlineLinksChanged}
/>
)
break
default:
return assertNever(index, `Unknown tab index: ${index}`)
}
@ -525,6 +552,14 @@ export class Preferences extends React.Component<
this.props.dispatcher.setSelectedTheme(theme)
}
private onUnderlineLinksChanged = (underlineLinks: boolean) => {
this.setState({ underlineLinks })
}
private onShowDiffCheckMarksChanged = (showDiffCheckMarks: boolean) => {
this.setState({ showDiffCheckMarks })
}
private renderFooter() {
const hasDisabledError = this.state.disallowedCharactersMessage != null
@ -645,6 +680,12 @@ export class Preferences extends React.Component<
this.state.uncommittedChangesStrategy
)
this.props.dispatcher.setUnderlineLinksSetting(this.state.underlineLinks)
this.props.dispatcher.setDiffCheckMarksSetting(
this.state.showDiffCheckMarks
)
this.props.onDismissed()
}

View file

@ -9,7 +9,7 @@ import { merge } from '../../lib/merge'
import { caseInsensitiveCompare } from '../../lib/compare'
import { sanitizedRepositoryName } from '../add-repository/sanitized-repository-name'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { RepositoryPublicationSettings } from '../../models/publish-settings'
interface IPublishRepositoryProps {
@ -184,7 +184,7 @@ export class PublishRepository extends React.Component<
return (
<Row className="warning-helper-text">
<Octicon symbol={OcticonSymbol.alert} />
<Octicon symbol={octicons.alert} />
Will be created as {sanitizedName}
</Row>
)

View file

@ -6,7 +6,7 @@ import { Dispatcher } from './dispatcher'
import { Button } from './lib/button'
import { SandboxedMarkdown } from './lib/sandboxed-markdown'
import { Octicon } from './octicons'
import * as OcticonSymbol from './octicons/octicons.generated'
import * as octicons from './octicons/octicons.generated'
import classNames from 'classnames'
/**
@ -34,6 +34,8 @@ interface IPullRequestQuickViewProps {
/** Map from the emoji shortcut (e.g., :+1:) to the image's local path. */
readonly emoji: Map<string, string>
readonly underlineLinks: boolean
}
interface IPullRequestQuickViewState {
@ -158,11 +160,15 @@ export class PullRequestQuickView extends React.Component<
private renderHeader = (): JSX.Element => {
return (
<header className="header">
<Octicon symbol={OcticonSymbol.listUnordered} />
<Octicon symbol={octicons.listUnordered} />
<div className="action-needed">Review requested</div>
<Button className="button-with-icon" onClick={this.onViewOnGitHub}>
<Button
className="button-with-icon"
onClick={this.onViewOnGitHub}
role="link"
>
View on GitHub
<Octicon symbol={OcticonSymbol.linkExternal} />
<Octicon symbol={octicons.linkExternal} />
</Button>
</header>
)
@ -174,9 +180,7 @@ export class PullRequestQuickView extends React.Component<
<Octicon
className="icon"
symbol={
isDraft
? OcticonSymbol.gitPullRequestDraft
: OcticonSymbol.gitPullRequest
isDraft ? octicons.gitPullRequestDraft : octicons.gitPullRequest
}
/>
<span className="state">{isDraft ? 'Draft' : 'Open'}</span>
@ -211,6 +215,7 @@ export class PullRequestQuickView extends React.Component<
markdownContext={'PullRequest'}
onMarkdownLinkClicked={this.onMarkdownLinkClicked}
onMarkdownParsed={this.onMarkdownParsed}
underlineLinks={this.props.underlineLinks}
/>
</div>
)

View file

@ -15,6 +15,7 @@ interface IReleaseNotesProps {
readonly onDismissed: () => void
readonly emoji: Map<string, string>
readonly newReleases: ReadonlyArray<ReleaseSummary>
readonly underlineLinks: boolean
}
/**
@ -118,6 +119,7 @@ export class ReleaseNotes extends React.Component<IReleaseNotesProps, {}> {
markdown={pretext[0].message}
emoji={this.props.emoji}
onMarkdownLinkClicked={this.onMarkdownLinkClicked}
underlineLinks={this.props.underlineLinks}
/>
)
}

View file

@ -15,7 +15,7 @@ import { ILocalRepositoryState, Repository } from '../../models/repository'
import { Dispatcher } from '../dispatcher'
import { Button } from '../lib/button'
import { Octicon } from '../octicons'
import * as OcticonSymbol from '../octicons/octicons.generated'
import * as octicons from '../octicons/octicons.generated'
import { showContextualMenu } from '../../lib/menu-item'
import { IMenuItem } from '../../lib/menu-item'
import { PopupType } from '../../models/popup'
@ -282,7 +282,7 @@ export class RepositoriesList extends React.Component<
ariaExpanded={this.state.newRepositoryMenuExpanded}
>
Add
<Octicon symbol={OcticonSymbol.triangleDown} />
<Octicon symbol={octicons.triangleDown} />
</Button>
)
}

Some files were not shown because too many files have changed in this diff Show more