chore: upgrade frontend to React 19 (#5940)

pull/5942/head
boojack 3 weeks ago committed by GitHub
parent 084f40bc9e
commit b6f42cbe6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -21,7 +21,7 @@
"@connectrpc/connect-web": "^2.1.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@github/relative-time-element": "^4.5.1",
"@github/relative-time-element": "^5.0.0",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
@ -34,34 +34,34 @@
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"@tailwindcss/vite": "^4.2.4",
"@tanstack/react-query": "^5.100.5",
"@tanstack/react-query-devtools": "^5.100.5",
"@tanstack/react-query": "^5.100.9",
"@tanstack/react-query-devtools": "^5.100.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"copy-to-clipboard": "^3.3.3",
"copy-to-clipboard": "^4.0.2",
"dayjs": "^1.11.20",
"fuse.js": "^7.3.0",
"highlight.js": "^11.11.1",
"html-to-image": "^1.11.13",
"i18next": "^25.10.10",
"i18next": "^26.0.10",
"katex": "^0.16.45",
"leaflet": "^1.9.4",
"leaflet.markercluster": "^1.5.3",
"lodash-es": "^4.18.1",
"lucide-react": "^0.577.0",
"lucide-react": "^1.14.0",
"mdast-util-from-markdown": "^2.0.3",
"mdast-util-gfm": "^3.1.0",
"mermaid": "^11.14.0",
"micromark-extension-gfm": "^3.0.0",
"mime": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-hot-toast": "^2.6.0",
"react-i18next": "^15.7.4",
"react-leaflet": "^4.2.1",
"react-leaflet-cluster": "^2.1.0",
"react-i18next": "^17.0.7",
"react-leaflet": "^5.0.0",
"react-leaflet-cluster": "^4.1.3",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.14.2",
"react-router-dom": "^7.15.0",
"react-use": "^17.6.0",
"rehype-katex": "^7.0.1",
"rehype-raw": "^7.0.0",
@ -73,13 +73,18 @@
"tailwindcss": "^4.2.4",
"textarea-caret": "^3.1.0",
"unist-util-visit": "^5.1.0",
"uuid": "^11.1.0"
"uuid": "^14.0.0"
},
"devDependencies": {
"@biomejs/biome": "^2.4.13",
"@babel/core": "7.29.0",
"@babel/plugin-transform-runtime": "7.29.0",
"@babel/runtime": "7.29.2",
"@biomejs/biome": "^2.4.14",
"@bufbuild/protobuf": "^2.12.0",
"@rolldown/plugin-babel": "0.2.3",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/babel__core": "7.20.5",
"@types/d3": "^7.4.3",
"@types/hast": "^3.0.4",
"@types/katex": "^0.16.8",
@ -87,20 +92,20 @@
"@types/lodash-es": "^4.17.12",
"@types/mdast": "^4.0.4",
"@types/node": "^24.12.2",
"@types/qs": "^6.15.0",
"@types/react": "^18.3.28",
"@types/react-dom": "^18.3.7",
"@types/qs": "^6.15.1",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/textarea-caret": "^3.0.4",
"@types/unist": "^3.0.3",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react": "^4.7.0",
"baseline-browser-mapping": "^2.10.23",
"jsdom": "^29.1.0",
"@vitejs/plugin-react": "^6.0.1",
"babel-plugin-react-compiler": "1.0.0",
"baseline-browser-mapping": "^2.10.27",
"jsdom": "^29.1.1",
"long": "^5.3.2",
"terser": "^5.46.2",
"terser": "^5.47.1",
"tw-animate-css": "^1.4.0",
"typescript": "^6.0.3",
"vite": "^7.3.2",
"vite": "^8.0.11",
"vitest": "^4.1.5"
}
}

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
import copy from "copy-to-clipboard";
import hljs from "highlight.js";
import { CheckIcon, CopyIcon } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { isValidElement, type ReactElement, type ReactNode, useEffect, useMemo, useState } from "react";
import { useAuth } from "@/contexts/AuthContext";
import { cn } from "@/lib/utils";
import { getThemeWithFallback, resolveTheme } from "@/utils/theme";
@ -10,7 +10,7 @@ import type { ReactMarkdownProps } from "./markdown/types";
import { extractCodeContent, extractLanguage } from "./utils";
interface CodeBlockProps extends ReactMarkdownProps {
children?: React.ReactNode;
children?: ReactNode;
className?: string;
}
@ -18,8 +18,8 @@ export const CodeBlock = ({ children, className, node: _node, ...props }: CodeBl
const { userGeneralSetting } = useAuth();
const [copied, setCopied] = useState(false);
const codeElement = children as React.ReactElement;
const codeClassName = codeElement?.props?.className || "";
const codeElement = isValidElement(children) ? (children as ReactElement<{ className?: string }>) : null;
const codeClassName = codeElement?.props.className || "";
const codeContent = extractCodeContent(children);
const language = extractLanguage(codeClassName);
@ -93,7 +93,7 @@ export const CodeBlock = ({ children, className, node: _node, ...props }: CodeBl
setTimeout(() => setCopied(false), 2000);
} else {
// Fallback to copy-to-clipboard library for non-secure contexts
const success = copy(codeContent);
const success = await copy(codeContent);
if (success) {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
@ -104,7 +104,7 @@ export const CodeBlock = ({ children, className, node: _node, ...props }: CodeBl
} catch (err) {
// If native API fails, try fallback
console.warn("Native clipboard failed, using fallback:", err);
const success = copy(codeContent);
const success = await copy(codeContent);
if (success) {
setCopied(true);
setTimeout(() => setCopied(false), 2000);

@ -1,4 +1,4 @@
import type React from "react";
import { isValidElement, type ReactElement, type ReactNode } from "react";
/**
* Extracts code content from a react-markdown code element.
@ -7,9 +7,9 @@ import type React from "react";
* @param children - The children prop from react-markdown (typically a code element)
* @returns The extracted code content as a string with trailing newline removed
*/
export const extractCodeContent = (children: React.ReactNode): string => {
const codeElement = children as React.ReactElement;
return String(codeElement?.props?.children || "").replace(/\n$/, "");
export const extractCodeContent = (children: ReactNode): string => {
const codeElement = isValidElement(children) ? (children as ReactElement<{ children?: ReactNode }>) : null;
return String(codeElement?.props.children || "").replace(/\n$/, "");
};
/**

@ -1,9 +1,9 @@
import { useEffect, useRef } from "react";
import { type RefObject, useEffect, useRef } from "react";
import { detectLastListItem, generateListContinuation } from "@/utils/markdown-list-detection";
import { EditorRefActions } from ".";
interface UseListCompletionOptions {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorRef: RefObject<HTMLTextAreaElement | null>;
editorActions: EditorRefActions;
isInIME: boolean;
}

@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { type ForwardedRef, type RefObject, useEffect, useRef, useState } from "react";
import getCaretCoordinates from "textarea-caret";
import { EditorRefActions } from ".";
@ -9,8 +9,8 @@ export interface Position {
}
export interface UseSuggestionsOptions<T> {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
editorRef: RefObject<HTMLTextAreaElement | null>;
editorActions: ForwardedRef<EditorRefActions>;
triggerChar: string;
items: T[];
filterItems: (items: T[], searchQuery: string) => T[];

@ -1,3 +1,4 @@
import type { ClipboardEvent, ForwardedRef, RefObject } from "react";
import type { Location, Memo, Visibility } from "@/types/proto/api/v1/memo_service_pb";
import type { EditorRefActions } from "../Editor";
import type { Command } from "../Editor/commands";
@ -70,13 +71,13 @@ export interface InsertMenuProps {
}
export interface TagSuggestionsProps {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
editorRef: RefObject<HTMLTextAreaElement | null>;
editorActions: ForwardedRef<EditorRefActions>;
}
export interface SlashCommandsProps {
editorRef: React.RefObject<HTMLTextAreaElement>;
editorActions: React.ForwardedRef<EditorRefActions>;
editorRef: RefObject<HTMLTextAreaElement | null>;
editorActions: ForwardedRef<EditorRefActions>;
commands: Command[];
}
@ -85,7 +86,7 @@ export interface EditorProps {
initialContent: string;
placeholder: string;
onContentChange: (content: string) => void;
onPaste: (event: React.ClipboardEvent) => void;
onPaste: (event: ClipboardEvent) => void;
isFocusMode?: boolean;
isInIME?: boolean;
onCompositionStart?: () => void;

@ -1,6 +1,6 @@
import { useQueryClient } from "@tanstack/react-query";
import { ArrowUpIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { MentionResolutionProvider } from "@/components/MemoContent/MentionResolutionContext";
import { deriveDefaultCreateTimeFromFilters } from "@/components/MemoEditor/utils/deriveDefaultCreateTime";
import { Button } from "@/components/ui/button";
@ -18,7 +18,7 @@ import MemoFilters from "../MemoFilters";
import Skeleton from "../Skeleton";
interface Props {
renderer: (memo: Memo) => JSX.Element;
renderer: (memo: Memo) => ReactElement;
listSort?: (list: Memo[]) => Memo[];
state?: State;
orderBy?: string;

@ -1,4 +1,5 @@
import { Monitor, Moon, Palette, Sun } from "lucide-react";
import type { ReactElement } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { loadTheme, THEME_OPTIONS } from "@/utils/theme";
@ -8,7 +9,7 @@ interface ThemeSelectProps {
className?: string;
}
const THEME_ICONS: Record<string, JSX.Element> = {
const THEME_ICONS: Record<string, ReactElement> = {
system: <Monitor className="w-4 h-4" />,
default: <Sun className="w-4 h-4" />,
"default-dark": <Moon className="w-4 h-4" />,

@ -4,14 +4,9 @@ import * as React from "react";
import { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
const DropdownMenu = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
>(({ ...props }, _ref) => {
const DropdownMenu = ({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) => {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" modal={false} {...props} />;
});
DropdownMenu.displayName = "DropdownMenu";
};
const DropdownMenuPortal = ({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) => {
return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;

@ -2,14 +2,9 @@ import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
import { cn } from "@/lib/utils";
const Popover = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
>(({ ...props }, _ref) => {
const Popover = ({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) => {
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
});
Popover.displayName = "Popover";
};
const PopoverTrigger = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Trigger>,

@ -3,13 +3,9 @@ import { XIcon } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";
const Sheet = React.forwardRef<React.ElementRef<typeof SheetPrimitive.Root>, React.ComponentPropsWithoutRef<typeof SheetPrimitive.Root>>(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
({ ...props }, _ref) => {
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
},
);
Sheet.displayName = "Sheet";
const Sheet = ({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) => {
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
};
const SheetTrigger = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Trigger>,

@ -2,27 +2,17 @@ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";
import { cn } from "@/lib/utils";
const TooltipProvider = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Provider>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
>(({ delayDuration = 0, ...props }, _ref) => {
const TooltipProvider = ({ delayDuration = 0, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) => {
return <TooltipPrimitive.Provider data-slot="tooltip-provider" delayDuration={delayDuration} {...props} />;
});
TooltipProvider.displayName = "TooltipProvider";
};
const Tooltip = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>
// eslint-disable-next-line @typescript-eslint/no-unused-vars
>(({ ...props }, _ref) => {
const Tooltip = ({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) => {
return (
<TooltipProvider>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipProvider>
);
});
Tooltip.displayName = "Tooltip";
};
const TooltipTrigger = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Trigger>,

@ -0,0 +1,15 @@
import type { Format, RelativeTimeElement } from "@github/relative-time-element";
import type { HTMLAttributes } from "react";
declare module "react" {
// biome-ignore lint/style/noNamespace: React exposes JSX customization through namespace merging.
namespace JSX {
interface IntrinsicElements {
"relative-time": HTMLAttributes<RelativeTimeElement> & {
datetime?: string;
format?: Format;
"no-title"?: string;
};
}
}
}

@ -1,4 +1,5 @@
import react from "@vitejs/plugin-react";
import babel from "@rolldown/plugin-babel";
import react, { reactCompilerPreset } from "@vitejs/plugin-react";
import { resolve } from "path";
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
@ -9,9 +10,9 @@ if (process.env.DEV_PROXY_SERVER && process.env.DEV_PROXY_SERVER.length > 0) {
devProxyServer = process.env.DEV_PROXY_SERVER;
}
// https://vitejs.dev/config/
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
plugins: [react(), babel({ presets: [reactCompilerPreset()] }), tailwindcss()],
server: {
host: "0.0.0.0",
port: 3001,
@ -42,12 +43,23 @@ export default defineConfig({
},
},
build: {
rollupOptions: {
rolldownOptions: {
output: {
manualChunks: {
"utils-vendor": ["dayjs", "lodash-es"],
"mermaid-vendor": ["mermaid"],
"leaflet-vendor": ["leaflet", "react-leaflet"],
codeSplitting: {
groups: [
{
name: "utils-vendor",
test: /node_modules[\\/](dayjs|lodash-es)([\\/]|$)/,
},
{
name: "mermaid-vendor",
test: /node_modules[\\/]mermaid([\\/]|$)/,
},
{
name: "leaflet-vendor",
test: /node_modules[\\/](leaflet|react-leaflet)([\\/]|$)/,
},
],
},
},
},

Loading…
Cancel
Save