Merge remote-tracking branch 'origin/main' into feat/model-provider-refactor

# Conflicts:
#	web/app/components/header/account-setting/model-provider-page/index.tsx
This commit is contained in:
yyh
2026-03-05 10:46:24 +08:00
17 changed files with 715 additions and 692 deletions

View File

@ -8,7 +8,7 @@ import GotoAnything from '@/app/components/goto-anything'
import Header from '@/app/components/header'
import HeaderWrapper from '@/app/components/header/header-wrapper'
import ReadmePanel from '@/app/components/plugins/readme-panel'
import { AppContextProvider } from '@/context/app-context'
import { AppContextProvider } from '@/context/app-context-provider'
import { EventEmitterContextProvider } from '@/context/event-emitter'
import { ModalContextProvider } from '@/context/modal-context'
import { ProviderContextProvider } from '@/context/provider-context'

View File

@ -4,7 +4,7 @@ import { AppInitializer } from '@/app/components/app-initializer'
import AmplitudeProvider from '@/app/components/base/amplitude'
import GA, { GaType } from '@/app/components/base/ga'
import HeaderWrapper from '@/app/components/header/header-wrapper'
import { AppContextProvider } from '@/context/app-context'
import { AppContextProvider } from '@/context/app-context-provider'
import { EventEmitterContextProvider } from '@/context/event-emitter'
import { ModalContextProvider } from '@/context/modal-context'
import { ProviderContextProvider } from '@/context/provider-context'

View File

