import { ReactElement } from 'react' ; import * as ReactDOM from 'react-dom' ; export function pick < T > ( source , keys ) { if ( keys && typeof keys === 'object' ) { return ( Array . isArray ( keys ) ? keys : Object . keys ( keys ) ) . reduce ( ( copy , key ) => { if ( key in source ) { copy [ key as keyof T ] = source [ key as keyof T ] ; } return copy ; } , { } as Partial < T > ) ; } return { } as Partial < T > ; } export enum OperatingSystems { Windows = 'Windows' , macOS = 'Macintosh' , Linux = 'Linux' , others = 'unknown' } function detectOS ( ) { if ( typeof window !== 'undefined' ) { const { navigator : { userAgent } , } = window ; if ( userAgent . indexOf ( OperatingSystems . Windows ) !== - 1 ) return OperatingSystems . Windows ; else if ( userAgent . indexOf ( OperatingSystems . macOS ) !== - 1 ) return OperatingSystems . macOS ; else if ( userAgent . indexOf ( OperatingSystems . Linux ) !== - 1 ) return OperatingSystems . Linux ; } return OperatingSystems . others ; } export const os = detectOS ( ) ; export function friendlyFormatShortcut ( shortcut ? ) { if ( ! shortcut ) return '' ; if ( os === OperatingSystems . Windows ) { return shortcut . replace ( /meta/ , 'win' ) ; } else if ( os === OperatingSystems . macOS ) { return shortcut . replace ( /meta/ , '⌘' ) . replace ( /ctrl/ , '⌃' ) . replace ( /shift/ , '⇧' ) . replace ( /alt/ , '⌥' ) . toUpperCase ( ) ; } else { return shortcut ; } } /**
 * if item's name matches path, return the depth of the item
 * else return 0
 */ function measureDistance ( item , path ) { const pathString = path . join ( '/' ) ; if ( item . name . startsWith ( pathString + '/' ) ) { return path . length ; } else if ( pathString === item . name || pathString . startsWith ( item . name + '/' ) ) { return item . name . split ( '/' ) . length ; } return 0 ; } export async function traverse < T > ( range = [ ] , conditionAndEffect , getChildren ) { for ( const item of range ) { if ( await conditionAndEffect ( item ) ) { await traverse ( getChildren ( item ) , conditionAndEffect , getChildren ) ; } } } /**
 * look for the first item matches given path under root.content
 */ export async function findNode ( root , path ) { let node ; await traverse ( [ root ] , $node => { if ( path === $node . path ) { node = $node ; return false ; } return path . startsWith ( $node . path ) ; } , node => node . contents || [ ] ) ; return node ; } export function createStyleSheet ( content ) { const style = document . createElement ( 'style' ) ; style . appendChild ( document . createTextNode ( content ) ) ; document . head . appendChild ( style ) ; return style ; } export function setStyleSheetMedia ( style , media ) { style . setAttribute ( 'media' , media ) ; } export function parseURLSearch ( search = window . location . search ) { return new URLSearchParams ( search ) ; } export async function JSONRequest ( url , data , extra = { method : 'post' } ) { return ( await fetch ( url , { mode : 'cors' , cache : 'no-cache' , credentials : 'same-origin' , headers : { 'Content-Type' : 'application/json' , Accept : 'application/json' , } , redirect : 'follow' , referrerPolicy : 'no-referrer' , method : extra . method || 'post' , body : JSON . stringify ( data ) , ... extra , } ) ) . json ( ) ; } export function searchKeyToRegexp ( searchKey ) { if ( ! searchKey ) return null ; try { const flags = hasUpperCase ( searchKey ) ? '' : 'i' ; return new RegExp ( searchKey , flags ) ; } catch ( err ) { return null ; } } export function hasUpperCase ( input ) { return /[A-Z]/ . test ( input ) ; } export async function renderReact ( element ) { return new Promise < Node > ( resolve => { const mount = document . createElement ( 'div' ) ; ReactDOM . render ( element , mount , ( ) => { resolve ( mount . childNodes [ 0 ] ) ; } ) ; } ) ; } export function isValidRegexpSource ( source ) { try { new RegExp ( source ) ; return true ; } catch ( err ) { return false ; } } export function withEffect < Method extends ( ... args : any [ ] ) => any > ( method , effect ) { return ( ... args ) => { const returnValue = method . apply ( null , args ) ; Promise . resolve ( returnValue ) . then ( effect ) ; return returnValue ; } ; } export function run < T > ( fn ) { return fn ( ) ; } export function createPromiseQueue ( ) { let promise ; return { async enter ( ) { let leave ; const current = new Promise < void > ( resolve => ( leave = ( ) => resolve ( ) ) ) ; const lastPromise = promise ; promise = current ! ; if ( lastPromise ) await lastPromise ; return leave ! ; } , } ; } export function isOpenInNewWindowClick ( event ) { return ( ( os === OperatingSystems . macOS && ( event . metaKey || event . shiftKey ) ) || ( os === OperatingSystems . Linux && event . ctrlKey ) || ( os === OperatingSystems . Windows && event . ctrlKey ) ) ; } export function resolveDiffGraphMeta ( additions , deletions , changes ) { const both = additions > 0 && deletions > 0 , overflow = changes > 5 , preserved = both && overflow ? 1 : 0 , g = overflow ? Math . floor ( ( ( 5 - preserved ) * ( additions + 1 ) ) / ( changes + 1 ) ) : additions , r = overflow ? 5 - preserved - g : deletions , w = 5 - g - r ; return { g , r , w } ; }