Document and shapes
This commit is contained in:
parent
e7a52dd70f
commit
f38481efee
18 changed files with 292 additions and 54 deletions
|
@ -1,21 +1,20 @@
|
||||||
import styled from "styles"
|
import styled from "styles"
|
||||||
import { useRef } from "react"
|
import { useRef } from "react"
|
||||||
import useZoomEvents from "hooks/useZoomEvents"
|
import useZoomEvents from "hooks/useZoomEvents"
|
||||||
import useZoomPanEffect from "hooks/useZoomPanEffect"
|
import useCamera from "hooks/useCamera"
|
||||||
|
import Page from "./page"
|
||||||
|
|
||||||
export default function Canvas() {
|
export default function Canvas() {
|
||||||
const rCanvas = useRef<SVGSVGElement>(null)
|
const rCanvas = useRef<SVGSVGElement>(null)
|
||||||
const rGroup = useRef<SVGGElement>(null)
|
const rGroup = useRef<SVGGElement>(null)
|
||||||
const events = useZoomEvents(rCanvas)
|
const events = useZoomEvents(rCanvas)
|
||||||
|
|
||||||
useZoomPanEffect(rGroup)
|
useCamera(rGroup)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainSVG ref={rCanvas} {...events}>
|
<MainSVG ref={rCanvas} {...events}>
|
||||||
<MainGroup ref={rGroup}>
|
<MainGroup ref={rGroup}>
|
||||||
<circle cx={100} cy={100} r={50} />
|
<Page />
|
||||||
<circle cx={500} cy={500} r={200} />
|
|
||||||
<circle cx={200} cy={800} r={100} />
|
|
||||||
</MainGroup>
|
</MainGroup>
|
||||||
</MainSVG>
|
</MainSVG>
|
||||||
)
|
)
|
||||||
|
|
24
components/canvas/page.tsx
Normal file
24
components/canvas/page.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { useSelector } from "state"
|
||||||
|
import { deepCompareArrays } from "utils/utils"
|
||||||
|
import Shape from "./shape"
|
||||||
|
|
||||||
|
/*
|
||||||
|
On each state change, compare node ids of all shapes
|
||||||
|
on the current page. Kind of expensive but only happens
|
||||||
|
here; and still cheaper than any other pattern I've found.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const currentPageShapeIds = useSelector((state) => {
|
||||||
|
const { currentPageId, document } = state.data
|
||||||
|
return Object.keys(document.pages[currentPageId].shapes)
|
||||||
|
}, deepCompareArrays)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{currentPageShapeIds.map((shapeId) => (
|
||||||
|
<Shape key={shapeId} id={shapeId} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
29
components/canvas/shape.tsx
Normal file
29
components/canvas/shape.tsx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { memo } from "react"
|
||||||
|
import { useSelector } from "state"
|
||||||
|
import { ShapeType } from "types"
|
||||||
|
import Circle from "./shapes/circle"
|
||||||
|
import Rectangle from "./shapes/rectangle"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets the shape from the current page's shapes, using the
|
||||||
|
provided ID. Depending on the shape's type, return the
|
||||||
|
component for that type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Shape({ id }: { id: string }) {
|
||||||
|
const shape = useSelector((state) => {
|
||||||
|
const { currentPageId, document } = state.data
|
||||||
|
return document.pages[currentPageId].shapes[id]
|
||||||
|
})
|
||||||
|
|
||||||
|
switch (shape.type) {
|
||||||
|
case ShapeType.Circle:
|
||||||
|
return <Circle {...shape} />
|
||||||
|
case ShapeType.Rectangle:
|
||||||
|
return <Rectangle {...shape} />
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Shape)
|
5
components/canvas/shapes/circle.tsx
Normal file
5
components/canvas/shapes/circle.tsx
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { CircleShape } from "types"
|
||||||
|
|
||||||
|
export default function Circle({ point, radius }: CircleShape) {
|
||||||
|
return <circle cx={point[0]} cy={point[1]} r={radius} fill="black" />
|
||||||
|
}
|
13
components/canvas/shapes/rectangle.tsx
Normal file
13
components/canvas/shapes/rectangle.tsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { RectangleShape } from "types"
|
||||||
|
|
||||||
|
export default function Rectangle({ point, size }: RectangleShape) {
|
||||||
|
return (
|
||||||
|
<rect
|
||||||
|
x={point[0]}
|
||||||
|
y={point[1]}
|
||||||
|
width={size[0]}
|
||||||
|
height={size[1]}
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ const StatusBarContainer = styled("div", {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
backgroundColor: "white",
|
backgroundColor: "white",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
|
fontSize: "$1",
|
||||||
padding: "0 16px",
|
padding: "0 16px",
|
||||||
zIndex: 200,
|
zIndex: 200,
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,9 +6,7 @@ import state from "state"
|
||||||
* the SVG group to reflect the correct zoom and pan.
|
* the SVG group to reflect the correct zoom and pan.
|
||||||
* @param ref
|
* @param ref
|
||||||
*/
|
*/
|
||||||
export default function useZoomPanEffect(
|
export default function useCamera(ref: React.MutableRefObject<SVGGElement>) {
|
||||||
ref: React.MutableRefObject<SVGGElement>
|
|
||||||
) {
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let { camera } = state.data
|
let { camera } = state.data
|
||||||
|
|
||||||
|
@ -19,7 +17,6 @@ export default function useZoomPanEffect(
|
||||||
const { point, zoom } = data.camera
|
const { point, zoom } = data.camera
|
||||||
|
|
||||||
if (point !== camera.point || zoom !== camera.zoom) {
|
if (point !== camera.point || zoom !== camera.zoom) {
|
||||||
console.log("changed!")
|
|
||||||
g.setAttribute(
|
g.setAttribute(
|
||||||
"transform",
|
"transform",
|
||||||
`scale(${zoom}) translate(${point[0]} ${point[1]})`
|
`scale(${zoom}) translate(${point[0]} ${point[1]})`
|
|
@ -2,6 +2,11 @@ import React, { useEffect, useRef } from "react"
|
||||||
import state from "state"
|
import state from "state"
|
||||||
import * as vec from "utils/vec"
|
import * as vec from "utils/vec"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
|
||||||
|
* @param ref
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export default function useZoomEvents(
|
export default function useZoomEvents(
|
||||||
ref: React.MutableRefObject<SVGSVGElement>
|
ref: React.MutableRefObject<SVGSVGElement>
|
||||||
) {
|
) {
|
||||||
|
@ -30,17 +35,19 @@ export default function useZoomEvents(
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchMove(e: TouchEvent) {
|
function handleTouchMove(e: TouchEvent) {
|
||||||
if (e.ctrlKey) {
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
|
||||||
|
|
||||||
if (e.touches.length === 2) {
|
if (e.touches.length === 2) {
|
||||||
const { clientX: x0, clientY: y0 } = e.touches[0]
|
const { clientX: x0, clientY: y0 } = e.touches[0]
|
||||||
const { clientX: x1, clientY: y1 } = e.touches[1]
|
const { clientX: x1, clientY: y1 } = e.touches[1]
|
||||||
|
|
||||||
const dist = vec.dist([x0, y0], [x1, y1])
|
const dist = vec.dist([x0, y0], [x1, y1])
|
||||||
|
const point = vec.med([x0, y0], [x1, y1])
|
||||||
|
|
||||||
state.send("WHEELED", { delta: [0, dist - rTouchDist.current] })
|
state.send("WHEELED", {
|
||||||
|
delta: dist - rTouchDist.current,
|
||||||
|
point,
|
||||||
|
})
|
||||||
|
|
||||||
rTouchDist.current = dist
|
rTouchDist.current = dist
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@state-designer/react": "^1.7.1",
|
"@state-designer/react": "^1.7.1",
|
||||||
"@stitches/react": "^0.1.9",
|
"@stitches/react": "^0.1.9",
|
||||||
|
"@types/uuid": "^8.3.0",
|
||||||
"next": "10.2.0",
|
"next": "10.2.0",
|
||||||
"perfect-freehand": "^0.4.7",
|
"perfect-freehand": "^0.4.7",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2"
|
"react-dom": "17.0.2",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/next": "^9.0.0",
|
"@types/next": "^9.0.0",
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
import Document, {
|
import NextDocument, { Html, Head, Main, NextScript } from "next/document"
|
||||||
DocumentContext,
|
import { dark, getCssString } from "styles"
|
||||||
Html,
|
|
||||||
Head,
|
|
||||||
Main,
|
|
||||||
NextScript,
|
|
||||||
} from "next/document"
|
|
||||||
import { dark } from "styles"
|
|
||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends NextDocument {
|
||||||
static async getInitialProps(ctx: DocumentContext) {
|
static async getInitialProps(ctx) {
|
||||||
const initialProps = await Document.getInitialProps(ctx)
|
try {
|
||||||
return { ...initialProps }
|
const initialProps = await NextDocument.getInitialProps(ctx)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...initialProps,
|
||||||
|
styles: (
|
||||||
|
<>
|
||||||
|
{initialProps.styles}
|
||||||
|
<style
|
||||||
|
id="stitches"
|
||||||
|
dangerouslySetInnerHTML={{ __html: getCssString() }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message)
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html lang="en">
|
||||||
<Head />
|
<Head />
|
||||||
<body className={dark}>
|
<body className={dark}>
|
||||||
<Main />
|
<Main />
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import Head from "next/head"
|
|
||||||
import Image from "next/image"
|
|
||||||
import Editor from "components/editor"
|
import Editor from "components/editor"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|
|
@ -1,13 +1,56 @@
|
||||||
import { createSelectorHook, createState } from "@state-designer/react"
|
import { createSelectorHook, createState } from "@state-designer/react"
|
||||||
import * as vec from "utils/vec"
|
|
||||||
import { clamp, screenToWorld } from "utils/utils"
|
import { clamp, screenToWorld } from "utils/utils"
|
||||||
import { IData } from "types"
|
import * as vec from "utils/vec"
|
||||||
|
import { Data, ShapeType } from "types"
|
||||||
|
|
||||||
const initialData: IData = {
|
const initialData: Data = {
|
||||||
camera: {
|
camera: {
|
||||||
point: [0, 0],
|
point: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
},
|
},
|
||||||
|
currentPageId: "page0",
|
||||||
|
document: {
|
||||||
|
pages: {
|
||||||
|
page0: {
|
||||||
|
id: "page0",
|
||||||
|
type: "page",
|
||||||
|
name: "Page 0",
|
||||||
|
childIndex: 0,
|
||||||
|
shapes: {
|
||||||
|
shape0: {
|
||||||
|
id: "shape0",
|
||||||
|
type: ShapeType.Circle,
|
||||||
|
name: "Shape 0",
|
||||||
|
parentId: "page0",
|
||||||
|
childIndex: 1,
|
||||||
|
point: [100, 100],
|
||||||
|
radius: 50,
|
||||||
|
rotation: 0,
|
||||||
|
},
|
||||||
|
shape1: {
|
||||||
|
id: "shape1",
|
||||||
|
type: ShapeType.Rectangle,
|
||||||
|
name: "Shape 1",
|
||||||
|
parentId: "page0",
|
||||||
|
childIndex: 1,
|
||||||
|
point: [300, 300],
|
||||||
|
size: [200, 200],
|
||||||
|
rotation: 0,
|
||||||
|
},
|
||||||
|
shape2: {
|
||||||
|
id: "shape2",
|
||||||
|
type: ShapeType.Circle,
|
||||||
|
name: "Shape 2",
|
||||||
|
parentId: "page0",
|
||||||
|
childIndex: 2,
|
||||||
|
point: [200, 800],
|
||||||
|
radius: 25,
|
||||||
|
rotation: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = createState({
|
const state = createState({
|
||||||
|
|
|
@ -1,10 +1 @@
|
||||||
* {
|
@import url("https://fonts.googleapis.com/css2?family=Recursive:wght@500;700&display=swap");
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overscroll-behavior: none;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import styled, { css, globalStyles, light, dark } from "./stitches.config"
|
import styled, {
|
||||||
|
css,
|
||||||
|
getCssString,
|
||||||
|
globalStyles,
|
||||||
|
light,
|
||||||
|
dark,
|
||||||
|
} from "./stitches.config"
|
||||||
|
|
||||||
export default styled
|
export default styled
|
||||||
export { css, globalStyles, light, dark }
|
export { css, getCssString, globalStyles, light, dark }
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
import { createCss, global } from "@stitches/react"
|
import { createCss, defaultThemeMap } from "@stitches/react"
|
||||||
|
|
||||||
const { styled, css, theme } = createCss({
|
const { styled, global, css, theme, getCssString } = createCss({
|
||||||
|
themeMap: {
|
||||||
|
...defaultThemeMap,
|
||||||
|
},
|
||||||
theme: {
|
theme: {
|
||||||
colors: {},
|
colors: {},
|
||||||
space: {},
|
space: {},
|
||||||
fontSizes: {},
|
fontSizes: {
|
||||||
fonts: {},
|
0: "10px",
|
||||||
|
1: "12px",
|
||||||
|
2: "13px",
|
||||||
|
3: "16px",
|
||||||
|
4: "18px",
|
||||||
|
},
|
||||||
|
fonts: {
|
||||||
|
ui: `"Recursive", system-ui, sans-serif`,
|
||||||
|
},
|
||||||
fontWeights: {},
|
fontWeights: {},
|
||||||
lineHeights: {},
|
lineHeights: {},
|
||||||
letterSpacings: {},
|
letterSpacings: {},
|
||||||
|
@ -26,12 +37,14 @@ const dark = theme({})
|
||||||
const globalStyles = global({
|
const globalStyles = global({
|
||||||
"*": { boxSizing: "border-box" },
|
"*": { boxSizing: "border-box" },
|
||||||
"html, body": {
|
"html, body": {
|
||||||
padding: "0",
|
padding: "0px",
|
||||||
margin: "0",
|
margin: "0px",
|
||||||
overscrollBehavior: "none",
|
overscrollBehavior: "none",
|
||||||
|
fontFamily: "$ui",
|
||||||
|
fontSize: "$2",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default styled
|
export default styled
|
||||||
|
|
||||||
export { css, globalStyles, light, dark }
|
export { css, getCssString, globalStyles, light, dark }
|
||||||
|
|
91
types.ts
91
types.ts
|
@ -1,6 +1,95 @@
|
||||||
export interface IData {
|
export interface Data {
|
||||||
camera: {
|
camera: {
|
||||||
point: number[]
|
point: number[]
|
||||||
zoom: number
|
zoom: number
|
||||||
}
|
}
|
||||||
|
currentPageId: string
|
||||||
|
selectedIds: string[]
|
||||||
|
pointedId: string
|
||||||
|
document: {
|
||||||
|
pages: Record<string, Page>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Page {
|
||||||
|
id: string
|
||||||
|
type: "page"
|
||||||
|
childIndex: number
|
||||||
|
name: string
|
||||||
|
shapes: Record<string, Shape>
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ShapeType {
|
||||||
|
Circle = "circle",
|
||||||
|
Ellipse = "ellipse",
|
||||||
|
Square = "square",
|
||||||
|
Rectangle = "rectangle",
|
||||||
|
Line = "line",
|
||||||
|
LineSegment = "lineSegment",
|
||||||
|
Dot = "dot",
|
||||||
|
Ray = "ray",
|
||||||
|
Glob = "glob",
|
||||||
|
Spline = "spline",
|
||||||
|
Cubic = "cubic",
|
||||||
|
Conic = "conic",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseShape {
|
||||||
|
id: string
|
||||||
|
type: ShapeType
|
||||||
|
parentId: string
|
||||||
|
childIndex: number
|
||||||
|
name: string
|
||||||
|
rotation: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DotShape extends BaseShape {
|
||||||
|
type: ShapeType.Dot
|
||||||
|
point: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CircleShape extends BaseShape {
|
||||||
|
type: ShapeType.Circle
|
||||||
|
point: number[]
|
||||||
|
radius: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EllipseShape extends BaseShape {
|
||||||
|
type: ShapeType.Ellipse
|
||||||
|
point: number[]
|
||||||
|
radiusX: number
|
||||||
|
radiusY: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LineShape extends BaseShape {
|
||||||
|
type: ShapeType.Line
|
||||||
|
point: number[]
|
||||||
|
vector: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RayShape extends BaseShape {
|
||||||
|
type: ShapeType.Ray
|
||||||
|
point: number[]
|
||||||
|
vector: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LineSegmentShape extends BaseShape {
|
||||||
|
type: ShapeType.LineSegment
|
||||||
|
start: number[]
|
||||||
|
end: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RectangleShape extends BaseShape {
|
||||||
|
type: ShapeType.Rectangle
|
||||||
|
point: number[]
|
||||||
|
size: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Shape =
|
||||||
|
| CircleShape
|
||||||
|
| EllipseShape
|
||||||
|
| DotShape
|
||||||
|
| LineShape
|
||||||
|
| RayShape
|
||||||
|
| LineSegmentShape
|
||||||
|
| RectangleShape
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { IData } from "types"
|
import { Data } from "types"
|
||||||
import * as svg from "./svg"
|
import * as svg from "./svg"
|
||||||
import * as vec from "./vec"
|
import * as vec from "./vec"
|
||||||
|
|
||||||
export function screenToWorld(point: number[], data: IData) {
|
export function screenToWorld(point: number[], data: Data) {
|
||||||
return vec.add(vec.div(point, data.camera.zoom), data.camera.point)
|
return vec.add(vec.div(point, data.camera.zoom), data.camera.point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -1440,6 +1440,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||||
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
||||||
|
|
||||||
|
"@types/uuid@^8.3.0":
|
||||||
|
version "8.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
|
||||||
|
integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
|
||||||
|
|
||||||
"@types/yargs-parser@*":
|
"@types/yargs-parser@*":
|
||||||
version "20.2.0"
|
version "20.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
|
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
|
||||||
|
@ -7263,6 +7268,11 @@ uuid@^3.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||||
|
|
||||||
|
uuid@^8.3.2:
|
||||||
|
version "8.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
v8-compile-cache@^2.0.3:
|
v8-compile-cache@^2.0.3:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||||
|
|
Loading…
Reference in a new issue