@ -2,7 +2,7 @@
import Loading from '@/app/components/base/loading'
import Header from '@/app/signin/_header'
import { AppContextProvider } from '@/context/app-context'
import { AppContextProvider } from '@/context/app-context-provider'
import { useGlobalPublicStore } from '@/context/global-public-context'
import useDocumentTitle from '@/hooks/use-document-title'
import { useIsLogin } from '@/service/use-common'
@ -38,7 +38,7 @@ export default function SignInLayout({ children }: any) {
</div>
</div>
{systemFeatures.branding.enabled === false && (
<div className="system-xs-regular px-8 py-6 text-text-tertiary">
<div className="px-8 py-6 text-text-tertiary system-xs-regular">
©
{' '}
{new Date().getFullYear()}

View File

@ -3,13 +3,18 @@
import type { FC, ReactNode } from 'react'
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import { useQueryClient } from '@tanstack/react-query'
import { noop } from 'es-toolkit/function'
import { useCallback, useEffect, useMemo } from 'react'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
import { ZENDESK_FIELD_IDS } from '@/config'
import {
AppContext,
initialLangGeniusVersionInfo,
initialWorkspaceInfo,
userProfilePlaceholder,
useSelector,
} from '@/context/app-context'
import { env } from '@/env'
import {
useCurrentWorkspace,
@ -18,72 +23,6 @@ import {
} from '@/service/use-common'
import { useGlobalPublicStore } from './global-public-context'
export type AppContextValue = {
userProfile: UserProfileResponse
mutateUserProfile: VoidFunction
currentWorkspace: ICurrentWorkspace
isCurrentWorkspaceManager: boolean
isCurrentWorkspaceOwner: boolean
isCurrentWorkspaceEditor: boolean
isCurrentWorkspaceDatasetOperator: boolean
mutateCurrentWorkspace: VoidFunction
langGeniusVersionInfo: LangGeniusVersionResponse
useSelector: typeof useSelector
isLoadingCurrentWorkspace: boolean
isValidatingCurrentWorkspace: boolean
}
const userProfilePlaceholder = {
id: '',
name: '',
email: '',
avatar: '',
avatar_url: '',
is_password_set: false,
}
const initialLangGeniusVersionInfo = {
current_env: '',
current_version: '',
latest_version: '',
release_date: '',
release_notes: '',
version: '',
can_auto_update: false,
}
const initialWorkspaceInfo: ICurrentWorkspace = {
id: '',
name: '',
plan: '',
status: '',
created_at: 0,
role: 'normal',
providers: [],
trial_credits: 200,
trial_credits_used: 0,
next_credit_reset_date: 0,
}
const AppContext = createContext<AppContextValue>({
userProfile: userProfilePlaceholder,
currentWorkspace: initialWorkspaceInfo,
isCurrentWorkspaceManager: false,
isCurrentWorkspaceOwner: false,
isCurrentWorkspaceEditor: false,
isCurrentWorkspaceDatasetOperator: false,
mutateUserProfile: noop,
mutateCurrentWorkspace: noop,
langGeniusVersionInfo: initialLangGeniusVersionInfo,
useSelector,
isLoadingCurrentWorkspace: false,
isValidatingCurrentWorkspace: false,
})
export function useSelector<T>(selector: (value: AppContextValue) => T): T {
return useContextSelector(AppContext, selector)
}
export type AppContextProviderProps = {
children: ReactNode
}
@ -170,7 +109,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
// Report user and workspace info to Amplitude when loaded
if (userProfile?.id) {
setUserId(userProfile.email)
const properties: Record<string, any> = {
const properties: Record<string, string | number | boolean> = {
email: userProfile.email,
name: userProfile.name,
has_password: userProfile.is_password_set,
@ -213,7 +152,3 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
</AppContext.Provider>
)
}
export const useAppContext = () => useContext(AppContext)
export default AppContext

View File

@ -0,0 +1,73 @@
'use client'
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import { noop } from 'es-toolkit/function'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
export type AppContextValue = {
userProfile: UserProfileResponse
mutateUserProfile: VoidFunction
currentWorkspace: ICurrentWorkspace
isCurrentWorkspaceManager: boolean
isCurrentWorkspaceOwner: boolean
isCurrentWorkspaceEditor: boolean
isCurrentWorkspaceDatasetOperator: boolean
mutateCurrentWorkspace: VoidFunction
langGeniusVersionInfo: LangGeniusVersionResponse
useSelector: typeof useSelector
isLoadingCurrentWorkspace: boolean
isValidatingCurrentWorkspace: boolean
}
export const userProfilePlaceholder = {
id: '',
name: '',
email: '',
avatar: '',
avatar_url: '',
is_password_set: false,
}
export const initialLangGeniusVersionInfo = {
current_env: '',
current_version: '',
latest_version: '',
release_date: '',
release_notes: '',
version: '',
can_auto_update: false,
}
export const initialWorkspaceInfo: ICurrentWorkspace = {
id: '',
name: '',
plan: '',
status: '',
created_at: 0,
role: 'normal',
providers: [],
trial_credits: 200,
trial_credits_used: 0,
next_credit_reset_date: 0,
}
export const AppContext = createContext<AppContextValue>({
userProfile: userProfilePlaceholder,
currentWorkspace: initialWorkspaceInfo,
isCurrentWorkspaceManager: false,
isCurrentWorkspaceOwner: false,
isCurrentWorkspaceEditor: false,
isCurrentWorkspaceDatasetOperator: false,
mutateUserProfile: noop,
mutateCurrentWorkspace: noop,
langGeniusVersionInfo: initialLangGeniusVersionInfo,
useSelector,
isLoadingCurrentWorkspace: false,
isValidatingCurrentWorkspace: false,
})
export function useSelector<T>(selector: (value: AppContextValue) => T): T {
return useContextSelector(AppContext, selector)
}
export const useAppContext = () => useContext(AppContext)

View File

@ -10,6 +10,9 @@ This document tracks the migration away from legacy overlay APIs.
- `@/app/components/base/modal`
- `@/app/components/base/confirm`
- `@/app/components/base/select` (including `custom` / `pure`)
- `@/app/components/base/popover`
- `@/app/components/base/dropdown`
- `@/app/components/base/dialog`
- Replacement primitives:
- `@/app/components/base/ui/tooltip`
- `@/app/components/base/ui/dropdown-menu`

View File

@ -275,6 +275,9 @@
}
},
"app/account/(commonLayout)/delete-account/components/feed-back.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 1
}
@ -287,15 +290,17 @@
"count": 3
}
},
"app/account/(commonLayout)/delete-account/index.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/account/(commonLayout)/header.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2
}
},
"app/account/oauth/authorize/layout.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 1
},
"ts/no-explicit-any": {
"count": 1
}
@ -436,6 +441,9 @@
}
},
"app/components/app/annotation/header-opts/index.tsx": {
"no-restricted-imports": {
"count": 1
},
"react/no-nested-component-definitions": {
"count": 1
},
@ -965,6 +973,11 @@
"count": 1
}
},
"app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx": {
"no-restricted-imports": {
"count": 2
}
},
"app/components/app/configuration/debug/debug-with-multiple-model/index.spec.tsx": {
"ts/no-explicit-any": {
"count": 5
@ -1375,7 +1388,7 @@
},
"app/components/apps/app-card.tsx": {
"no-restricted-imports": {
"count": 1
"count": 3
},
"react-hooks-extra/no-direct-set-state-in-use-effect": {
"count": 1
@ -2864,6 +2877,16 @@
"count": 1
}
},
"app/components/base/tag-management/panel.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/base/tag-management/selector.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/base/tag-management/tag-item-editor.tsx": {
"no-restricted-imports": {
"count": 2
@ -3172,6 +3195,11 @@
"count": 1
}
},
"app/components/datasets/create-from-pipeline/list/template-card/actions.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/datasets/create-from-pipeline/list/template-card/content.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 3
@ -3261,7 +3289,7 @@
},
"app/components/datasets/create/step-two/components/indexing-mode-section.tsx": {
"no-restricted-imports": {
"count": 1
"count": 2
},
"tailwindcss/enforce-consistent-class-order": {
"count": 8
@ -3296,6 +3324,9 @@
}
},
"app/components/datasets/create/step-two/language-select/index.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 2
}
@ -3429,7 +3460,7 @@
},
"app/components/datasets/documents/components/operations.tsx": {
"no-restricted-imports": {
"count": 2
"count": 3
}
},
"app/components/datasets/documents/components/rename-modal.tsx": {
@ -3849,6 +3880,9 @@
}
},
"app/components/datasets/documents/detail/segment-add/index.tsx": {
"no-restricted-imports": {
"count": 1
},
"react-refresh/only-export-components": {
"count": 1
},
@ -4063,6 +4097,11 @@
"count": 1
}
},
"app/components/datasets/list/dataset-card/components/operations-popover.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"app/components/datasets/list/dataset-card/hooks/use-dataset-card-state.ts": {
"react-hooks-extra/no-direct-set-state-in-use-effect": {
"count": 1
@ -4496,6 +4535,9 @@
}
},
"app/components/header/account-setting/data-source-page-new/operator.tsx": {
"no-restricted-imports": {
"count": 2
},
"tailwindcss/enforce-consistent-class-order": {
"count": 5
}
@ -4790,6 +4832,9 @@
}
},
"app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx": {
"no-restricted-imports": {
"count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 1
}
@ -9446,14 +9491,6 @@
"count": 5
}
},
"context/app-context.tsx": {
"react-refresh/only-export-components": {
"count": 2
},
"ts/no-explicit-any": {
"count": 1
}
},
"context/datasets-context.tsx": {
"react-refresh/only-export-components": {
"count": 1

View File

@ -195,6 +195,24 @@ export default antfu(
'**/base/confirm/index',
],
message: 'Deprecated: use @/app/components/base/ui/alert-dialog instead. See issue #32767.',
}, {
group: [
'**/base/popover',
'**/base/popover/index',
],
message: 'Deprecated: use @/app/components/base/ui/popover instead. See issue #32767.',
}, {
group: [
'**/base/dropdown',
'**/base/dropdown/index',
],
message: 'Deprecated: use @/app/components/base/ui/dropdown-menu instead. See issue #32767.',
}, {
group: [
'**/base/dialog',
'**/base/dialog/index',
],
message: 'Deprecated: use @/app/components/base/ui/dialog instead. See issue #32767.',
}],
}],
},

