Merge pull request #420 from alibaba/refactor/node-and-eslint-upgrade
refactor: upgrade Node and ESLint
This commit is contained in:
@@ -1,8 +1,5 @@
|
|||||||
|
import eslintReact from '@eslint-react/eslint-plugin'
|
||||||
import js from '@eslint/js'
|
import js from '@eslint/js'
|
||||||
import reactDom from 'eslint-plugin-react-dom'
|
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
||||||
import reactX from 'eslint-plugin-react-x'
|
|
||||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
import globals from 'globals'
|
import globals from 'globals'
|
||||||
import tseslint from 'typescript-eslint'
|
import tseslint from 'typescript-eslint'
|
||||||
@@ -15,44 +12,24 @@ export default defineConfig([
|
|||||||
'**/.wxt',
|
'**/.wxt',
|
||||||
'**/.output',
|
'**/.output',
|
||||||
]),
|
]),
|
||||||
{
|
|
||||||
plugins: {
|
|
||||||
'react-hooks': reactHooks,
|
|
||||||
},
|
|
||||||
rules: reactHooks.configs.recommended.rules,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
files: ['**/*.{ts,tsx}'],
|
files: ['**/*.{ts,tsx}'],
|
||||||
extends: [
|
extends: [
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
// reactHooks.configs['recommended-latest'],
|
|
||||||
reactRefresh.configs.vite,
|
|
||||||
|
|
||||||
// Remove tseslint.configs.recommended and replace with this
|
|
||||||
...tseslint.configs.recommendedTypeChecked,
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
// Alternatively, use this for stricter rules
|
|
||||||
...tseslint.configs.strictTypeChecked,
|
...tseslint.configs.strictTypeChecked,
|
||||||
// Optionally, add this for stylistic rules
|
|
||||||
...tseslint.configs.stylisticTypeChecked,
|
...tseslint.configs.stylisticTypeChecked,
|
||||||
|
eslintReact.configs['recommended-typescript'],
|
||||||
// Enable lint rules for React
|
|
||||||
reactX.configs['recommended-typescript'],
|
|
||||||
// Enable lint rules for React DOM
|
|
||||||
reactDom.configs.recommended,
|
|
||||||
],
|
],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
// project: ['./tsconfig.json'],
|
|
||||||
// project: ['./packages/*/tsconfig.json'],
|
|
||||||
// tsconfigRootDir: import.meta.dirname,
|
|
||||||
projectService: true,
|
projectService: true,
|
||||||
},
|
},
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2020,
|
||||||
globals: globals.browser,
|
globals: globals.browser,
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
// Add any additional rules here
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
@@ -72,14 +49,14 @@ export default defineConfig([
|
|||||||
'@typescript-eslint/no-unsafe-argument': 'off',
|
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||||
'@typescript-eslint/no-unsafe-return': 'off',
|
'@typescript-eslint/no-unsafe-return': 'off',
|
||||||
'@typescript-eslint/restrict-plus-operands': 'off',
|
'@typescript-eslint/restrict-plus-operands': 'off',
|
||||||
'react-dom/no-missing-button-type': 'off',
|
|
||||||
'react-x/no-nested-component-definitions': 'off',
|
|
||||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||||
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
|
||||||
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
|
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
|
||||||
|
|
||||||
// 'require-await': 'off',
|
|
||||||
'@typescript-eslint/require-await': 'off',
|
'@typescript-eslint/require-await': 'off',
|
||||||
|
'@eslint-react/dom-no-missing-button-type': 'off',
|
||||||
|
'@eslint-react/no-nested-component-definitions': 'off',
|
||||||
|
'@eslint-react/no-array-index-key': 'off',
|
||||||
|
'@eslint-react/dom-no-dangerously-set-innerhtml': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
3465
package-lock.json
generated
3465
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://alibaba.github.io/page-agent/",
|
"homepage": "https://alibaba.github.io/page-agent/",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
"node": "^22.13.0 || >=24"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm run dev --workspace=@page-agent/website",
|
"start": "npm run dev --workspace=@page-agent/website",
|
||||||
@@ -35,12 +35,13 @@
|
|||||||
"version": "node scripts/sync-version.js",
|
"version": "node scripts/sync-version.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"cleanup": "rm -rf packages/*/dist",
|
"cleanup": "rm -rf packages/*/dist",
|
||||||
"prepare": "husky"
|
"prepare": "husky || true"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^20.5.0",
|
"@commitlint/cli": "^20.5.0",
|
||||||
"@commitlint/config-conventional": "^20.5.0",
|
"@commitlint/config-conventional": "^20.5.0",
|
||||||
"@eslint/js": "^9.39.2",
|
"@eslint-react/eslint-plugin": "^4.2.3",
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
"@microsoft/api-extractor": "^7.58.1",
|
"@microsoft/api-extractor": "^7.58.1",
|
||||||
"@tailwindcss/vite": "^4.2.2",
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||||
@@ -49,25 +50,21 @@
|
|||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
"dotenv": "^17.4.1",
|
"dotenv": "^17.4.1",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^10.2.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
|
||||||
"eslint-plugin-react-dom": "^2.13.0",
|
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
|
||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
|
||||||
"eslint-plugin-react-x": "^2.13.0",
|
|
||||||
"globals": "^17.4.0",
|
"globals": "^17.4.0",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.4.0",
|
"lint-staged": "^16.4.0",
|
||||||
"prettier": "^3.8.0",
|
"prettier": "^3.8.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.58.1",
|
"typescript-eslint": "^8.58.1",
|
||||||
"unplugin-dts": "^1.0.0-beta.6",
|
"unplugin-dts": "^1.0.0-beta.6",
|
||||||
"vite": "^7.3.2",
|
"vite": "^7.3.2",
|
||||||
"vite-plugin-css-injected-by-js": "^4.0.1",
|
"vite-bundle-analyzer": "^1.3.7",
|
||||||
"vite-bundle-analyzer": "^1.3.7"
|
"vite-plugin-css-injected-by-js": "^4.0.1"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3",
|
||||||
|
"@vitejs/plugin-react": "^5.2.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts,cjs,cts,mjs,mts}": [
|
"*.{js,ts,cjs,cts,mjs,mts}": [
|
||||||
|
|||||||
@@ -51,6 +51,6 @@
|
|||||||
"zod": "^3.25.0 || ^4.0.0"
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const log = console.log.bind(console, chalk.yellow('[autoFixer]'))
|
|||||||
* - etc.
|
* - etc.
|
||||||
*/
|
*/
|
||||||
export function normalizeResponse(response: any, tools?: Map<string, PageAgentTool>): any {
|
export function normalizeResponse(response: any, tools?: Map<string, PageAgentTool>): any {
|
||||||
let resolvedArguments = null as any
|
let resolvedArguments: any
|
||||||
|
|
||||||
const choice = (response as { choices?: Choice[] }).choices?.[0]
|
const choice = (response as { choices?: Choice[] }).choices?.[0]
|
||||||
if (!choice) throw new Error('No choices in response')
|
if (!choice) throw new Error('No choices in response')
|
||||||
|
|||||||
@@ -16,9 +16,10 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.8",
|
"@radix-ui/react-separator": "^1.1.8",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
"@types/chrome": "^0.1.39",
|
"@types/chrome": "^0.1.39",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.1",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@wxt-dev/module-react": "^1.2.2",
|
"@wxt-dev/module-react": "^1.2.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
"simple-icons": "^16.15.0",
|
"simple-icons": "^16.15.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.2.2",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"wxt": "^0.20.20"
|
"wxt": "^0.20.20"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export class RemotePageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getBrowserState(): Promise<BrowserState> {
|
async getBrowserState(): Promise<BrowserState> {
|
||||||
let browserState = {} as BrowserState
|
let browserState: BrowserState
|
||||||
debug('getBrowserState', this.currentTabId)
|
debug('getBrowserState', this.currentTabId)
|
||||||
|
|
||||||
const currentUrl = await this.getCurrentUrl()
|
const currentUrl = await this.getCurrentUrl()
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
const [showToken, setShowToken] = useState(false)
|
const [showToken, setShowToken] = useState(false)
|
||||||
const [showApiKey, setShowApiKey] = useState(false)
|
const [showApiKey, setShowApiKey] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
const [prevConfig, setPrevConfig] = useState(config)
|
||||||
|
if (prevConfig !== config) {
|
||||||
|
setPrevConfig(config)
|
||||||
setBaseURL(config?.baseURL || DEMO_BASE_URL)
|
setBaseURL(config?.baseURL || DEMO_BASE_URL)
|
||||||
setModel(config?.model || DEMO_MODEL)
|
setModel(config?.model || DEMO_MODEL)
|
||||||
setApiKey(config?.apiKey)
|
setApiKey(config?.apiKey)
|
||||||
@@ -59,7 +61,7 @@ export function ConfigPanel({ config, onSave, onClose }: ConfigPanelProps) {
|
|||||||
setExperimentalLlmsTxt(config?.experimentalLlmsTxt ?? false)
|
setExperimentalLlmsTxt(config?.experimentalLlmsTxt ?? false)
|
||||||
setExperimentalIncludeAllTabs(config?.experimentalIncludeAllTabs ?? false)
|
setExperimentalIncludeAllTabs(config?.experimentalIncludeAllTabs ?? false)
|
||||||
setDisableNamedToolChoice(config?.disableNamedToolChoice ?? false)
|
setDisableNamedToolChoice(config?.disableNamedToolChoice ?? false)
|
||||||
}, [config])
|
}
|
||||||
|
|
||||||
// Poll for user auth token every second until found
|
// Poll for user auth token every second until found
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export function HistoryDetail({
|
|||||||
{/* Events (read-only) */}
|
{/* Events (read-only) */}
|
||||||
<div className="flex-1 overflow-y-auto p-3 space-y-2">
|
<div className="flex-1 overflow-y-auto p-3 space-y-2">
|
||||||
{session.history.map((event, index) => (
|
{session.history.map((event, index) => (
|
||||||
// eslint-disable-next-line react-x/no-array-index-key
|
|
||||||
<EventCard key={index} event={event} />
|
<EventCard key={index} event={event} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export function HistoryList({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
||||||
load()
|
load()
|
||||||
}, [load])
|
}, [load])
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ export default function App() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{history.map((event, index) => (
|
{history.map((event, index) => (
|
||||||
// eslint-disable-next-line react-x/no-array-index-key
|
|
||||||
<EventCard key={index} event={event} />
|
<EventCard key={index} event={event} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|||||||
@@ -206,9 +206,9 @@ export function useHubWs(
|
|||||||
const [wsState, setWsState] = useState<HubWsState>(() => (wsPort ? 'connecting' : 'disconnected'))
|
const [wsState, setWsState] = useState<HubWsState>(() => (wsPort ? 'connecting' : 'disconnected'))
|
||||||
const hubWsRef = useRef<HubWs | null>(null)
|
const hubWsRef = useRef<HubWs | null>(null)
|
||||||
|
|
||||||
const latest = useRef({ execute, stop, configure, config })
|
const latestRef = useRef({ execute, stop, configure, config })
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
latest.current = { execute, stop, configure, config }
|
latestRef.current = { execute, stop, configure, config }
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -218,14 +218,14 @@ export function useHubWs(
|
|||||||
Number(wsPort),
|
Number(wsPort),
|
||||||
{
|
{
|
||||||
onExecute: async (task, incomingConfig) => {
|
onExecute: async (task, incomingConfig) => {
|
||||||
const { execute, configure, config } = latest.current
|
const { execute, configure, config } = latestRef.current
|
||||||
if (incomingConfig) {
|
if (incomingConfig) {
|
||||||
await configure({ ...config, ...incomingConfig } as ExtConfig)
|
await configure({ ...config, ...incomingConfig } as ExtConfig)
|
||||||
}
|
}
|
||||||
const result = await execute(task)
|
const result = await execute(task)
|
||||||
return { success: result.success, data: result.data }
|
return { success: result.success, data: result.data }
|
||||||
},
|
},
|
||||||
onStop: () => latest.current.stop(),
|
onStop: () => latestRef.current.stop(),
|
||||||
},
|
},
|
||||||
setWsState
|
setWsState
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -178,7 +178,6 @@ export default function App() {
|
|||||||
{showEmptyState && <EmptyState />}
|
{showEmptyState && <EmptyState />}
|
||||||
|
|
||||||
{history.map((event, index) => (
|
{history.map((event, index) => (
|
||||||
// eslint-disable-next-line react-x/no-array-index-key
|
|
||||||
<EventCard key={index} event={event} />
|
<EventCard key={index} event={event} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,6 @@
|
|||||||
"zod": "^3.25.0 || ^4.0.0"
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||||
"ws": "^8.20.0",
|
"ws": "^8.20.0",
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,6 @@
|
|||||||
"zod": "^3.25.0 || ^4.0.0"
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"zod": "^4.3.5"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"@radix-ui/react-switch": "^1.2.6",
|
"@radix-ui/react-switch": "^1.2.6",
|
||||||
"@radix-ui/react-tooltip": "^1.2.8",
|
"@radix-ui/react-tooltip": "^1.2.8",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.1",
|
"@types/react-dom": "^19.2.3",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"simple-icons": "^16.15.0",
|
"simple-icons": "^16.15.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.5.0",
|
"tailwind-merge": "^3.5.0",
|
||||||
"tailwindcss": "^4.1.14",
|
"tailwindcss": "^4.2.2",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"wouter": "^3.9.0"
|
"wouter": "^3.9.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,6 @@ function highlightSyntax(code: string): string {
|
|||||||
const HighlightSyntaxClient: React.FC<HighlightSyntaxProps> = ({ code }) => {
|
const HighlightSyntaxClient: React.FC<HighlightSyntaxProps> = ({ code }) => {
|
||||||
const htmlContent = highlightSyntax(code)
|
const htmlContent = highlightSyntax(code)
|
||||||
|
|
||||||
// eslint-disable-next-line react-dom/no-dangerously-set-innerhtml
|
|
||||||
return <code className={styles.syntax} dangerouslySetInnerHTML={{ __html: htmlContent }} />
|
return <code className={styles.syntax} dangerouslySetInnerHTML={{ __html: htmlContent }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ function JSConsole({
|
|||||||
// 全局console拦截处理
|
// 全局console拦截处理
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interceptor = ConsoleInterceptor.getInstance()
|
const interceptor = ConsoleInterceptor.getInstance()
|
||||||
|
let scrollTimer: ReturnType<typeof setTimeout>
|
||||||
|
|
||||||
const handleGlobalConsole = (type: string, args: unknown[]) => {
|
const handleGlobalConsole = (type: string, args: unknown[]) => {
|
||||||
const content = args.map((arg) => formatResult(arg)).join(' ')
|
const content = args.map((arg) => formatResult(arg)).join(' ')
|
||||||
@@ -152,8 +153,8 @@ function JSConsole({
|
|||||||
|
|
||||||
setOutputs((prev) => [...prev, outputItem])
|
setOutputs((prev) => [...prev, outputItem])
|
||||||
|
|
||||||
// 自动滚动到底部
|
clearTimeout(scrollTimer)
|
||||||
setTimeout(() => {
|
scrollTimer = setTimeout(() => {
|
||||||
if (outputRef.current) {
|
if (outputRef.current) {
|
||||||
outputRef.current.scrollTop = outputRef.current.scrollHeight
|
outputRef.current.scrollTop = outputRef.current.scrollHeight
|
||||||
}
|
}
|
||||||
@@ -164,6 +165,7 @@ function JSConsole({
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
interceptor.unsubscribe(handleGlobalConsole)
|
interceptor.unsubscribe(handleGlobalConsole)
|
||||||
|
clearTimeout(scrollTimer)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@@ -9,25 +9,24 @@ const LanguageContext = createContext<{
|
|||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
export function LanguageProvider({ children }: { children: ReactNode }) {
|
export function LanguageProvider({ children }: { children: ReactNode }) {
|
||||||
const [language, setLang] = useState<Lang>(() => {
|
const [language, setLanguage] = useState<Lang>(() => {
|
||||||
const stored = localStorage.getItem('language') as Lang
|
const stored = localStorage.getItem('language') as Lang
|
||||||
if (stored === 'zh-CN' || stored === 'en-US') return stored
|
if (stored === 'zh-CN' || stored === 'en-US') return stored
|
||||||
return navigator.language.startsWith('zh') ? 'zh-CN' : 'en-US'
|
return navigator.language.startsWith('zh') ? 'zh-CN' : 'en-US'
|
||||||
})
|
})
|
||||||
|
|
||||||
const setLanguage = (lang: Lang) => {
|
const switchLanguage = (lang: Lang) => {
|
||||||
setLang(lang)
|
setLanguage(lang)
|
||||||
localStorage.setItem('language', lang)
|
localStorage.setItem('language', lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LanguageContext value={{ language, isZh: language === 'zh-CN', setLanguage }}>
|
<LanguageContext value={{ language, isZh: language === 'zh-CN', setLanguage: switchLanguage }}>
|
||||||
{children}
|
{children}
|
||||||
</LanguageContext>
|
</LanguageContext>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-refresh/only-export-components
|
|
||||||
export function useLanguage() {
|
export function useLanguage() {
|
||||||
const ctx = use(LanguageContext)
|
const ctx = use(LanguageContext)
|
||||||
if (!ctx) throw new Error('useLanguage must be used within LanguageProvider')
|
if (!ctx) throw new Error('useLanguage must be used within LanguageProvider')
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable react-dom/no-dangerously-set-innerhtml */
|
|
||||||
import type { PageAgent as PageAgentType } from 'page-agent'
|
import type { PageAgent as PageAgentType } from 'page-agent'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Link, useSearchParams } from 'wouter'
|
import { Link, useSearchParams } from 'wouter'
|
||||||
@@ -46,10 +45,11 @@ export default function HeroSection() {
|
|||||||
: 'Goto docs in navigation bar, find Quick-Start section, and summarize in markdown'
|
: 'Goto docs in navigation bar, find Quick-Start section, and summarize in markdown'
|
||||||
|
|
||||||
const [task, setTask] = useState(() => defaultTask)
|
const [task, setTask] = useState(() => defaultTask)
|
||||||
|
const [prevDefaultTask, setPrevDefaultTask] = useState(defaultTask)
|
||||||
useEffect(() => {
|
if (prevDefaultTask !== defaultTask) {
|
||||||
|
setPrevDefaultTask(defaultTask)
|
||||||
setTask(defaultTask)
|
setTask(defaultTask)
|
||||||
}, [defaultTask])
|
}
|
||||||
|
|
||||||
const [params] = useSearchParams()
|
const [params] = useSearchParams()
|
||||||
const isOther = params.has('try_other')
|
const isOther = params.has('try_other')
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ function ScrollToTop() {
|
|||||||
|
|
||||||
export default function Router() {
|
export default function Router() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const schedule = globalThis.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1))
|
if ('requestIdleCallback' in globalThis) {
|
||||||
const cancel = globalThis.cancelIdleCallback ?? clearTimeout
|
const id = requestIdleCallback(() => docsImport())
|
||||||
const id = schedule(() => docsImport())
|
return () => cancelIdleCallback(id)
|
||||||
return () => cancel(id)
|
}
|
||||||
|
const id = setTimeout(() => docsImport(), 1)
|
||||||
|
return () => clearTimeout(id)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user