From 728c7787191c6c1c67e461e7160db14afe7b5f37 Mon Sep 17 00:00:00 2001 From: Tom Lienard Date: Sun, 1 Sep 2024 08:23:31 +0100 Subject: [PATCH] refactor: design & cleanup --- .gitignore | 1 + apps/dashboard/package.json | 2 +- apps/dashboard/src/app/api/fonts/route.tsx | 1 - .../src/components/RightPanel/AddFont.tsx | 97 ------------ .../src/components/RightPanel/FontSection.tsx | 50 +++--- .../components/RightPanel/FontSelector.tsx | 83 ++++++++++ apps/dashboard/src/lib/fonts.ts | 52 +++---- apps/dashboard/src/lib/hooks/useDebounce.ts | 18 +++ apps/dashboard/src/stores/fontsStore.ts | 9 +- pnpm-lock.yaml | 145 +----------------- 10 files changed, 164 insertions(+), 294 deletions(-) delete mode 100644 apps/dashboard/src/components/RightPanel/AddFont.tsx create mode 100644 apps/dashboard/src/components/RightPanel/FontSelector.tsx create mode 100644 apps/dashboard/src/lib/hooks/useDebounce.ts diff --git a/.gitignore b/.gitignore index 0304a48..5323b20 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ db.sqlite* .DS_Store *.log .env +.env* *.tsbuildinfo diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index fd2f395..2d8dd86 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -26,7 +26,7 @@ "@vercel/speed-insights": "^1.0.10", "clsx": "^2.1.1", "fuse.js": "^7.0.0", - "next": "14.2.3", + "next": "14.2.7", "next-themes": "^0.3.0", "react": "^18", "react-colorful": "^5.6.1", diff --git a/apps/dashboard/src/app/api/fonts/route.tsx b/apps/dashboard/src/app/api/fonts/route.tsx index 061dcfe..3049406 100644 --- a/apps/dashboard/src/app/api/fonts/route.tsx +++ b/apps/dashboard/src/app/api/fonts/route.tsx @@ -1,7 +1,6 @@ import { getFontData } from "../../../lib/fonts"; export const dynamic = "force-static"; -export const runtime = "nodejs"; export async function GET() { return Response.json(await getFontData()); diff --git a/apps/dashboard/src/components/RightPanel/AddFont.tsx b/apps/dashboard/src/components/RightPanel/AddFont.tsx deleted file mode 100644 index 74a4428..0000000 --- a/apps/dashboard/src/components/RightPanel/AddFont.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Popover, Button, TextField, Flex, Badge } from "@radix-ui/themes"; -import Fuse from "fuse.js"; -import { useFontsStore } from "../../stores/fontsStore"; -import { useElementsStore } from "../../stores/elementsStore"; -import { useEffect, useMemo, useState } from "react"; -import { FontPreview } from "../FontPreview"; -import { OGElement } from "../../lib/types"; - -export function AddFont({ - selectedElement, -}: { - selectedElement: OGElement & { tag: "p" | "span" }; -}) { - const { allFonts, installedFonts, installFont } = useFontsStore(); - - const updateElement = useElementsStore((state) => state.updateElement); - - const fuse = useMemo( - () => new Fuse(allFonts, { keys: ["name"] }), - [allFonts], - ); - const [search, setSearch] = useState(""); - const [open, setOpen] = useState(false); - const debouncedSearch = useDebounce(search, 200); - - const searchedFonts = fuse - .search(debouncedSearch) - .slice(0, 5) - .map(({ item }) => item.name); - - return ( - - - - - - - { - setSearch(e.target.value); - }} - /> - - {searchedFonts.map((font) => ( - - ))} - - - - ); -} - -// source: https://github.com/uidotdev/usehooks/blob/90fbbb4cc085e74e50c36a62a5759a40c62bb98e/index.js#L239 -function useDebounce(value: T, delay: number) { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect(() => { - const handler = setTimeout(() => { - setDebouncedValue(value); - }, delay); - - return () => { - clearTimeout(handler); - }; - }, [value, delay]); - - return debouncedValue; -} diff --git a/apps/dashboard/src/components/RightPanel/FontSection.tsx b/apps/dashboard/src/components/RightPanel/FontSection.tsx index b21178c..f42009f 100644 --- a/apps/dashboard/src/components/RightPanel/FontSection.tsx +++ b/apps/dashboard/src/components/RightPanel/FontSection.tsx @@ -7,7 +7,7 @@ import { useElementsStore } from "../../stores/elementsStore"; import { ColorPicker } from "../ColorPicker"; import { FontPreview } from "../FontPreview"; import { useFontsStore } from "../../stores/fontsStore"; -import { AddFont } from "./AddFont"; +import { FontSelector } from "./FontSelector"; const SPACES_REGEX = /\s+/g; @@ -28,10 +28,8 @@ export function FontSection({ selectedElement }: FontSectionProps) { Font { - const font = value; - const weights = allFonts.find((f) => f.name === font)?.weights; - + onValueChange={(font) => { + const weights = allFonts.find(({ name }) => name === font)?.weights; if (!installedFonts.has(font)) { installFont(font); } @@ -55,11 +53,28 @@ export function FontSection({ selectedElement }: FontSectionProps) { ))} - - + - + { + updateElement({ + ...selectedElement, + fontSize: event.target.valueAsNumber, + }); + }} + type="number" + value={selectedElement.fontSize} + variant="soft" + > + + + + + + px + { updateElement({ @@ -81,25 +96,6 @@ export function FontSection({ selectedElement }: FontSectionProps) { ))} - { - updateElement({ - ...selectedElement, - fontSize: event.target.valueAsNumber, - }); - }} - type="number" - value={selectedElement.fontSize} - variant="soft" - > - - - - - - px - { updateElement({ diff --git a/apps/dashboard/src/components/RightPanel/FontSelector.tsx b/apps/dashboard/src/components/RightPanel/FontSelector.tsx new file mode 100644 index 0000000..f4fc8bc --- /dev/null +++ b/apps/dashboard/src/components/RightPanel/FontSelector.tsx @@ -0,0 +1,83 @@ +import { Popover, Button, TextField, Flex, Badge } from "@radix-ui/themes"; +import Fuse from "fuse.js"; +import { useMemo, useState } from "react"; +import { useFontsStore } from "../../stores/fontsStore"; +import { useElementsStore } from "../../stores/elementsStore"; +import { FontPreview } from "../FontPreview"; +import type { OGElement } from "../../lib/types"; +import { useDebounce } from "../../lib/hooks/useDebounce"; +import { DEFAULT_FONTS } from "../../lib/fonts"; + +interface FontSelectorProps { + selectedElement: OGElement & { tag: "p" | "span" }; +} + +export function FontSelector({ selectedElement }: FontSelectorProps) { + const { allFonts, installedFonts, installFont } = useFontsStore(); + const updateElement = useElementsStore((state) => state.updateElement); + const fuse = useMemo( + () => new Fuse(allFonts, { keys: ["name"] }), + [allFonts], + ); + const [search, setSearch] = useState(""); + const [isOpen, setIsOpen] = useState(false); + const debouncedSearch = useDebounce(search, 200); + + const searchedFonts = fuse + .search(debouncedSearch) + .slice(0, 8) + .map(({ item }) => item.name); + + return ( + + + + + + + { + setSearch(event.target.value); + }} + /> + + {searchedFonts.map((font) => ( + + ))} + + + + + ); +} diff --git a/apps/dashboard/src/lib/fonts.ts b/apps/dashboard/src/lib/fonts.ts index 35aaad1..787284e 100644 --- a/apps/dashboard/src/lib/fonts.ts +++ b/apps/dashboard/src/lib/fonts.ts @@ -1,5 +1,21 @@ import type { OGElement } from "./types"; +export interface Font { + name: string; + weights: number[]; +} + +export interface FontData { + name: string; + data: ArrayBuffer; + weight: number; +} + +const fontsCache = new Map(); + +/** + * Default fonts that are available in the editor. + */ export const DEFAULT_FONTS = [ "Roboto", "Open Sans", @@ -14,7 +30,8 @@ export const DEFAULT_FONTS = [ ]; /** - * Adds the font stylesheet to the document body + * Adds the font stylesheet to the document body. + * * The font is loaded asynchronously, so it may not be available immediately * and the caller should make sure to wait for the font to be loaded before * using it. @@ -44,16 +61,8 @@ export function maybeLoadFont(font: string, weight: number) { document.head.appendChild(style); } -export interface FontData { - name: string; - data: ArrayBuffer; - weight: number; -} - -const fontsCache = new Map(); - /** - * Load all fonts used in the given elements from Bunny Fonts. The fonts are + * Load all fonts used in the given elements from sourcefonts. The fonts are * returned as an `ArrayBuffer`, along with the font name and weight. */ export async function loadFonts(elements: OGElement[]): Promise { @@ -69,9 +78,6 @@ export async function loadFonts(elements: OGElement[]): Promise { return fontCache; } - if (element.tag !== "p" && element.tag !== "span") - throw new Error("unreachable!"); - const data = await fetch( getFontURL(element.fontFamily, element.fontWeight), ).then((response) => response.arrayBuffer()); @@ -79,7 +85,6 @@ export async function loadFonts(elements: OGElement[]): Promise { const fontData: FontData = { name: element.fontFamily, data, - weight: element.fontWeight, }; @@ -89,26 +94,21 @@ export async function loadFonts(elements: OGElement[]): Promise { ); } -export async function getFontData() { +/** + * Get a list of all available fonts from Fontsource. + */ +export async function getFontData(): Promise { interface FontsourceFont { - id: string; family: string; - subsets: string[]; weights: number[]; styles: string[]; defSubset: string; - variable: boolean; - lastModified: Date; - category: string; - license: string; - type: string; } - const res = await fetch("https://api.fontsource.org/v1/fonts", { + const response = await fetch("https://api.fontsource.org/v1/fonts", { cache: "no-store", }); - - const data = (await res.json()) as FontsourceFont[]; + const data = (await response.json()) as FontsourceFont[]; return data .filter(({ styles }) => styles.includes("normal")) @@ -119,8 +119,6 @@ export async function getFontData() { })); } -export type Font = Awaited>[number]; - export function getFontURL(fontName: string, weight: number) { const fontID = fontName.toLowerCase().replaceAll(" ", "-"); return `https://cdn.jsdelivr.net/fontsource/fonts/${fontID}@latest/latin-${weight}-normal.woff`; diff --git a/apps/dashboard/src/lib/hooks/useDebounce.ts b/apps/dashboard/src/lib/hooks/useDebounce.ts new file mode 100644 index 0000000..6960007 --- /dev/null +++ b/apps/dashboard/src/lib/hooks/useDebounce.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; + +// source: https://github.com/uidotdev/usehooks/blob/90fbbb4cc085e74e50c36a62a5759a40c62bb98e/index.js#L239 +export function useDebounce(value: T, delayMs: number) { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delayMs); + + return () => { + clearTimeout(handler); + }; + }, [value, delayMs]); + + return debouncedValue; +} diff --git a/apps/dashboard/src/stores/fontsStore.ts b/apps/dashboard/src/stores/fontsStore.ts index a85fed9..042d40c 100644 --- a/apps/dashboard/src/stores/fontsStore.ts +++ b/apps/dashboard/src/stores/fontsStore.ts @@ -3,17 +3,19 @@ import { DEFAULT_FONTS, type Font } from "../lib/fonts"; interface FontsState { installedFonts: Set; - /** install the font based on its name */ installFont: (font: string) => void; allFonts: Font[]; } +// If we are in a browser, fetch the available fonts from the API if (typeof window !== "undefined") { void fetch("/api/fonts") - .then((res) => res.json()) + .then((response) => response.json()) .then((fonts) => { useFontsStore.getState().allFonts = fonts as Font[]; - }); + }) + // eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable, no-console -- the error is logged + .catch(console.error); } export const useFontsStore = create((set) => ({ @@ -22,7 +24,6 @@ export const useFontsStore = create((set) => ({ installFont: (name) => { set((state) => { state.installedFonts.add(name); - return state; }); }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ab222d..8fc69e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,13 +52,13 @@ importers: version: 5.35.1(react@18.2.0) '@vercel/analytics': specifier: ^1.2.2 - version: 1.2.2(next@14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) + version: 1.2.2(next@14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) '@vercel/kv': specifier: ^1.0.1 version: 1.0.1 '@vercel/speed-insights': specifier: ^1.0.10 - version: 1.0.10(next@14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) + version: 1.0.10(next@14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -66,8 +66,8 @@ importers: specifier: ^7.0.0 version: 7.0.0 next: - specifier: 14.2.3 - version: 14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + specifier: 14.2.7 + version: 14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -970,117 +970,60 @@ packages: '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} - '@next/env@14.2.3': - resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} - '@next/env@14.2.7': resolution: {integrity: sha512-OTx9y6I3xE/eih+qtthppwLytmpJVPM5PPoJxChFsbjIEFXIayG0h/xLzefHGJviAa3Q5+Fd+9uYojKkHDKxoQ==} '@next/eslint-plugin-next@14.2.3': resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} - '@next/swc-darwin-arm64@14.2.3': - resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - '@next/swc-darwin-arm64@14.2.7': resolution: {integrity: sha512-UhZGcOyI9LE/tZL3h9rs/2wMZaaJKwnpAyegUVDGZqwsla6hMfeSj9ssBWQS9yA4UXun3pPhrFLVnw5KXZs3vw==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.3': - resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - '@next/swc-darwin-x64@14.2.7': resolution: {integrity: sha512-ys2cUgZYRc+CbyDeLAaAdZgS7N1Kpyy+wo0b/gAj+SeOeaj0Lw/q+G1hp+DuDiDAVyxLBCJXEY/AkhDmtihUTA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.3': - resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - '@next/swc-linux-arm64-gnu@14.2.7': resolution: {integrity: sha512-2xoWtE13sUJ3qrC1lwE/HjbDPm+kBQYFkkiVECJWctRASAHQ+NwjMzgrfqqMYHfMxFb5Wws3w9PqzZJqKFdWcQ==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.3': - resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - '@next/swc-linux-arm64-musl@14.2.7': resolution: {integrity: sha512-+zJ1gJdl35BSAGpkCbfyiY6iRTaPrt3KTl4SF/B1NyELkqqnrNX6cp4IjjjxKpd64/7enI0kf6b9O1Uf3cL0pw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.3': - resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - '@next/swc-linux-x64-gnu@14.2.7': resolution: {integrity: sha512-m6EBqrskeMUzykBrv0fDX/28lWIBGhMzOYaStp0ihkjzIYJiKUOzVYD1gULHc8XDf5EMSqoH/0/TRAgXqpQwmw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.3': - resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - '@next/swc-linux-x64-musl@14.2.7': resolution: {integrity: sha512-gUu0viOMvMlzFRz1r1eQ7Ql4OE+hPOmA7smfZAhn8vC4+0swMZaZxa9CSIozTYavi+bJNDZ3tgiSdMjmMzRJlQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.3': - resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - '@next/swc-win32-arm64-msvc@14.2.7': resolution: {integrity: sha512-PGbONHIVIuzWlYmLvuFKcj+8jXnLbx4WrlESYlVnEzDsa3+Q2hI1YHoXaSmbq0k4ZwZ7J6sWNV4UZfx1OeOlbQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.3': - resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - '@next/swc-win32-ia32-msvc@14.2.7': resolution: {integrity: sha512-BiSY5umlx9ed5RQDoHcdbuKTUkuFORDqzYKPHlLeS+STUWQKWziVOn3Ic41LuTBvqE0TRJPKpio9GSIblNR+0w==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.3': - resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - '@next/swc-win32-x64-msvc@14.2.7': resolution: {integrity: sha512-pxsI23gKWRt/SPHFkDEsP+w+Nd7gK37Hpv0ngc5HpWy2e7cKx9zR/+Q2ptAUqICNTecAaGWvmhway7pj/JLEWA==} engines: {node: '>= 10'} @@ -3677,24 +3620,6 @@ packages: react: ^16.8 || ^17 || ^18 react-dom: ^16.8 || ^17 || ^18 - next@14.2.3: - resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} - engines: {node: '>=18.17.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - sass: - optional: true - next@14.2.7: resolution: {integrity: sha512-4Qy2aK0LwH4eQiSvQWyKuC7JXE13bIopEQesWE0c/P3uuNRnZCQanI0vsrMLmUQJLAto+A+/8+sve2hd+BQuOQ==} engines: {node: '>=18.17.0'} @@ -5343,65 +5268,36 @@ snapshots: '@neon-rs/load@0.0.4': {} - '@next/env@14.2.3': {} - '@next/env@14.2.7': {} '@next/eslint-plugin-next@14.2.3': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.3': - optional: true - '@next/swc-darwin-arm64@14.2.7': optional: true - '@next/swc-darwin-x64@14.2.3': - optional: true - '@next/swc-darwin-x64@14.2.7': optional: true - '@next/swc-linux-arm64-gnu@14.2.3': - optional: true - '@next/swc-linux-arm64-gnu@14.2.7': optional: true - '@next/swc-linux-arm64-musl@14.2.3': - optional: true - '@next/swc-linux-arm64-musl@14.2.7': optional: true - '@next/swc-linux-x64-gnu@14.2.3': - optional: true - '@next/swc-linux-x64-gnu@14.2.7': optional: true - '@next/swc-linux-x64-musl@14.2.3': - optional: true - '@next/swc-linux-x64-musl@14.2.7': optional: true - '@next/swc-win32-arm64-msvc@14.2.3': - optional: true - '@next/swc-win32-arm64-msvc@14.2.7': optional: true - '@next/swc-win32-ia32-msvc@14.2.3': - optional: true - '@next/swc-win32-ia32-msvc@14.2.7': optional: true - '@next/swc-win32-x64-msvc@14.2.3': - optional: true - '@next/swc-win32-x64-msvc@14.2.7': optional: true @@ -6496,20 +6392,20 @@ snapshots: dependencies: crypto-js: 4.2.0 - '@vercel/analytics@1.2.2(next@14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': + '@vercel/analytics@1.2.2(next@14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': dependencies: server-only: 0.0.1 optionalDependencies: - next: 14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + next: 14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 '@vercel/kv@1.0.1': dependencies: '@upstash/redis': 1.25.1 - '@vercel/speed-insights@1.0.10(next@14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': + '@vercel/speed-insights@1.0.10(next@14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': optionalDependencies: - next: 14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + next: 14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: 18.2.0 '@vercel/style-guide@6.0.0(@next/eslint-plugin-next@14.2.3)(eslint@8.56.0)(prettier@3.2.5)(typescript@5.5.4)(vitest@1.6.0)': @@ -8275,31 +8171,6 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - next@14.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): - dependencies: - '@next/env': 14.2.3 - '@swc/helpers': 0.5.5 - busboy: 1.6.0 - caniuse-lite: 1.0.30001616 - graceful-fs: 4.2.11 - postcss: 8.4.31 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(react@18.2.0) - optionalDependencies: - '@next/swc-darwin-arm64': 14.2.3 - '@next/swc-darwin-x64': 14.2.3 - '@next/swc-linux-arm64-gnu': 14.2.3 - '@next/swc-linux-arm64-musl': 14.2.3 - '@next/swc-linux-x64-gnu': 14.2.3 - '@next/swc-linux-x64-musl': 14.2.3 - '@next/swc-win32-arm64-msvc': 14.2.3 - '@next/swc-win32-ia32-msvc': 14.2.3 - '@next/swc-win32-x64-msvc': 14.2.3 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - next@14.2.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@next/env': 14.2.7