diff --git a/src/components/ActionButton.tsx b/src/components/ActionButton.tsx index 7c7d4d7bf..9059e01b4 100644 --- a/src/components/ActionButton.tsx +++ b/src/components/ActionButton.tsx @@ -26,7 +26,7 @@ export interface IActionButtonProps { /** * On-click event handler */ - onClick?: (event?: React.MouseEvent) => void; + onClick?: (event?: React.MouseEvent) => void; } /** diff --git a/src/components/BranchMenu.tsx b/src/components/BranchMenu.tsx index 550a49379..4edeff9de 100644 --- a/src/components/BranchMenu.tsx +++ b/src/components/BranchMenu.tsx @@ -17,6 +17,7 @@ import { newBranchButtonClass, wrapperClass } from '../style/BranchMenu'; +import { branchIcon } from '../style/icons'; import { Git, IGitExtension, Level } from '../tokens'; import { NewBranchDialog } from './NewBranchDialog'; @@ -270,13 +271,7 @@ export class BranchMenu extends React.Component< onClick={this._onBranchClickFactory(branch.name)} style={style} > - + {branch.name} ); diff --git a/src/components/FileItem.tsx b/src/components/FileItem.tsx index be66d9c5f..5a4f126b9 100644 --- a/src/components/FileItem.tsx +++ b/src/components/FileItem.tsx @@ -185,7 +185,7 @@ export class FileItem extends React.PureComponent { )} {this.props.actions} diff --git a/src/components/FilePath.tsx b/src/components/FilePath.tsx index 1dd4af195..88f7f47a8 100644 --- a/src/components/FilePath.tsx +++ b/src/components/FilePath.tsx @@ -1,11 +1,8 @@ +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { fileIcon } from '@jupyterlab/ui-components'; import * as React from 'react'; -import { classes } from 'typestyle'; -import { - fileIconStyle, - fileLabelStyle, - folderLabelStyle -} from '../style/FilePathStyle'; -import { extractFilename, getFileIconClassName } from '../utils'; +import { fileLabelStyle, folderLabelStyle } from '../style/FilePathStyle'; +import { extractFilename } from '../utils'; /** * FilePath component properties @@ -16,13 +13,9 @@ export interface IFilePathProps { */ filepath: string; /** - * Is file selected? - impact style of the icon + * File type */ - selected?: boolean; -} - -function getFileIconClass(path: string): string { - return getFileIconClassName(path); + filetype: DocumentRegistry.IFileType; } export const FilePath: React.FunctionComponent = ( @@ -33,16 +26,11 @@ export const FilePath: React.FunctionComponent = ( .slice(0, props.filepath.length - filename.length) .replace(/^\/|\/$/g, ''); // Remove leading and trailing '/' + const icon = props.filetype.icon || fileIcon; + return ( - + {filename} {folder} diff --git a/src/components/NewBranchDialog.tsx b/src/components/NewBranchDialog.tsx index bfc9c6b3c..5c2efb176 100644 --- a/src/components/NewBranchDialog.tsx +++ b/src/components/NewBranchDialog.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import { ListChildComponentProps, VariableSizeList } from 'react-window'; import { classes } from 'typestyle'; import { Logger } from '../logger'; +import { branchIcon } from '../style/icons'; import { actionsWrapperClass, activeListItemClass, @@ -313,14 +314,7 @@ export class NewBranchDialog extends React.Component< onClick={this._onBranchClickFactory(branch.name)} style={style} > - +

- + {flg ? ( ) : null} diff --git a/src/index.ts b/src/index.ts index a4408ebcd..9bfe889c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -112,6 +112,7 @@ async function activate( gitExtension = new GitExtension( serverSettings.serverRoot, docmanager, + app.docRegistry, settings ); diff --git a/src/model.ts b/src/model.ts index 37041c703..156baa476 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,5 +1,6 @@ import { IChangedArgs, PathExt } from '@jupyterlab/coreutils'; import { IDocumentManager } from '@jupyterlab/docmanager'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { JSONObject } from '@lumino/coreutils'; import { Poll } from '@lumino/polling'; @@ -26,10 +27,12 @@ export class GitExtension implements IGitExtension { constructor( serverRoot: string, docmanager: IDocumentManager = null, + docRegistry: DocumentRegistry = null, settings?: ISettingRegistry.ISettings ) { this._serverRoot = serverRoot; this._docmanager = docmanager; + this._docRegistry = docRegistry; this._settings = settings || null; this._taskHandler = new TaskHandler(this); @@ -519,6 +522,11 @@ export class GitExtension implements IGitExtension { return d; } ); + + data.modified_files = data.modified_files.map(f => { + f.type = this._resolveFileType(f.modified_file_path); + return f; + }); return data; } @@ -751,7 +759,11 @@ export class GitExtension implements IGitExtension { this._setStatus( data.files.map(file => { - return { ...file, status: decodeStage(file.x, file.y) }; + return { + ...file, + status: decodeStage(file.x, file.y), + type: this._resolveFileType(file.to) + }; }) ); } catch (err) { @@ -1076,6 +1088,21 @@ export class GitExtension implements IGitExtension { return path; } + /** + * Resolve path to filetype + */ + protected _resolveFileType(path: string): DocumentRegistry.IFileType { + // test if directory + if (path.endsWith('/')) { + return DocumentRegistry.defaultDirectoryFileType; + } + + return ( + this._docRegistry.getFileTypesForPath(path)[0] || + DocumentRegistry.defaultTextFileType + ); + } + /** * Set the repository status. * @@ -1131,6 +1158,7 @@ export class GitExtension implements IGitExtension { private _currentBranch: Git.IBranch; private _serverRoot: string; private _docmanager: IDocumentManager | null; + private _docRegistry: DocumentRegistry | null; private _diffProviders: { [key: string]: Git.IDiffCallback } = {}; private _isDisposed = false; private _markerCache: Markers = new Markers(() => this._markChanged.emit()); diff --git a/src/style/BranchMenu.ts b/src/style/BranchMenu.ts index cd5064b02..3ee92abcb 100644 --- a/src/style/BranchMenu.ts +++ b/src/style/BranchMenu.ts @@ -30,7 +30,7 @@ export const filterInputClass = style({ /* top | right | bottom | left */ padding: '1px 18px 2px 7px', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, @@ -86,7 +86,7 @@ export const newBranchButtonClass = style({ width: '7.7em', height: '2em', - color: 'white', + color: 'var(--jp-ui-inverse-font-color1)', fontSize: 'var(--jp-ui-font-size1)', backgroundColor: 'var(--jp-brand-color1)', @@ -101,19 +101,20 @@ export const listItemClass = style({ }); export const activeListItemClass = style({ - color: 'white!important', + color: 'var(--jp-ui-inverse-font-color1)!important', - backgroundColor: 'var(--jp-brand-color1)!important' + backgroundColor: 'var(--jp-brand-color1)!important', + + $nest: { + '& .jp-icon-selectable[fill]': { + fill: 'var(--jp-layout-color1)' + } + } }); export const listItemIconClass = style({ width: '16px', height: '16px', - marginRight: '4px', - - backgroundImage: 'var(--jp-icon-git-branch)', - backgroundSize: '16px', - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + marginRight: '4px' }); diff --git a/src/style/CommitBox.ts b/src/style/CommitBox.ts index 738a399e5..34a19b749 100644 --- a/src/style/CommitBox.ts +++ b/src/style/CommitBox.ts @@ -24,7 +24,7 @@ export const commitSummaryClass = style({ outline: 'none', overflowX: 'auto', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, @@ -52,7 +52,7 @@ export const commitDescriptionClass = style({ overflowX: 'auto', resize: 'none', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, @@ -90,7 +90,7 @@ export const commitButtonClass = style({ marginBottom: '0.5em', - color: 'white', + color: 'var(--jp-ui-inverse-font-color1)', fontSize: 'var(--jp-ui-font-size1)', cursor: 'pointer', diff --git a/src/style/FileItemStyle.ts b/src/style/FileItemStyle.ts index ecd697e95..47e552ef6 100644 --- a/src/style/FileItemStyle.ts +++ b/src/style/FileItemStyle.ts @@ -22,12 +22,12 @@ export const fileStyle = style( ); export const selectedFileStyle = style({ - color: 'white', + color: 'var(--jp-ui-inverse-font-color1)', background: 'var(--jp-brand-color1)', $nest: { '&:hover': { - color: 'white', + color: 'var(--jp-ui-inverse-font-color1)', background: 'var(--jp-brand-color1) !important' }, '&:hover .jp-icon-selectable[fill]': { @@ -37,7 +37,7 @@ export const selectedFileStyle = style({ stroke: 'var(--jp-layout-color2)' }, '& .jp-icon-selectable[fill]': { - fill: '#fff' + fill: 'var(--jp-layout-color1)' }, '& .jp-icon-selectable-inverse[fill]': { fill: 'var(--jp-brand-color1)' @@ -51,7 +51,7 @@ export const fileChangedLabelStyle = style({ }); export const selectedFileChangedLabelStyle = style({ - color: 'white !important' + color: 'var(--jp-ui-inverse-font-color1) !important' }); export const fileChangedLabelBrandStyle = style({ diff --git a/src/style/FileListStyle.ts b/src/style/FileListStyle.ts index e80df39b8..2499de811 100644 --- a/src/style/FileListStyle.ts +++ b/src/style/FileListStyle.ts @@ -7,43 +7,3 @@ export const fileListWrapperClass = style({ overflow: 'hidden', overflowY: 'auto' }); - -export const notebookFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-notebook)' -}); - -export const folderFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-directory)' -}); - -export const genericFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-file)' -}); - -export const yamlFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-yaml)' -}); - -export const markdownFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-markdown)' -}); - -export const imageFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-image)' -}); - -export const spreadsheetFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-spreadsheet)' -}); - -export const jsonFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-json)' -}); - -export const pythonFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-python)' -}); - -export const rKernelFileIconStyle = style({ - backgroundImage: 'var(--jp-icon-r-kernel)' -}); diff --git a/src/style/FilePathStyle.ts b/src/style/FilePathStyle.ts index c8c0248c3..b8ed11502 100644 --- a/src/style/FilePathStyle.ts +++ b/src/style/FilePathStyle.ts @@ -1,10 +1,6 @@ import { style } from 'typestyle'; export const fileIconStyle = style({ - alignSelf: 'center', - backgroundPosition: 'center', - backgroundRepeat: 'no-repeat', - backgroundSize: '16px', flex: '0 0 auto', height: '16px', width: '16px', diff --git a/src/style/NewBranchDialog.ts b/src/style/NewBranchDialog.ts index 450908e58..d3d21109c 100644 --- a/src/style/NewBranchDialog.ts +++ b/src/style/NewBranchDialog.ts @@ -73,7 +73,7 @@ export const nameInputClass = style({ /* top | right | bottom | left */ padding: '1px 18px 2px 7px', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, @@ -118,7 +118,7 @@ export const filterInputClass = style({ /* top | right | bottom | left */ padding: '1px 18px 2px 7px', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, @@ -191,9 +191,15 @@ export const listItemClass = style({ }); export const activeListItemClass = style({ - color: 'white!important', + color: 'var(--jp-ui-inverse-font-color1)!important', - backgroundColor: 'var(--jp-brand-color1)!important' + backgroundColor: 'var(--jp-brand-color1)!important', + + $nest: { + '& .jp-icon-selectable[fill]': { + fill: 'var(--jp-layout-color1)' + } + } }); export const listItemContentClass = style({ @@ -214,12 +220,7 @@ export const listItemIconClass = style({ height: '16px', /* top | right | bottom | left */ - margin: 'auto 8px auto 0', - - backgroundImage: 'var(--jp-icon-git-branch)', - backgroundSize: '16px', - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + margin: 'auto 8px auto 0' }); export const listItemTitleClass = style({}); @@ -244,7 +245,7 @@ export const buttonClass = style({ width: '9em', height: '2em', - color: 'white', + color: 'var(--jp-ui-inverse-font-color1)', fontSize: 'var(--jp-ui-font-size1)', border: '0', @@ -252,7 +253,16 @@ export const buttonClass = style({ }); export const cancelButtonClass = style({ - backgroundColor: '#757575' + backgroundColor: 'var(--md-grey-500)', + + $nest: { + '&:hover': { + backgroundColor: 'var(--md-grey-600)' + }, + '&:active': { + backgroundColor: 'var(--md-grey-700)' + } + } }); export const createButtonClass = style({ diff --git a/src/style/PastCommitNode.ts b/src/style/PastCommitNode.ts index cc763e52d..9bd625b7c 100644 --- a/src/style/PastCommitNode.ts +++ b/src/style/PastCommitNode.ts @@ -40,24 +40,24 @@ export const branchWrapperClass = style({ export const branchClass = style({ padding: '2px', - // Special case as black, regardless of theme, because + // Special case, regardless of theme, because // backgrounds of colors are not based on theme either - color: '#000000', - border: 'var(--jp-border-width) solid #424242', + color: 'var(--md-grey-900)', + border: 'var(--jp-border-width) solid var(--md-grey-700)', borderRadius: '4px', margin: '3px' }); export const remoteBranchClass = style({ - backgroundColor: '#ffcdd3' + backgroundColor: 'var(--md-blue-100)' }); export const localBranchClass = style({ - backgroundColor: '#b2ebf3' + backgroundColor: 'var(--md-orange-100)' }); export const workingBranchClass = style({ - backgroundColor: '#ffce83' + backgroundColor: 'var(--md-red-100)' }); export const commitExpandedClass = style({ diff --git a/src/style/ResetRevertDialog.ts b/src/style/ResetRevertDialog.ts index 0a3441736..0e92d587b 100644 --- a/src/style/ResetRevertDialog.ts +++ b/src/style/ResetRevertDialog.ts @@ -74,7 +74,7 @@ export const buttonClass = style({ width: '9em', height: '2em', - color: 'white', + color: 'var(--jp-ui-inverse-font-color1)', fontSize: 'var(--jp-ui-font-size1)', cursor: 'pointer', @@ -90,7 +90,16 @@ export const buttonClass = style({ }); export const cancelButtonClass = style({ - backgroundColor: '#757575' + backgroundColor: 'var(--md-grey-500)', + + $nest: { + '&:hover': { + backgroundColor: 'var(--md-grey-600)' + }, + '&:active': { + backgroundColor: 'var(--md-grey-700)' + } + } }); export const submitButtonClass = style({ @@ -126,7 +135,7 @@ export const commitSummaryClass = style({ outline: 'none', overflowX: 'auto', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, @@ -154,7 +163,7 @@ export const commitDescriptionClass = style({ overflowX: 'auto', resize: 'none', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', fontSize: 'var(--jp-ui-font-size1)', fontWeight: 300, diff --git a/src/style/SinglePastCommitInfo.ts b/src/style/SinglePastCommitInfo.ts index 2132e3d99..ddbb26dc4 100644 --- a/src/style/SinglePastCommitInfo.ts +++ b/src/style/SinglePastCommitInfo.ts @@ -63,7 +63,7 @@ export const iconClass = style({ export const insertionsIconClass = style({ $nest: { '.jp-icon3': { - fill: '#00dc00' + fill: 'var(--md-green-500)' } } }); @@ -71,7 +71,7 @@ export const insertionsIconClass = style({ export const deletionsIconClass = style({ $nest: { '.jp-icon3': { - fill: '#ff0000' + fill: '--var(--md-red-500)' } } }); diff --git a/src/style/SuspendModal.ts b/src/style/SuspendModal.ts index 49d14d144..e2051c0e7 100644 --- a/src/style/SuspendModal.ts +++ b/src/style/SuspendModal.ts @@ -4,6 +4,6 @@ export const fullscreenProgressClass = style({ position: 'absolute', top: '50%', left: '50%', - color: '#ffffff', + color: 'var(--jp-ui-inverse-font-color0)', textAlign: 'center' }); diff --git a/src/style/Toolbar.ts b/src/style/Toolbar.ts index cdf11ccbe..2a6c4edad 100644 --- a/src/style/Toolbar.ts +++ b/src/style/Toolbar.ts @@ -44,7 +44,7 @@ export const toolbarMenuButtonClass = style({ fontSize: 'var(--jp-ui-font-size1)', lineHeight: '1.5em', - color: 'var(--jp-ui-font-color0)', + color: 'var(--jp-ui-font-color1)', textAlign: 'left', border: 'none', diff --git a/src/utils.ts b/src/utils.ts index b251bb51a..48816f029 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,4 @@ import { PathExt } from '@jupyterlab/coreutils'; -import { - folderFileIconStyle, - genericFileIconStyle, - imageFileIconStyle, - jsonFileIconStyle, - rKernelFileIconStyle, - markdownFileIconStyle, - notebookFileIconStyle, - pythonFileIconStyle, - spreadsheetFileIconStyle, - yamlFileIconStyle -} from './style/FileListStyle'; import { Git } from './tokens'; /** Get the filename from a path */ @@ -40,47 +28,6 @@ export function decodeStage(x: string, y: string): Git.Status { return null; } -/** - * Get the extension of a given file - * - * @param path File path for which the icon should be found - */ -export function getFileIconClassName(path: string): string { - if (path[path.length - 1] === '/') { - return folderFileIconStyle; - } - const fileExtension = PathExt.extname(path).toLocaleLowerCase(); - switch (fileExtension) { - case '.md': - return markdownFileIconStyle; - case '.py': - return pythonFileIconStyle; - case '.ipynb': - return notebookFileIconStyle; - case '.json': - return jsonFileIconStyle; - case '.csv': - case '.xls': - case '.xlsx': - return spreadsheetFileIconStyle; - case '.r': - return rKernelFileIconStyle; - case '.yml': - case '.yaml': - return yamlFileIconStyle; - case '.svg': - case '.tiff': - case '.jpeg': - case '.jpg': - case '.gif': - case '.png': - case '.raw': - return imageFileIconStyle; - default: - return genericFileIconStyle; - } -} - /** * Returns a promise which resolves after a specified duration. * diff --git a/style/icons/branch.svg b/style/icons/branch.svg index fdd6fb8c2..775ec437f 100644 --- a/style/icons/branch.svg +++ b/style/icons/branch.svg @@ -1,3 +1,3 @@ - + diff --git a/style/index.css b/style/index.css index 3afaea539..1bf5cc1da 100644 --- a/style/index.css +++ b/style/index.css @@ -7,15 +7,3 @@ @import 'diff-nb.css'; @import 'diff-text.css'; @import 'variables.css'; - -[data-jp-theme-light='true'] .jp-git-icon.jp-git-selected { - filter: grayscale(1) brightness(3); -} - -[data-jp-theme-light='false'] .jp-git-icon { - filter: brightness(1.25); -} - -[data-jp-theme-light='false'] .jp-git-icon.jp-git-selected { - filter: grayscale(1) brightness(3); -} diff --git a/style/variables.css b/style/variables.css index 00b75ae41..9eee22806 100644 --- a/style/variables.css +++ b/style/variables.css @@ -8,6 +8,4 @@ --jp-git-diff-deleted-color: rgba(255, 0, 0, 0.2); --jp-git-diff-output-border-color: rgba(0, 141, 255, 0.7); --jp-git-diff-output-color: rgba(0, 141, 255, 0.3); - - --jp-icon-git-branch: url('./icons/branch.svg'); } diff --git a/tests/GitExtension.spec.tsx b/tests/GitExtension.spec.tsx index 2c9b30458..ac6c8cb5e 100644 --- a/tests/GitExtension.spec.tsx +++ b/tests/GitExtension.spec.tsx @@ -16,11 +16,14 @@ describe('IGitExtension', () => { const fakeRoot = '/path/to/server'; let model: IGitExtension; const docmanager = jest.mock('@jupyterlab/docmanager') as any; - docmanager.findWidget = jest.fn(); let mockResponses: IMockedResponses; beforeEach(async () => { jest.restoreAllMocks(); + docmanager.findWidget = jest.fn(); + const docregistry = { + getFileTypesForPath: jest.fn().mockReturnValue([]) + }; mockResponses = { ...defaultMockedResponses @@ -28,7 +31,7 @@ describe('IGitExtension', () => { mockGit.requestAPI.mockImplementation(mockedRequestAPI(mockResponses)); - model = new GitExtension(fakeRoot, docmanager as any); + model = new GitExtension(fakeRoot, docmanager as any, docregistry as any); }); describe('#pathRepository', () => {