chore: upgrade frontend to React 19 (#5940)
This commit is contained in:
parent
084f40bc9e
commit
b6f42cbe6b
15 changed files with 1008 additions and 1175 deletions
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1995
web/pnpm-lock.yaml
1995
web/pnpm-lock.yaml
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>,
|
||||
|
|
|
|||
15
web/src/types/relative-time-element.d.ts
vendored
Normal file
15
web/src/types/relative-time-element.d.ts
vendored
Normal file
|
|
@ -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…
Reference in a new issue