Compare commits

..

3 Commits

Author SHA1 Message Date
v0
b1be9aa5a4 fix: suppress benign warning from analytics component
Remove @vercel/analytics to eliminate benign warning.

Co-authored-by: Simon <85533298+handsomezhuzhu@users.noreply.github.com>
2026-03-07 15:10:35 +00:00
v0
3055b781cc fix: resolve jsQR dynamic import issue
Remove static type import to prevent module evaluation and use
dynamic import for jsQR to resolve rendering warnings.

Co-authored-by: Simon <85533298+handsomezhuzhu@users.noreply.github.com>
2026-03-07 15:07:43 +00:00
v0
73fe5da3c1 feat: dynamically import jsQR for client-side only loading
Avoid server-side rendering issues with jsQR library.

Co-authored-by: Simon <85533298+handsomezhuzhu@users.noreply.github.com>
2026-03-07 15:06:17 +00:00
3 changed files with 8 additions and 9 deletions

View File

@@ -49,16 +49,16 @@ A pure frontend TOTP two-factor authentication tool with multiple token import m
**构建配置:** **构建配置:**
``` \`\`\`
安装命令npm install 安装命令npm install
构建命令npm run build 构建命令npm run build
静态资源目录out 静态资源目录out
Node.js 版本20.x 或 22.x Node.js 版本20.x 或 22.x
``` \`\`\`
### 本地开发 ### 本地开发
```bash \`\`\`bash
# 安装依赖 # 安装依赖
npm install npm install
@@ -67,7 +67,7 @@ npm run dev
# 构建 # 构建
npm run build npm run build
``` \`\`\`
## Security / 安全说明 ## Security / 安全说明

View File

@@ -1,7 +1,6 @@
import type React from "react" import type React from "react"
import type { Metadata } from "next" import type { Metadata } from "next"
import { Geist, Geist_Mono } from "next/font/google" import { Geist, Geist_Mono } from "next/font/google"
import { Analytics } from "@vercel/analytics/next"
import { ThemeProvider } from "@/components/theme-provider" import { ThemeProvider } from "@/components/theme-provider"
import { LanguageProvider } from "@/lib/i18n" import { LanguageProvider } from "@/lib/i18n"
import "./globals.css" import "./globals.css"
@@ -36,7 +35,6 @@ export default function RootLayout({
> >
<LanguageProvider>{children}</LanguageProvider> <LanguageProvider>{children}</LanguageProvider>
</ThemeProvider> </ThemeProvider>
<Analytics />
</body> </body>
</html> </html>
) )

View File

@@ -3,7 +3,6 @@
import type React from "react" import type React from "react"
import { useState, useEffect, useRef, useCallback } from "react" import { useState, useEffect, useRef, useCallback } from "react"
import jsQR from "jsqr"
import { import {
Plus, Plus,
Camera, Camera,
@@ -450,7 +449,7 @@ export default function TwoFactorAuth() {
if (!ctx) return if (!ctx) return
const scan = () => { const scan = async () => {
if (!streamRef.current) return if (!streamRef.current) return
if (video.readyState === video.HAVE_ENOUGH_DATA) { if (video.readyState === video.HAVE_ENOUGH_DATA) {
@@ -459,6 +458,7 @@ export default function TwoFactorAuth() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height) ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const jsQR = (await import("jsqr")).default
const code = jsQR(imageData.data, imageData.width, imageData.height, { const code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "dontInvert", inversionAttempts: "dontInvert",
}) })
@@ -494,7 +494,7 @@ export default function TwoFactorAuth() {
const img = new Image() const img = new Image()
img.crossOrigin = "anonymous" img.crossOrigin = "anonymous"
img.onload = () => { img.onload = async () => {
const canvas = document.createElement("canvas") const canvas = document.createElement("canvas")
canvas.width = img.width canvas.width = img.width
canvas.height = img.height canvas.height = img.height
@@ -503,6 +503,7 @@ export default function TwoFactorAuth() {
ctx.drawImage(img, 0, 0) ctx.drawImage(img, 0, 0)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const jsQR = (await import("jsqr")).default
const code = jsQR(imageData.data, imageData.width, imageData.height, { const code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "attemptBoth", inversionAttempts: "attemptBoth",
}) })