[improvement] Adds error boundary (#690)
* Add error boundary * Update useStyle.tsx * Update ErrorFallback.tsx
This commit is contained in:
parent
e33edb9cab
commit
e2a6badaef
12 changed files with 280 additions and 124 deletions
|
@ -48,11 +48,11 @@
|
|||
"react-dom": "^16.8 || ^17.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tldraw/intersect": "*",
|
||||
"@tldraw/vec": "*",
|
||||
"@swc-node/jest": "^1.4.3",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@tldraw/intersect": "*",
|
||||
"@tldraw/vec": "*",
|
||||
"@types/node": "^17.0.14",
|
||||
"@types/react": "^17.0.38",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
||||
|
@ -97,4 +97,4 @@
|
|||
}
|
||||
},
|
||||
"gitHead": "4b1137849ad07da36fc8f0f19cb64e7535a79296"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as React from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import {
|
||||
usePreventNavigationCss,
|
||||
useZoomEvents,
|
||||
|
@ -136,5 +136,3 @@ export const Canvas = observer(function _Canvas<
|
|||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const stopPropagation: React.ClipboardEventHandler<HTMLDivElement> = (e) => e.stopPropagation()
|
||||
|
|
|
@ -29,14 +29,24 @@ export function useSelection<T extends TLShape>(
|
|||
if (selectedIds.length === 1) {
|
||||
const id = selectedIds[0]
|
||||
const shape = page.shapes[id]
|
||||
|
||||
if (!shape) {
|
||||
throw Error(`selectedIds is set to the id of a shape that doesn't exist: ${id}`)
|
||||
}
|
||||
|
||||
rotation = shape.rotation || 0
|
||||
isLocked = shape.isLocked || false
|
||||
|
||||
const utils = getShapeUtils(shapeUtils, shape)
|
||||
|
||||
bounds = utils.hideBounds ? undefined : utils.getBounds(shape)
|
||||
} else if (selectedIds.length > 1) {
|
||||
const selectedShapes = selectedIds.map((id) => page.shapes[id])
|
||||
|
||||
rotation = 0
|
||||
|
||||
isLocked = selectedShapes.every((shape) => shape.isLocked)
|
||||
|
||||
bounds = selectedShapes.reduce((acc, shape, i) => {
|
||||
if (i === 0) {
|
||||
return getShapeUtils(shapeUtils, shape).getRotatedBounds(shape)
|
||||
|
@ -48,9 +58,11 @@ export function useSelection<T extends TLShape>(
|
|||
if (bounds) {
|
||||
const [minX, minY] = canvasToScreen([bounds.minX, bounds.minY], pageState.camera)
|
||||
const [maxX, maxY] = canvasToScreen([bounds.maxX, bounds.maxY], pageState.camera)
|
||||
|
||||
isLinked = !!Object.values(page.bindings).find(
|
||||
(binding) => selectedIds.includes(binding.toId) || selectedIds.includes(binding.fromId)
|
||||
)
|
||||
|
||||
rSelectionBounds.current = {
|
||||
minX,
|
||||
minY,
|
||||
|
|
|
@ -138,8 +138,14 @@ export function useShapeTree<T extends TLShape, M extends Record<string, unknown
|
|||
// If the shape's parent is a different shape (e.g. a group),
|
||||
// add the parent to the sets of shapes to render. The parent's
|
||||
// children will all be rendered.
|
||||
shapesIdsToRender.add(shape.parentId)
|
||||
shapesToRender.add(page.shapes[shape.parentId])
|
||||
const parent = page.shapes[shape.parentId]
|
||||
|
||||
if (parent === undefined) {
|
||||
throw Error(`A shape (${shape.id}) has a parent (${shape.parentId}) that does not exist!`)
|
||||
} else {
|
||||
shapesIdsToRender.add(parent.id)
|
||||
shapesToRender.add(parent)
|
||||
}
|
||||
})
|
||||
|
||||
// Call onRenderCountChange callback when number of rendering shapes changes
|
||||
|
@ -163,7 +169,11 @@ export function useShapeTree<T extends TLShape, M extends Record<string, unknown
|
|||
|
||||
const tree: IShapeTreeNode<T, M>[] = []
|
||||
|
||||
shapesToRender.forEach((shape) =>
|
||||
shapesToRender.forEach((shape) => {
|
||||
if (shape === undefined) {
|
||||
throw Error('Rendered shapes included a missing shape')
|
||||
}
|
||||
|
||||
addToShapeTree(
|
||||
shape,
|
||||
tree,
|
||||
|
@ -174,7 +184,7 @@ export function useShapeTree<T extends TLShape, M extends Record<string, unknown
|
|||
false,
|
||||
meta
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
tree.sort((a, b) => a.shape.childIndex - b.shape.childIndex)
|
||||
|
||||
|
|
|
@ -79,36 +79,6 @@ const defaultTheme: TLTheme = {
|
|||
}
|
||||
|
||||
export const TLCSS = css`
|
||||
@font-face {
|
||||
font-family: 'Recursive';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/recursive/v23/8vI-7wMr0mhh-RQChyHEH06TlXhq_gukbYrFMk1QuAIcyEwG_X-dpEfaE5YaERmK-CImKsvxvU-MXGX2fSqasNfUlTGZnI14ZeY.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Recursive';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/recursive/v23/8vI-7wMr0mhh-RQChyHEH06TlXhq_gukbYrFMk1QuAIcyEwG_X-dpEfaE5YaERmK-CImKsvxvU-MXGX2fSqasNfUlTGZnI14ZeY.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Recursive Mono';
|
||||
font-style: normal;
|
||||
font-weight: 420;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/recursive/v23/8vI-7wMr0mhh-RQChyHEH06TlXhq_gukbYrFMk1QuAIcyEwG_X-dpEfaE5YaERmK-CImqvTxvU-MXGX2fSqasNfUlTGZnI14ZeY.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
.tl-container {
|
||||
--tl-zoom: 1;
|
||||
--tl-scale: calc(1 / var(--tl-zoom));
|
||||
|
@ -351,11 +321,6 @@ export const TLCSS = css`
|
|||
.tl-brush.dashed {
|
||||
stroke: none;
|
||||
}
|
||||
.tl-dot {
|
||||
fill: var(--tl-background);
|
||||
stroke: var(--tl-foreground);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.tl-handle {
|
||||
pointer-events: all;
|
||||
cursor: grab;
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"idb-keyval": "^6.1.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"perfect-freehand": "^1.0.16",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"react-hotkey-hook": "^1.0.2",
|
||||
"react-hotkeys-hook": "^3.4.4",
|
||||
"tslib": "^2.3.1",
|
||||
|
@ -105,4 +106,4 @@
|
|||
}
|
||||
},
|
||||
"gitHead": "4b1137849ad07da36fc8f0f19cb64e7535a79296"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import { FocusButton } from '~components/FocusButton'
|
|||
import { TLDR } from '~state/TLDR'
|
||||
import { GRID_SIZE } from '~constants'
|
||||
import { Loading } from '~components/Loading'
|
||||
import { ErrorBoundary } from 'react-error-boundary'
|
||||
import { ErrorFallback } from '~components/ErrorFallback'
|
||||
|
||||
export interface TldrawProps extends TDCallbacks {
|
||||
/**
|
||||
|
@ -415,81 +417,83 @@ const InnerTldraw = React.memo(function InnerTldraw({
|
|||
<Loading />
|
||||
<OneOff focusableRef={rWrapper} autofocus={autofocus} />
|
||||
<ContextMenu>
|
||||
<Renderer
|
||||
id={id}
|
||||
containerRef={rWrapper}
|
||||
shapeUtils={shapeUtils}
|
||||
page={page}
|
||||
pageState={pageState}
|
||||
assets={assets}
|
||||
snapLines={appState.snapLines}
|
||||
grid={GRID_SIZE}
|
||||
users={room?.users}
|
||||
userId={room?.userId}
|
||||
theme={theme}
|
||||
meta={meta}
|
||||
hideBounds={hideBounds}
|
||||
hideHandles={hideHandles}
|
||||
hideResizeHandles={isHideResizeHandlesShape}
|
||||
hideIndicators={hideIndicators}
|
||||
hideBindingHandles={!settings.showBindingHandles}
|
||||
hideCloneHandles={hideCloneHandles}
|
||||
hideRotateHandles={!settings.showRotateHandles}
|
||||
hideGrid={!settings.showGrid}
|
||||
showDashedBrush={showDashedBrush}
|
||||
performanceMode={app.session?.performanceMode}
|
||||
onPinchStart={app.onPinchStart}
|
||||
onPinchEnd={app.onPinchEnd}
|
||||
onPinch={app.onPinch}
|
||||
onPan={app.onPan}
|
||||
onZoom={app.onZoom}
|
||||
onPointerDown={app.onPointerDown}
|
||||
onPointerMove={app.onPointerMove}
|
||||
onPointerUp={app.onPointerUp}
|
||||
onPointCanvas={app.onPointCanvas}
|
||||
onDoubleClickCanvas={app.onDoubleClickCanvas}
|
||||
onRightPointCanvas={app.onRightPointCanvas}
|
||||
onDragCanvas={app.onDragCanvas}
|
||||
onReleaseCanvas={app.onReleaseCanvas}
|
||||
onPointShape={app.onPointShape}
|
||||
onDoubleClickShape={app.onDoubleClickShape}
|
||||
onRightPointShape={app.onRightPointShape}
|
||||
onDragShape={app.onDragShape}
|
||||
onHoverShape={app.onHoverShape}
|
||||
onUnhoverShape={app.onUnhoverShape}
|
||||
onReleaseShape={app.onReleaseShape}
|
||||
onPointBounds={app.onPointBounds}
|
||||
onDoubleClickBounds={app.onDoubleClickBounds}
|
||||
onRightPointBounds={app.onRightPointBounds}
|
||||
onDragBounds={app.onDragBounds}
|
||||
onHoverBounds={app.onHoverBounds}
|
||||
onUnhoverBounds={app.onUnhoverBounds}
|
||||
onReleaseBounds={app.onReleaseBounds}
|
||||
onPointBoundsHandle={app.onPointBoundsHandle}
|
||||
onDoubleClickBoundsHandle={app.onDoubleClickBoundsHandle}
|
||||
onRightPointBoundsHandle={app.onRightPointBoundsHandle}
|
||||
onDragBoundsHandle={app.onDragBoundsHandle}
|
||||
onHoverBoundsHandle={app.onHoverBoundsHandle}
|
||||
onUnhoverBoundsHandle={app.onUnhoverBoundsHandle}
|
||||
onReleaseBoundsHandle={app.onReleaseBoundsHandle}
|
||||
onPointHandle={app.onPointHandle}
|
||||
onDoubleClickHandle={app.onDoubleClickHandle}
|
||||
onRightPointHandle={app.onRightPointHandle}
|
||||
onDragHandle={app.onDragHandle}
|
||||
onHoverHandle={app.onHoverHandle}
|
||||
onUnhoverHandle={app.onUnhoverHandle}
|
||||
onReleaseHandle={app.onReleaseHandle}
|
||||
onError={app.onError}
|
||||
onRenderCountChange={app.onRenderCountChange}
|
||||
onShapeChange={app.onShapeChange}
|
||||
onShapeBlur={app.onShapeBlur}
|
||||
onShapeClone={app.onShapeClone}
|
||||
onBoundsChange={app.updateBounds}
|
||||
onKeyDown={app.onKeyDown}
|
||||
onKeyUp={app.onKeyUp}
|
||||
onDragOver={app.onDragOver}
|
||||
onDrop={app.onDrop}
|
||||
/>
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
<Renderer
|
||||
id={id}
|
||||
containerRef={rWrapper}
|
||||
shapeUtils={shapeUtils}
|
||||
page={page}
|
||||
pageState={pageState}
|
||||
assets={assets}
|
||||
snapLines={appState.snapLines}
|
||||
grid={GRID_SIZE}
|
||||
users={room?.users}
|
||||
userId={room?.userId}
|
||||
theme={theme}
|
||||
meta={meta}
|
||||
hideBounds={hideBounds}
|
||||
hideHandles={hideHandles}
|
||||
hideResizeHandles={isHideResizeHandlesShape}
|
||||
hideIndicators={hideIndicators}
|
||||
hideBindingHandles={!settings.showBindingHandles}
|
||||
hideCloneHandles={hideCloneHandles}
|
||||
hideRotateHandles={!settings.showRotateHandles}
|
||||
hideGrid={!settings.showGrid}
|
||||
showDashedBrush={showDashedBrush}
|
||||
performanceMode={app.session?.performanceMode}
|
||||
onPinchStart={app.onPinchStart}
|
||||
onPinchEnd={app.onPinchEnd}
|
||||
onPinch={app.onPinch}
|
||||
onPan={app.onPan}
|
||||
onZoom={app.onZoom}
|
||||
onPointerDown={app.onPointerDown}
|
||||
onPointerMove={app.onPointerMove}
|
||||
onPointerUp={app.onPointerUp}
|
||||
onPointCanvas={app.onPointCanvas}
|
||||
onDoubleClickCanvas={app.onDoubleClickCanvas}
|
||||
onRightPointCanvas={app.onRightPointCanvas}
|
||||
onDragCanvas={app.onDragCanvas}
|
||||
onReleaseCanvas={app.onReleaseCanvas}
|
||||
onPointShape={app.onPointShape}
|
||||
onDoubleClickShape={app.onDoubleClickShape}
|
||||
onRightPointShape={app.onRightPointShape}
|
||||
onDragShape={app.onDragShape}
|
||||
onHoverShape={app.onHoverShape}
|
||||
onUnhoverShape={app.onUnhoverShape}
|
||||
onReleaseShape={app.onReleaseShape}
|
||||
onPointBounds={app.onPointBounds}
|
||||
onDoubleClickBounds={app.onDoubleClickBounds}
|
||||
onRightPointBounds={app.onRightPointBounds}
|
||||
onDragBounds={app.onDragBounds}
|
||||
onHoverBounds={app.onHoverBounds}
|
||||
onUnhoverBounds={app.onUnhoverBounds}
|
||||
onReleaseBounds={app.onReleaseBounds}
|
||||
onPointBoundsHandle={app.onPointBoundsHandle}
|
||||
onDoubleClickBoundsHandle={app.onDoubleClickBoundsHandle}
|
||||
onRightPointBoundsHandle={app.onRightPointBoundsHandle}
|
||||
onDragBoundsHandle={app.onDragBoundsHandle}
|
||||
onHoverBoundsHandle={app.onHoverBoundsHandle}
|
||||
onUnhoverBoundsHandle={app.onUnhoverBoundsHandle}
|
||||
onReleaseBoundsHandle={app.onReleaseBoundsHandle}
|
||||
onPointHandle={app.onPointHandle}
|
||||
onDoubleClickHandle={app.onDoubleClickHandle}
|
||||
onRightPointHandle={app.onRightPointHandle}
|
||||
onDragHandle={app.onDragHandle}
|
||||
onHoverHandle={app.onHoverHandle}
|
||||
onUnhoverHandle={app.onUnhoverHandle}
|
||||
onReleaseHandle={app.onReleaseHandle}
|
||||
onError={app.onError}
|
||||
onRenderCountChange={app.onRenderCountChange}
|
||||
onShapeChange={app.onShapeChange}
|
||||
onShapeBlur={app.onShapeBlur}
|
||||
onShapeClone={app.onShapeClone}
|
||||
onBoundsChange={app.updateBounds}
|
||||
onKeyDown={app.onKeyDown}
|
||||
onKeyUp={app.onKeyUp}
|
||||
onDragOver={app.onDragOver}
|
||||
onDrop={app.onDrop}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</ContextMenu>
|
||||
{showUI && (
|
||||
<StyledUI>
|
||||
|
|
122
packages/tldraw/src/components/ErrorFallback/ErrorFallback.tsx
Normal file
122
packages/tldraw/src/components/ErrorFallback/ErrorFallback.tsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import * as React from 'react'
|
||||
import { FallbackProps } from 'react-error-boundary'
|
||||
import { Divider } from '~components/Primitives/Divider'
|
||||
import { RowButton } from '~components/Primitives/RowButton'
|
||||
import { useTldrawApp } from '~hooks'
|
||||
import { styled } from '~styles'
|
||||
|
||||
export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
||||
const app = useTldrawApp()
|
||||
|
||||
const refreshPage = () => {
|
||||
window.location.reload()
|
||||
resetErrorBoundary()
|
||||
}
|
||||
|
||||
const copyError = () => {
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.value = error.message
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select()
|
||||
document.execCommand('copy')
|
||||
textarea.remove()
|
||||
}
|
||||
|
||||
const downloadBackup = () => {
|
||||
app.saveProjectAs()
|
||||
}
|
||||
|
||||
const resetDocument = () => {
|
||||
app.resetDocument()
|
||||
resetErrorBoundary()
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<InnerContainer>
|
||||
<div>We've encountered an error!</div>
|
||||
<pre>
|
||||
<code>{error.message}</code>
|
||||
</pre>
|
||||
<Buttons>
|
||||
<RowButton onClick={copyError}>Copy Error</RowButton>
|
||||
<RowButton onClick={refreshPage}>Refresh Page</RowButton>
|
||||
</Buttons>
|
||||
<Divider />
|
||||
<p>
|
||||
Keep getting this error?{' '}
|
||||
<a onClick={downloadBackup} title="Download your project">
|
||||
Download your project
|
||||
</a>{' '}
|
||||
as a backup and then{' '}
|
||||
<a onClick={resetDocument} title="Reset the document">
|
||||
reset the document
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</InnerContainer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled('div', {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '$canvas',
|
||||
})
|
||||
|
||||
const InnerContainer = styled('div', {
|
||||
backgroundColor: '$panel',
|
||||
border: '1px solid $panelContrast',
|
||||
padding: '$5',
|
||||
borderRadius: 8,
|
||||
boxShadow: '$panel',
|
||||
maxWidth: 320,
|
||||
color: '$text',
|
||||
fontFamily: '$ui',
|
||||
fontSize: '$2',
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '$3',
|
||||
'& > pre': {
|
||||
marginTop: '$3',
|
||||
marginBottom: '$3',
|
||||
textAlign: 'left',
|
||||
whiteSpace: 'pre-wrap',
|
||||
backgroundColor: '$hover',
|
||||
padding: '$4',
|
||||
borderRadius: '$2',
|
||||
fontFamily: '"Menlo", "Monaco", monospace',
|
||||
fontWeight: 500,
|
||||
},
|
||||
'& p': {
|
||||
fontFamily: '$body',
|
||||
lineHeight: 1.7,
|
||||
padding: '$5',
|
||||
margin: 0,
|
||||
},
|
||||
'& a': {
|
||||
color: '$text',
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
'& hr': {
|
||||
marginLeft: '-$5',
|
||||
marginRight: '-$5',
|
||||
},
|
||||
})
|
||||
|
||||
const Buttons = styled('div', {
|
||||
display: 'flex',
|
||||
'& > button > div': {
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
},
|
||||
})
|
1
packages/tldraw/src/components/ErrorFallback/index.ts
Normal file
1
packages/tldraw/src/components/ErrorFallback/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './ErrorFallback'
|
|
@ -3,9 +3,43 @@ import * as React from 'react'
|
|||
const styles = new Map<string, HTMLStyleElement>()
|
||||
|
||||
const UID = `tldraw-fonts`
|
||||
const WEBFONT_URL =
|
||||
'https://fonts.googleapis.com/css2?family=Caveat+Brush&family=Source+Code+Pro&family=Source+Sans+Pro&family=Crimson+Pro&display=block'
|
||||
const CSS = `@import url('${WEBFONT_URL}');`
|
||||
|
||||
const CSS = `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Caveat+Brush&family=Source+Code+Pro&family=Source+Sans+Pro&family=Crimson+Pro&display=block');
|
||||
|
||||
@font-face {
|
||||
font-family: 'Recursive';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/recursive/v23/8vI-7wMr0mhh-RQChyHEH06TlXhq_gukbYrFMk1QuAIcyEwG_X-dpEfaE5YaERmK-CImKsvxvU-MXGX2fSqasNfUlTGZnI14ZeY.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Recursive';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/recursive/v23/8vI-7wMr0mhh-RQChyHEH06TlXhq_gukbYrFMk1QuAIcyEwG_X-dpEfaE5YaERmK-CImKsvxvU-MXGX2fSqasNfUlTGZnI14ZeY.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Recursive Mono';
|
||||
font-style: normal;
|
||||
font-weight: 420;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/recursive/v23/8vI-7wMr0mhh-RQChyHEH06TlXhq_gukbYrFMk1QuAIcyEwG_X-dpEfaE5YaERmK-CImqvTxvU-MXGX2fSqasNfUlTGZnI14ZeY.woff2)
|
||||
format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
|
||||
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
`
|
||||
|
||||
export function useStylesheet() {
|
||||
React.useLayoutEffect(() => {
|
||||
|
|
|
@ -21,6 +21,7 @@ const { styled, createTheme } = createStitches({
|
|||
tooltip: '#1d1d1d',
|
||||
tooltipContrast: '#ffffff',
|
||||
warn: 'rgba(255, 100, 100, 1)',
|
||||
canvas: 'rgb(248, 249, 250)',
|
||||
},
|
||||
shadows: {
|
||||
2: '0px 1px 1px rgba(0, 0, 0, 0.14)',
|
||||
|
@ -110,6 +111,7 @@ export const dark = createTheme({
|
|||
text: '#f8f9fa',
|
||||
tooltip: '#1d1d1d',
|
||||
tooltipContrast: '#ffffff',
|
||||
canvas: '#212529',
|
||||
},
|
||||
shadows: {
|
||||
2: '0px 1px 1px rgba(0, 0, 0, 0.24)',
|
||||
|
|
|
@ -9385,6 +9385,13 @@ rc@^1.2.7, rc@^1.2.8:
|
|||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-error-boundary@^3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
|
||||
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
|
||||
react-feather@^2.0.9:
|
||||
version "2.0.9"
|
||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.9.tgz#6e42072130d2fa9a09d4476b0e61b0ed17814480"
|
||||
|
|
Loading…
Reference in a new issue