View File

@ -243,8 +243,9 @@
"tsx": "4.21.0",
"typescript": "5.9.3",
"uglify-js": "3.19.3",
"vinext": "https://pkg.pr.new/hyoban/vinext@a30ba79",
"vinext": "https://pkg.pr.new/hyoban/vinext@556a6d6",
"vite": "8.0.0-beta.16",
"vite-plugin-inspect": "11.3.3",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.0.18",
"vitest-canvas-mock": "1.1.3"

130
web/pnpm-lock.yaml generated
View File

@ -596,11 +596,14 @@ importers:
specifier: 3.19.3
version: 3.19.3
vinext:
specifier: https://pkg.pr.new/hyoban/vinext@a30ba79
version: https://pkg.pr.new/hyoban/vinext@a30ba79(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.2)(uglify-js@3.19.3))
specifier: https://pkg.pr.new/hyoban/vinext@556a6d6
version: https://pkg.pr.new/hyoban/vinext@556a6d6(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.2)(uglify-js@3.19.3))
vite:
specifier: 8.0.0-beta.16
version: 8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
vite-plugin-inspect:
specifier: 11.3.3
version: 11.3.3(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
vite-tsconfig-paths:
specifier: 6.1.1
version: 6.1.1(typescript@5.9.3)(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
@ -2175,19 +2178,13 @@ packages:
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
'@pivanov/utils@0.0.2':
resolution: {integrity: sha512-q9CN0bFWxWgMY5hVVYyBgez1jGiLBa6I+LkG37ycylPhFvEGOOeaADGtUSu46CaZasPnlY8fCdVJZmrgKb1EPA==}
peerDependencies:
react: '>=18'
react-dom: '>=18'
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@pkgr/core@0.2.9':
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
'@preact/signals-core@1.12.2':
resolution: {integrity: sha512-5Yf8h1Ke3SMHr15xl630KtwPTW4sYDFkkxS0vQ8UiQLWwZQnrF9IKaVG1mN5VcJz52EcWs2acsc/Npjha/7ysA==}
@ -3873,6 +3870,9 @@ packages:
birecord@0.1.1:
resolution: {integrity: sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==}
birpc@2.9.0:
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@ -4579,6 +4579,9 @@ packages:
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
engines: {node: '>=18'}
error-stack-parser-es@1.0.5:
resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
es-module-lexer@1.7.0:
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
@ -6108,6 +6111,10 @@ packages:
moo-color@1.0.3:
resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==}
mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@ -6230,6 +6237,9 @@ packages:
obug@2.1.1:
resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
ohash@2.0.11:
resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@ -6353,6 +6363,9 @@ packages:
pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
perfect-debounce@2.1.0:
resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==}
periscopic@4.0.2:
resolution: {integrity: sha512-sqpQDUy8vgB7ycLkendSKS6HnVz1Rneoc3Rc+ZBUCe2pbqlVuCC5vF52l0NJ1aiMg/r1qfYF9/myz8CZeI2rjA==}
@ -6981,6 +6994,10 @@ packages:
simple-swizzle@0.2.4:
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
sirv@3.0.2:
resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
engines: {node: '>=18'}
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
@ -7288,6 +7305,10 @@ packages:
resolution: {integrity: sha512-A5F0cM6+mDleacLIEUkmfpkBbnHJFV1d2rprHU2MXNk7mlxHq2zGojA+SRvQD1RoMo9gqjZPWEaKG4v1BQ48lw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
tough-cookie@6.0.0:
resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==}
engines: {node: '>=16'}
@ -7435,6 +7456,10 @@ packages:
unpic@4.2.2:
resolution: {integrity: sha512-z6T2ScMgRV2y2H8MwwhY5xHZWXhUx/YxtOCGJwfURSl7ypVy4HpLIMWoIZKnnxQa/RKzM0kg8hUh0paIrpLfvw==}
unplugin-utils@0.3.1:
resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==}
engines: {node: '>=20.19.0'}
unplugin@2.1.0:
resolution: {integrity: sha512-us4j03/499KhbGP8BU7Hrzrgseo+KdfJYWcbcajCOqsAyb8Gk0Yn2kiUIcZISYCb1JFaZfIuG3b42HmguVOKCQ==}
engines: {node: '>=18.12.0'}
@ -7541,8 +7566,8 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
vinext@https://pkg.pr.new/hyoban/vinext@a30ba79:
resolution: {integrity: sha512-yx/gCneOli5eGTrLUq6/M7A6DGQs14qOJW/Xp9RN6sTI0mErKyWWjO5E7FZT98BJbqH5xzI5nk8EOCLF3bojkA==, tarball: https://pkg.pr.new/hyoban/vinext@a30ba79}
vinext@https://pkg.pr.new/hyoban/vinext@556a6d6:
resolution: {integrity: sha512-Sz8RkTDsY6cnGrevlQi4nXgahu8okEGsdKY5m31d/L9tXo35bNETMHfVee5gaI2UKZS9LMcffWaTOxxINUgogQ==, tarball: https://pkg.pr.new/hyoban/vinext@556a6d6}
version: 0.0.5
engines: {node: '>=22'}
hasBin: true
@ -7551,12 +7576,32 @@ packages:
react-dom: '>=19.2.0'
vite: ^7.0.0
vite-dev-rpc@1.1.0:
resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0
vite-hot-client@2.1.0:
resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==}
peerDependencies:
vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
vite-plugin-commonjs@0.10.4:
resolution: {integrity: sha512-eWQuvQKCcx0QYB5e5xfxBNjQKyrjEWZIR9UOkOV6JAgxVhtbZvCOF+FNC2ZijBJ3U3Px04ZMMyyMyFBVWIJ5+g==}
vite-plugin-dynamic-import@1.6.0:
resolution: {integrity: sha512-TM0sz70wfzTIo9YCxVFwS8OA9lNREsh+0vMHGSkWDTZ7bgd1Yjs5RV8EgB634l/91IsXJReg0xtmuQqP0mf+rg==}
vite-plugin-inspect@11.3.3:
resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==}
engines: {node: '>=14'}
peerDependencies:
'@nuxt/kit': '*'
vite: ^6.0.0 || ^7.0.0-0
peerDependenciesMeta:
'@nuxt/kit':
optional: true
vite-plugin-storybook-nextjs@3.2.2:
resolution: {integrity: sha512-ZJXCrhi9mW4jEJTKhJ5sUtpBe84mylU40me2aMuLSgIJo4gE/Rc559hZvMYLFTWta1gX7Rm8Co5EEHakPct+wA==}
peerDependencies:
@ -9706,15 +9751,10 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.6
optional: true
'@pivanov/utils@0.0.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
'@pkgjs/parseargs@0.11.0':
optional: true
'@pkgr/core@0.2.9': {}
'@polka/url@1.0.0-next.29': {}
'@preact/signals-core@1.12.2': {}
'@preact/signals@1.3.2(preact@10.28.2)':
@ -11531,6 +11571,8 @@ snapshots:
birecord@0.1.1: {}
birpc@2.9.0: {}
bl@4.1.0:
dependencies:
buffer: 5.7.1
@ -12235,6 +12277,8 @@ snapshots:
environment@1.1.0: {}
error-stack-parser-es@1.0.5: {}
es-module-lexer@1.7.0: {}
es-module-lexer@2.0.0: {}
@ -14298,6 +14342,8 @@ snapshots:
dependencies:
color-name: 1.1.4
mrmime@2.0.1: {}
ms@2.1.3: {}
mz@2.7.0:
@ -14394,6 +14440,8 @@ snapshots:
obug@2.1.1: {}
ohash@2.0.11: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
@ -14547,6 +14595,8 @@ snapshots:
pend@1.2.0: {}
perfect-debounce@2.1.0: {}
periscopic@4.0.2:
dependencies:
'@types/estree': 1.0.8
@ -15379,6 +15429,12 @@ snapshots:
dependencies:
is-arrayish: 0.3.4
sirv@3.0.2:
dependencies:
'@polka/url': 1.0.0-next.29
mrmime: 2.0.1
totalist: 3.0.1
sisteransi@1.0.5: {}
size-sensor@1.0.3: {}
@ -15694,6 +15750,8 @@ snapshots:
dependencies:
eslint-visitor-keys: 5.0.0
totalist@3.0.1: {}
tough-cookie@6.0.0:
dependencies:
tldts: 7.0.17
@ -15838,6 +15896,11 @@ snapshots:
unpic@4.2.2: {}
unplugin-utils@0.3.1:
dependencies:
pathe: 2.0.3
picomatch: 4.0.3
unplugin@2.1.0:
dependencies:
acorn: 8.16.0
@ -15931,7 +15994,7 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
vinext@https://pkg.pr.new/hyoban/vinext@a30ba79(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.2)(uglify-js@3.19.3)):
vinext@https://pkg.pr.new/hyoban/vinext@556a6d6(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.2)(uglify-js@3.19.3)):
dependencies:
'@unpic/react': 1.0.2(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@vercel/og': 0.8.6
@ -15950,6 +16013,16 @@ snapshots:
- typescript
- webpack
vite-dev-rpc@1.1.0(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
birpc: 2.9.0
vite: 8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
vite-hot-client: 2.1.0(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
vite-hot-client@2.1.0(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
vite: 8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
vite-plugin-commonjs@0.10.4:
dependencies:
acorn: 8.16.0
@ -15963,6 +16036,21 @@ snapshots:
fast-glob: 3.3.3
magic-string: 0.30.21
vite-plugin-inspect@11.3.3(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
ansis: 4.2.0
debug: 4.4.3
error-stack-parser-es: 1.0.5
ohash: 2.0.11
open: 10.2.0
perfect-debounce: 2.1.0
sirv: 3.0.2
unplugin-utils: 0.3.1
vite: 8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
vite-dev-rpc: 1.1.0(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))
transitivePeerDependencies:
- supports-color
vite-plugin-storybook-nextjs@3.2.2(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(storybook@10.2.13(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)(vite@8.0.0-beta.16(@types/node@24.10.12)(esbuild@0.27.2)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
'@next/env': 16.0.0

View File

@ -6,6 +6,7 @@ import react from '@vitejs/plugin-react'
import { codeInspectorPlugin } from 'code-inspector-plugin'
import vinext from 'vinext'
import { defineConfig } from 'vite'
import Inspect from 'vite-plugin-inspect'
import tsconfigPaths from 'vite-tsconfig-paths'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
@ -70,6 +71,93 @@ const createForceInspectorClientInjectionPlugin = (): Plugin => {
}
}
function customI18nHmrPlugin(): Plugin {
const injectTarget = inspectorInjectTarget
const i18nHmrClientMarker = 'custom-i18n-hmr-client'
const i18nHmrClientSnippet = `/* ${i18nHmrClientMarker} */
if (import.meta.hot) {
const getI18nUpdateTarget = (file) => {
const match = file.match(/[/\\\\]i18n[/\\\\]([^/\\\\]+)[/\\\\]([^/\\\\]+)\\.json$/)
if (!match)
return null
const [, locale, namespaceFile] = match
return { locale, namespaceFile }
}
import.meta.hot.on('i18n-update', async ({ file, content }) => {
const target = getI18nUpdateTarget(file)
if (!target)
return
const [{ getI18n }, { camelCase }] = await Promise.all([
import('react-i18next'),
import('es-toolkit/string'),
])
const i18n = getI18n()
if (!i18n)
return
if (target.locale !== i18n.language)
return
let resources
try {
resources = JSON.parse(content)
}
catch {
return
}
const namespace = camelCase(target.namespaceFile)
i18n.addResourceBundle(target.locale, namespace, resources, true, true)
i18n.emit('languageChanged', i18n.language)
})
}
`
const injectI18nHmrClient = (code: string) => {
if (code.includes(i18nHmrClientMarker))
return code
const useClientMatch = code.match(/(['"])use client\1;?\s*\n/)
if (!useClientMatch)
return `${i18nHmrClientSnippet}\n${code}`
const insertAt = (useClientMatch.index ?? 0) + useClientMatch[0].length
return `${code.slice(0, insertAt)}\n${i18nHmrClientSnippet}\n${code.slice(insertAt)}`
}
return {
name: 'custom-i18n-hmr',
apply: 'serve',
handleHotUpdate({ file, server }) {
if (file.endsWith('.json') && file.includes('/i18n/')) {
server.ws.send({
type: 'custom',
event: 'i18n-update',
data: {
file,
content: fs.readFileSync(file, 'utf-8'),
},
})
// return empty array to prevent the default HMR
return []
}
},
transform(code, id) {
const cleanId = normalizeInspectorModuleId(id)
if (cleanId !== injectTarget)
return null
const nextCode = injectI18nHmrClient(code)
if (nextCode === code)
return null
return { code: nextCode, map: null }
},
}
}
export default defineConfig(({ mode }) => {
const isTest = mode === 'test'
@ -89,9 +177,12 @@ export default defineConfig(({ mode }) => {
} as Plugin,
]
: [
Inspect(),
createCodeInspectorPlugin(),
createForceInspectorClientInjectionPlugin(),
react(),
vinext(),
customI18nHmrPlugin(),
],
resolve: {
alias: {