adb devices
- https://developer.android.com/studio/debug/am-screenshot
- https://developer.android.com/studio/debug/am-video
FN=screenshot_$(date +%F_%T).png adb shell screencap -p $FN
adb pull /sdcard/$FN ~/Downloads/
/** | |
* Identify `/[param]/` in route string. | |
* @see https://github.com/vercel/next.js/blob/master/packages/next/next-server/lib/router/utils/is-dynamic.ts#L7 | |
* | |
* @param {route} The value from next/router's `route.asPath` (e.g., `/browse/[listingId]`). | |
* @returns {boolean} True if the route looks like a Next.js dynamic route. | |
*/ | |
const TEST_ROUTE = /\/\[[^/]+?\](?=\/|$)/; | |
export function isDynamicRoute(route) { | |
return TEST_ROUTE.test(route); |
adb devices
FN=screenshot_$(date +%F_%T).png adb shell screencap -p $FN
adb pull /sdcard/$FN ~/Downloads/
const enableCypressDebug = win => { | |
if (process.env.DEBUG === '1') { | |
win.localStorage.setItem('debug', 'cypress:*'); | |
} | |
}; | |
beforeEach(enableCypressDebug); |
if (process.env.NODE_ENV === 'development') { | |
// This helps for nodemon to not be prematurely killed. | |
// See nodemon's docs: https://github.com/remy/nodemon/blob/master/README.md#controlling-shutdown-of-your-script | |
// Adapted from source: https://www.benjiegillam.com/2011/08/node-js-clean-restart-and-faster-development-with-nodemon/ | |
const gracefulShutdown = cb => { | |
setTimeout(() => { | |
process.exit(0); | |
cb(); | |
}, 5000); | |
}; |
const stripTrailingSlashesInUrl = str => str.replace(/\/+$/g, ''); |
function stripHtmlComments(html) { | |
// Remove HTML comments (useful for removing developer-facing comments from production HTML markup). | |
return html.replace(/<!--[\s\S]*?(?:-->)/g, ''); | |
} |
// Just replace `placeholder_name` with the actual key for which you want its value. | |
var placeholderName = decodeURIComponent((window.location.search.match(/[?&]placeholder_name=([^&]+)/)||['', ''])[1]); |
function useMedia(mediaQuery) { | |
const match = () => { | |
if (!window.matchMedia) { | |
return false; | |
} | |
return window.matchMedia(mediaQuery).matches; | |
}; | |
const [value, set] = useState(match); |
@mixin text-crop($line-height: 1.3, $top-adjustment: 0px, $bottom-adjustment: 0px) { | |
// Configured in Step 1 | |
$top-crop: 4; | |
$bottom-crop: 14; | |
$crop-font-size: 36; | |
$crop-line-height: 1.2; | |
// Apply values to calculate em-based margins that work with any font size | |
$dynamic-top-crop: max(($top-crop + ($line-height - $crop-line-height) * ($crop-font-size / 2)), 0) / $crop-font-size; | |
$dynamic-bottom-crop: max(($bottom-crop + ($line-height - $crop-line-height) * ($crop-font-size / 2)), 0) / $crop-font-size; |
import React, { useEffect } from 'react'; | |
import * as bodyScrollLock from 'body-scroll-lock'; | |
const disableBodyScroll = bodyScrollLock.disableBodyScroll; | |
const enableBodyScroll = bodyScrollLock.enableBodyScroll; | |
export default function Page() { | |
const appRef = React.createRef(); | |
useEffect(() => { |