diff --git a/app.db b/app.db new file mode 100644 index 0000000..dca7027 Binary files /dev/null and b/app.db differ diff --git a/app.go b/app.go index c4ec5f7..3d8a6f9 100644 --- a/app.go +++ b/app.go @@ -7,45 +7,36 @@ import ( "os" "path/filepath" "time" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" ) -// App struct -type App struct { - ctx context.Context -} - -// NewApp creates a new App application struct func NewApp() *App { return &App{} } -// startup is called when the app starts. The context is saved -// so we can call the runtime methods func (a *App) startup(ctx context.Context) { a.ctx = ctx } -// Greet returns a greeting for the given name - func (a *App) Greet(name string) string { return fmt.Sprintf("Hello %s, It's show time!", name) } -func (a *App) Login() string { - println("1111") - return "" +func (a *App) GormDB() (*gorm.DB, error) { + db, err := gorm.Open(sqlite.Open("app.db"), &gorm.Config{}) + if err != nil { + return nil, err + } + return db, nil } -func (a *App) Register() string { - println("1111") - return "" -} - -func (a *App) UploadImage(data []byte, filename string) ImageResult { +func (a *App) UploadImage(data []byte, filename string) Response { uploadsDir := "./uploads" err := os.MkdirAll(uploadsDir, 0755) if err != nil { - return ImageResult{Code: 1, Message: "failed", Data: fmt.Sprintf("创建目录失败: %v", err)} + return Response{Code: 1, Message: "failed", Data: err.Error()} } ext := filepath.Ext(filename) @@ -54,26 +45,97 @@ func (a *App) UploadImage(data []byte, filename string) ImageResult { err = os.WriteFile(filePath, data, 0644) if err != nil { - return ImageResult{Code: 1, Message: "failed", Data: fmt.Sprintf("保存文件失败: %v", err)} + return Response{Code: 1, Message: "failed", Data: err.Error()} } - return ImageResult{Code: 0, Message: "success", Data: newFilename} + return Response{Code: 0, Message: "success", Data: newFilename} } -func (a *App) GetImage(filename string) ImageResult { +func (a *App) GetImage(filename string) Response { filePath := filepath.Join("./uploads", filename) - fmt.Println("filePath", filePath) + data, err := os.ReadFile(filePath) if err != nil { - return ImageResult{Code: 1, Message: "failed", Data: fmt.Sprintf("打开文件路径失败: %v", err)} + return Response{Code: 1, Message: "failed", Data: err.Error()} } + ext := filepath.Ext(filename) mimeType := "image/jpeg" if ext == ".png" { mimeType = "image/png" } - // app.go - GetImage - return ImageResult{Code: 0, Message: "success", Data: fmt.Sprintf("data:image/%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(data))} - // return ImageResult{Code: 1, Message: "failed", Data: data} + base64Data := base64.StdEncoding.EncodeToString(data) + dataURL := "data:image/" + mimeType + ";base64," + base64Data + + return Response{Code: 0, Message: "success", Data: dataURL} +} + +func (a *App) GetHistory(page int, pageSize int) Response { + db, err := a.GormDB() + if err != nil { + return Response{Code: 1, Message: err.Error()} + } + + var total int64 + db.Table("history_test").Count(&total) + + var historyList []HistoryWithBreed + err = db.Table("history_test").Select("history_test.*, breeds_test.brief, breeds_test.name"). + Joins("LEFT JOIN breeds_test ON history_test.breed = breeds_test.id"). + Order("history_test.id DESC").Limit(pageSize).Offset((page - 1) * pageSize).Find(&historyList).Error + + if err != nil { + return Response{Code: 1, Message: err.Error()} + } + + historyData := HistoryData{Page: page, PageSize: pageSize, Total: total, List: historyList} + + return Response{Code: 0, Message: "success", Data: historyData} +} + +func (a *App) Detect(img string) Response { + db, err := a.GormDB() + if err != nil { + return Response{Code: 1, Message: err.Error()} + } + + var breed Breed + detectRet := "british_shorthair" + err = db.Table("breeds_test").Where("code = ?", detectRet).First(&breed).Error + if err != nil { + return Response{Code: 1, Message: err.Error()} + } + + now := int(time.Now().Unix()) + + one := HistoryItem{Img: img, Breed: int(breed.Id), Date: now} + result := db.Table("history_test").Create(&one) + if result.Error != nil { + return Response{Code: 1, Message: result.Error.Error()} + } + + detectData := DetectData{ + Id: breed.Id, + Code: breed.Code, + Name: breed.Name, + Brief: breed.Brief, + ConfidenceLevel: 0.98, + } + + return Response{Code: 0, Message: "success", Data: detectData} +} + +func (a *App) DeleteOneHistory(id uint) Response { + db, err := a.GormDB() + if err != nil { + return Response{Code: 1, Message: err.Error()} + } + + result := db.Table("history_test").Delete(&HistoryItem{}, id) + if result.Error != nil { + return Response{Code: 1, Message: result.Error.Error()} + } + + return Response{Code: 0, Message: "success"} } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bc7cd16..a1e6633 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,6 +14,8 @@ }, "devDependencies": { "@preact/preset-vite": "^2.3.0", + "sharp": "^0.35.3", + "to-ico": "^1.1.5", "typescript": "^4.6.4", "vite": "^8.1.4" } @@ -345,6 +347,533 @@ "tslib": "^2.4.0" } }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1158,6 +1687,77 @@ "dev": true, "license": "MIT" }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, "node_modules/babel-plugin-transform-hook-names": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", @@ -1181,6 +1781,33 @@ "node": ">=6.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bignumber.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bmp-js": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.1.tgz", + "integrity": "sha512-OS74Rlt0Aynu2mTPmY9RZOUOXlqWecFIILFXr70vv16/xCZnFxvri9IKkF1IGxQ8r9dOE62qGNpKxXx8Lko8bg==", + "dev": true, + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1223,6 +1850,41 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/caniuse-lite": { "version": "1.0.30001799", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", @@ -1244,6 +1906,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/centra": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", + "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6" + } + }, "node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", @@ -1259,6 +1938,19 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1266,6 +1958,13 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", @@ -1296,6 +1995,19 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1314,6 +2026,16 @@ } } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1339,6 +2061,12 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -1383,6 +2111,17 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.378", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.378.tgz", @@ -1403,6 +2142,13 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1420,6 +2166,43 @@ "dev": true, "license": "MIT" }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1438,6 +2221,62 @@ } } }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1463,6 +2302,66 @@ "node": ">=6.9.0" } }, + "node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1473,12 +2372,51 @@ "he": "bin/he" } }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/immutable": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "license": "MIT" }, + "node_modules/ip-regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", + "integrity": "sha512-HjpCHTuxbR/6jWJroc/VN+npo5j0T4Vv2TAI5qdEHQx7hsL767MeccGFSsLtF694EiZKTSEqgoeU6DtGFCcuqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1489,6 +2427,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1502,6 +2447,66 @@ "node": ">=0.10.0" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jimp": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", + "integrity": "sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^2.1.0", + "bmp-js": "0.0.3", + "es6-promise": "^3.0.2", + "exif-parser": "^0.1.9", + "file-type": "^3.1.0", + "jpeg-js": "^0.2.0", + "load-bmfont": "^1.2.3", + "mime": "^1.3.4", + "mkdirp": "0.5.1", + "pixelmatch": "^4.0.0", + "pngjs": "^3.0.0", + "read-chunk": "^1.0.1", + "request": "^2.65.0", + "stream-to-buffer": "^0.1.0", + "tinycolor2": "^1.1.2", + "url-regex": "^3.0.0" + } + }, + "node_modules/jimp/node_modules/bmp-js": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", + "integrity": "sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jimp/node_modules/jpeg-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", + "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/jpeg-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.1.2.tgz", + "integrity": "sha512-CiRVjMKBUp6VYtGwzRjrdnro41yMwKGOWdP9ATXqmixdz2n7KHNwdqthTYSSbOKj/Ha79Gct1sA8ZQpse55TYA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1509,6 +2514,13 @@ "dev": true, "license": "MIT" }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -1522,6 +2534,27 @@ "node": ">=6" } }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -1535,6 +2568,22 @@ "node": ">=6" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/kolorist": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", @@ -1803,6 +2852,23 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/load-bmfont": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", + "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^3.7.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -1823,6 +2889,73 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-document": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1890,6 +3023,92 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-png": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-1.1.2.tgz", + "integrity": "sha512-Ge6gDV9T5zhkWHmjvnNiyhPTCIoY7W+FC7qWPtuL2lIGZAFxxqTRG/ouEXsH9qkw+HzYiPEU/tFcxOCEDTP1Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pngjs": "^3.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true, + "license": "MIT" + }, + "node_modules/phin": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", + "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "dependencies": { + "centra": "^2.7.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1910,6 +3129,52 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^3.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/postcss": { "version": "8.5.16", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", @@ -1959,6 +3224,59 @@ "preact": ">=10" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", + "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/read-chunk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz", + "integrity": "sha512-5NLTTdX45dKFtG8CX5pKmvS9V5u9wBE+gkklN7xhDuhq3pA2I4O7ALfKxosCMcLHOhkxj6GNacZhfXtp5nlCdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", @@ -1972,6 +3290,57 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/resize-img": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/resize-img/-/resize-img-1.1.2.tgz", + "integrity": "sha512-/4nKUmuNPuM6gYTWad136ica81baOVjpesgv8FGaIvP0KWcbCWahOWBKaM4tFoM+aVcSA+qQDg28pcnIzFRpJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bmp-js": "0.0.1", + "file-type": "^3.8.0", + "get-stream": "^2.0.0", + "jimp": "^0.2.21", + "jpeg-js": "^0.1.1", + "parse-png": "^1.1.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/rolldown": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", @@ -2006,6 +3375,34 @@ "@rolldown/binding-win32-x64-msvc": "1.1.5" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/sass": { "version": "1.101.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", @@ -2027,6 +3424,16 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2037,6 +3444,69 @@ "semver": "bin/semver.js" } }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/simple-code-frame": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/simple-code-frame/-/simple-code-frame-1.3.0.tgz", @@ -2066,6 +3536,32 @@ "node": ">=0.10.0" } }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stack-trace": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0.tgz", @@ -2076,6 +3572,36 @@ "node": ">=20.0.0" } }, + "node_modules/stream-to": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz", + "integrity": "sha512-Kg1BSDTwgGiVMtTCJNlo7kk/xzL33ZuZveEBRt6rXw+f1WLK/8kmz2NVCT/Qnv0JkV85JOHcLhD82mnXsR3kPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-to-buffer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz", + "integrity": "sha512-Da4WoKaZyu3nf+bIdIifh7IPkFjARBnBK+pYqn0EUJqksjV9afojjaCCHUemH30Jmu7T2qcKvlZm2ykN38uzaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "stream-to": "~0.2.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -2093,6 +3619,37 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/to-ico": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/to-ico/-/to-ico-1.1.5.tgz", + "integrity": "sha512-5kIh7m7bkIlqIESEZkL8gAMMzucXKfPe3hX2FoDY5HEAfD9OJU+Qh9b6Enp74w0qRcxVT5ejss66PHKqc3AVkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "buffer-alloc": "^1.1.0", + "image-size": "^0.5.0", + "parse-png": "^1.0.0", + "resize-img": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -2101,6 +3658,26 @@ "license": "0BSD", "optional": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -2146,6 +3723,55 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-regex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", + "integrity": "sha512-dQ9cJzMou5OKr6ZzfvwJkCq3rC72PNXhqz0v3EIhF4a3Np+ujr100AhUx2cKx5ei3iymoJpJrPB3sVSEMdqAeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/vite": { "version": "8.1.4", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz", @@ -2225,6 +3851,60 @@ } } }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 537f9a7..cd6e39d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,8 @@ }, "devDependencies": { "@preact/preset-vite": "^2.3.0", + "sharp": "^0.35.3", + "to-ico": "^1.1.5", "typescript": "^4.6.4", "vite": "^8.1.4" } diff --git a/frontend/src/assets/fonts/HARMONYOS_SANS_SC_BOLD.TTF b/frontend/src/assets/fonts/HARMONYOS_SANS_SC_BOLD.TTF new file mode 100644 index 0000000..55b75ad Binary files /dev/null and b/frontend/src/assets/fonts/HARMONYOS_SANS_SC_BOLD.TTF differ diff --git a/frontend/src/assets/fonts/HARMONYOS_SANS_SC_REGULAR.TTF b/frontend/src/assets/fonts/HARMONYOS_SANS_SC_REGULAR.TTF new file mode 100644 index 0000000..95b29bc Binary files /dev/null and b/frontend/src/assets/fonts/HARMONYOS_SANS_SC_REGULAR.TTF differ diff --git a/frontend/src/assets/menu-icon.ico b/frontend/src/assets/menu-icon.ico new file mode 100644 index 0000000..0ffa881 Binary files /dev/null and b/frontend/src/assets/menu-icon.ico differ diff --git a/frontend/src/assets/menu-icon.svg b/frontend/src/assets/menu-icon.svg new file mode 100644 index 0000000..ea18415 --- /dev/null +++ b/frontend/src/assets/menu-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx deleted file mode 100644 index f181e02..0000000 --- a/frontend/src/components/Dashboard.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { route } from "preact-router" - -const Dashboard = () => { - return ( -
-
- { e.preventDefault(); route("/dashboard/datasets") }}>训练集管理 - { e.preventDefault(); route("/dashboard/testsets") }}>测试集管理 - { e.preventDefault(); route("/dashboard/logs") }}>训练日志 -
-
- {/* 子页面内容通过 Router 渲染到这里 */} -
-
- ) -} - -export default Dashboard \ No newline at end of file diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx index cd8c5d0..7147586 100644 --- a/frontend/src/components/Footer.tsx +++ b/frontend/src/components/Footer.tsx @@ -1,11 +1,9 @@ -export const Footer = () => { - return ( - - ) -} \ No newline at end of file +export const Footer = () => ( + +) diff --git a/frontend/src/components/History.tsx b/frontend/src/components/History.tsx index 4e3e3a3..7d66773 100644 --- a/frontend/src/components/History.tsx +++ b/frontend/src/components/History.tsx @@ -1,82 +1,95 @@ import { useState, useEffect } from 'preact/hooks' +import { message } from '../utils/toast' import Pagination from './Pagination' +const formatDate = (timestamp: number) => { + const date = new Date(timestamp * 1000) + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }) +} -const historyList = [ - { - id: 1, - breeds: '英短&1212', - desc: 'The Maine Coon is a large, friendly cat breed known for its tufted ears.', - img: '../../', - date: 'Jan 15, 2024', - }, - { - id: 2, - breeds: '英短&fdfd', - desc: 'The Maine Coon is a large, friendly cat breed known for its tufted ears.', - img: '../../', - date: 'Jan 15, 2024', - }, - { - id: 3, - breeds: '英短&11111', - desc: 'The Maine Coon is a large, friendly cat breed known for its tufted ears.', - img: '../../', - date: 'Jan 15, 2024', - }, - { - id: 4, - breeds: '英短&4545', - desc: 'The Maine Coon is a large, friendly cat breed known for its tufted ears.', - img: '../../', - date: 'Jan 15, 2024', - }, - { - id: 5, - breeds: '英短&fff', - desc: 'The Maine Coon is a large, friendly cat breed known for its tufted ears.', - img: '../../', - date: 'Jan 15, 2024', - }, -] +interface IHistoryItem { + id: number + img: string + breed: number + date: number + name: string + brief: string +} const History = () => { const [page, setPage] = useState(1) + const pageSize = 5 const [total, setTotal] = useState(50) + const [historyList, setHistoryList] = useState([]) + useEffect(() => { + if (!(window as any).go?.main?.App?.GetHistory) { + message.error('Wails runtime not ready') + return + } + fetchData(page) + }, []) - const handlePageChange = (page: number) => { - // setPage(page) - // loadTranscript(currentNav, currentOpt, page).then(res => { - // if (res && res.code === 0) { - // setData(res.data.data) - // setTotal(res.data.total) - // } - // }).catch(e => message.error(e.toString())) + const fetchData = async(page: number) => { + const result = await (window as any).go.main.App.GetHistory(page, pageSize) + if (result.code === 0) { + setHistoryList(result.data.list) + setTotal(result.data.total) + } else if (result.code === 1) { + message.error(result.message) + } } + const handlePageChange = (page: number) => { + setPage(page) + fetchData(page) + } + + const handleDelete = async(id: number) => { + const result = await (window as any).go.main.App.DeleteOneHistory(id) + if (result.code === 0) { + message.success('删除成功') + fetchData(page) + } else if (result.code === 1) { + message.error(result.message) + } + } return (
- {historyList.map((item) => + + {historyList.map((item: IHistoryItem) =>
-

{item.breeds}

- {item.date} +

{item.name}

+
+ {formatDate(item.date)} + +
- {item.desc} + {item.brief}
) } -
- +
) diff --git a/frontend/src/components/Main.tsx b/frontend/src/components/Main.tsx index cd8279d..33db2dd 100644 --- a/frontend/src/components/Main.tsx +++ b/frontend/src/components/Main.tsx @@ -1,16 +1,22 @@ -import { useState, useEffect, useRef } from 'preact/hooks' +import { useState, useRef } from 'preact/hooks' +import { message } from '../utils/toast' +interface IDetectResult { + id: number + code: string + name: number + brief: string + confidence_level: number +} const Main = () => { const fileInputRef = useRef(null) const [fileName, setFileName] = useState('') const [fileSrc, setFileSrc] = useState('') + const [step, setStep] = useState(0) + const [detectResult, setDetectResult] = useState(null) - const [step, setStep] = useState(0) - - const handleUploadClick = () => { - fileInputRef.current?.click() - } + const handleUploadClick = () => fileInputRef.current?.click() const handleFileChange = (e: Event) => { const target = e.target as HTMLInputElement @@ -56,13 +62,20 @@ const Main = () => { const handleDetect = () => { setStep(2) - setTimeout(() => { - setStep(3) + setTimeout(async() => { + const result = await (window as any).go.main.App.Detect(fileName) + if (result.code === 0) { + setDetectResult(result.data) + setStep(3) + } else if (result.code === 1) { + message.error(result.message) + } }, 1000) } const handleTryOther = () => { setStep(0) + setDetectResult(null) resetUpload() } @@ -74,9 +87,14 @@ const Main = () => { 发现分类

- 上传一张猫咪照片,让AI不仅认出它是什么 + 上传一张猫咪照片,让AI认出它是什么

- 基于深度残差网络 + + + + + 基于深度残差网络 +
{ }
{fileSrc.length > 0 && step == 1 ? -
-

异国短毛猫

+

{detectResult?.name}

- 可信度 78% + 置信度 {detectResult ? detectResult.confidence_level * 100 + '%' : ''}
-

关于英短

-

尽量快点附近的路口附近的联发科九点零分肌肤的路口附近登陆反对浪费空间 尽量快点附近的路口附近的联发科九点零分肌肤的路口附近登陆反对浪费空间

+

{detectResult?.brief}

- - {/*
-
-
- - - -
-

Lightning Fast

-

Get results in under 3 seconds with our optimized AI model

-
- -
-
- - - -
-

98% Accuracy

-

Trained on 50,000+ cat images across 60+ breeds

-
- -
-
- - - -
-

Always Free

-

No hidden fees, no subscriptions. Detect away!

-
-
*/} ) diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index 0d0f8ab..3eac2b3 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -1,7 +1,7 @@ export const Navbar = () => { const svgPath = "M12 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-4.5 4c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm9 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-9.5 3.5c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zm11 0c-1.38 0-2.5 1.12-2.5 2.5s1.12 2.5 2.5 2.5 2.5-1.12 2.5-2.5-1.12-2.5-2.5-2.5zM12 18c-2.21 0-4 1.79-4 4h8c0-2.21-1.79-4-4-4z" - const handleClick = () => window.open('https://git.leonstack.com/Leon/Collage-Images', '_blank') + const handleClick = () => window.open('https://git.leonstack.com/owner/Collage-Images', '_blank') return ( diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx index 09df331..50daefd 100644 --- a/frontend/src/components/Pagination.tsx +++ b/frontend/src/components/Pagination.tsx @@ -45,14 +45,17 @@ const Pagination = ({ const handleEnterPress = (e: KeyboardEvent) => { if (e.key === 'Enter') { - if (parseInt(value) <= seqNums.length && parseInt(value) > 0) { - handleChangeNum(parseInt(value)) - } else if (parseInt(value) >= seqNums.length) { + const target = e.target as HTMLInputElement + const page = parseInt(target.value) + if (page >= 1 && page <= seqNums.length) { + handleChangeNum(page) + onChange(page) + } else if (page > seqNums.length) { handleChangeNum(seqNums.length) - } else if (parseInt(value) < seqNums.length) { - handleChangeNum(1) + onChange(seqNums.length) } else { handleChangeNum(1) + onChange(1) } } } diff --git a/frontend/src/components/TestsetManagement.tsx b/frontend/src/components/TestsetManagement.tsx deleted file mode 100644 index 64f5de1..0000000 --- a/frontend/src/components/TestsetManagement.tsx +++ /dev/null @@ -1,7 +0,0 @@ -const TestsetManagement = () => { - return ( -
- ) -} - -export default TestsetManagement \ No newline at end of file diff --git a/frontend/src/components/TrainsetManagement.tsx b/frontend/src/components/TrainsetManagement.tsx deleted file mode 100644 index 0de7db9..0000000 --- a/frontend/src/components/TrainsetManagement.tsx +++ /dev/null @@ -1,7 +0,0 @@ -const TrainsetManagement = () => { - return ( -
- ) -} - -export default TrainsetManagement \ No newline at end of file diff --git a/frontend/src/pages/App.tsx b/frontend/src/pages/App.tsx index 181b39a..c7fc3dc 100644 --- a/frontend/src/pages/App.tsx +++ b/frontend/src/pages/App.tsx @@ -2,26 +2,18 @@ import { Router, Route } from "preact-router" import { Navbar } from "../components/Navbar" import Main from "../components/Main" import History from "../components/History" -import Dashboard from "../components/Dashboard" -import TrainsetManagement from "../components/TrainsetManagement" -import TestsetManagement from "../components/TestsetManagement" import { Footer } from "../components/Footer" import '../styles/base.sass' import '../styles/history.sass' import '../styles/pagination.sass' -import '../styles/dashboard.sass' - -export function App(props: any) { +export function App() { return (
- - -
diff --git a/frontend/src/styles/base.sass b/frontend/src/styles/base.sass index 4f0457c..3fe644c 100644 --- a/frontend/src/styles/base.sass +++ b/frontend/src/styles/base.sass @@ -6,6 +6,7 @@ body margin: 0 color: white font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI" + overflow: hidden @font-face font-family: "Nunito" @@ -17,11 +18,16 @@ body font-family: "ZcoolHappy" src: url("../assets/fonts/站酷快乐体.ttf") format("truetype") +@font-face + font-family: "SANS_SC_BOLD" + src: url("../assets/fonts/HARMONYOS_SANS_SC_BOLD.TTF") format("truetype") + :root --primary: #3B82F6 --title: #1e293b - --text:#5f6874 - --textsub: #878d95 + --sub-title: #5f6874 + --text:#5D5D5D + --textsub: #979797 #app height: 100vh @@ -41,7 +47,7 @@ body width: 100% margin: 0 auto padding: 0.6rem 1.2rem - background-color: #FFFFFF + background-color: white backdrop-filter: blur(8px) border-radius: 0.6rem box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1) @@ -77,7 +83,7 @@ body width: 7rem height: 2.6rem margin-left: 1.5rem - color: #FFFFFF + color: white font-size: 0.9375rem background-color: var(--primary) border-radius: 0.5rem @@ -113,7 +119,7 @@ body > p text-align: center - color: var(--text) + color: var(--sub-title) font-size: 1.0625rem margin-bottom: 1rem max-width: 42rem @@ -123,6 +129,8 @@ body color: var(--primary) > i + display: flex + align-items: center font-style: normal text-align: center color: #FFD572 @@ -130,7 +138,12 @@ body margin: 0 auto padding: 0.25rem 0.75rem background-color: #38383D - border-radius: 0.2rem + border-radius: 0.25rem + gap: 0.25rem + + > svg + width: 0.625rem + height: 0.625rem .upload width: 32rem @@ -147,7 +160,6 @@ body border-width: 2px border-style: dashed border-color: #e2e8f0 - border-style: dashed text-align: center cursor: pointer @@ -175,8 +187,7 @@ body > img max-height: 10.5rem - margin-left: auto - margin-right: auto + margin: 0 auto margin-bottom: 0.25rem border-radius: 1rem object-fit: cover @@ -195,7 +206,7 @@ body .zone-loading padding: 2rem text-align: center - background-color: #FFFFFF + background-color: white border-radius: 1.5rem .loading-spinner @@ -223,25 +234,26 @@ body font-size: 1rem color: var(--textsub) - >button#detectBtn - width: 12rem - height: 3.5rem + > button + display: flex + align-items: center + justify-content: center + width: 9rem + height: 3rem margin: 1rem auto 0 auto background-color: var(--primary) color: white font-weight: 600 font-size: 1.125rem - border-radius: 0.75rem + border-radius: 0.5rem transition: background-color 200ms, color 200ms cursor: pointer - display: flex - align-items: center - justify-content: center - gap: 0.5rem + + gap: 0.25rem > svg - width: 1.5rem - height: 1.5rem + width: 1.25rem + height: 1.25rem .result width: 35rem @@ -251,6 +263,7 @@ body display: flex align-items: center justify-content: center + height: 2.25rem gap: 0.75rem margin-bottom: 1.5rem @@ -260,8 +273,8 @@ body color: var(--primary) > h2 - font-family: "ZcoolHappy" - font-weight: 700 + font-family: "SANS_SC_BOLD" + font-weight: bold font-size: 1.5rem color: var(--title) @@ -270,7 +283,7 @@ body min-height: 10rem padding: 1rem 1rem margin-bottom: 1.5rem - background-color: #ffffff + background-color: white border-radius: 1rem border: 1px solid var(--color-border) gap: 2rem @@ -293,7 +306,7 @@ body justify-content: space-between align-items: center width: 100% - padding-bottom: 1rem + padding-bottom: 0.75rem margin-bottom: 1rem border-bottom: 1px solid #c6c6c6 @@ -333,14 +346,6 @@ body > span color: var(--title) - // font-size: 0.725rem - - > h4 - color: var(--title) - text-align: left - font-size: 1rem - font-weight: bold - margin-bottom: 0.5rem > p color: var(--text) @@ -348,17 +353,24 @@ body font-size: 0.875rem > button + display: flex + align-items: center + justify-content: center width: 9rem height: 3rem - background-color: var(--primary) + margin: 0 auto color: white font-weight: 600 font-size: 1.125rem + background-color: var(--primary) border-radius: 0.5rem transition: background-color 200ms, color 200ms cursor: pointer + gap: 0.25rem - + > svg + width: 1.25rem + height: 1.25rem .app-footer display: flex @@ -375,56 +387,44 @@ body font-size: 0.8rem margin: 0 0.5rem -@media (prefers-reduced-motion: reduce) - .animate-fade-in, - .animate-slide-up, - .animate-pulse-slow - animation: none !important - transition: none !important +.message + width: 100% + height: 2rem + position: fixed + top: 6.25rem + z-index: 1000 + background: none + -moz-transition: all 0.5s ease-in + -webkit-transition: all 0.5s ease-in + -o-transition: all 0.5s ease-in + transition: all 0.5s ease-in -@keyframes fadeIn - from - opacity: 0 - to - opacity: 1 + .success, .error + display: inline-flex + justify-content: center + align-items: center + height: 2rem + margin: 0 auto + padding: 0 1.125rem + font-size: 0.875rem + background: white + border: 1px solid var(--border) + border-radius: 0.375rem + box-shadow: 0rem 0.25rem 0.25rem rgb(0 0 0 / 0.1) + gap: 0.25rem -@keyframes slideUp - from - opacity: 0 - transform: translateY(20px) - to - opacity: 1 - transform: translateY(0) + > span + width: 1rem + height: 1rem -@keyframes pulseSlow - 0%, 100% - opacity: 1 - 50% - opacity: 0.7 + > svg + width: 1rem + height: 1rem + line-height: 2rem -.animate-fade-in - animation: fadeIn 0.3s ease-out forwards - -.animate-slide-up - animation: slideUp 0.4s ease-out forwards - -.animate-pulse-slow - animation: pulseSlow 2s ease-in-out infinite - -.drop-zone - transition: all 0.2s ease-out - -.drop-zone.dragover - border-color: #3B82F6 - background-color: #EFF6FF - transform: scale(1.01) - -.breed-card - transition: all 0.2s ease-out - - &:hover - transform: translateY(-4px) - box-shadow: 0 12px 24px -8px rgba(59, 130, 246, 0.25) - -.confidence-bar - transition: width 0.8s ease-out + > p + max-width: 24rem + color: var(--text) + overflow: hidden + white-space: nowrap + text-overflow: ellipsis \ No newline at end of file diff --git a/frontend/src/styles/dashboard.sass b/frontend/src/styles/dashboard.sass deleted file mode 100644 index 038afbc..0000000 --- a/frontend/src/styles/dashboard.sass +++ /dev/null @@ -1,26 +0,0 @@ -.app-dashboard - display: flex - height: 648px - padding: 7rem 1.5rem 4rem 1.5rem - - .dashboard-sidebar - display: flex - flex-direction: column - align-items: center - width: 18% - height: 100% - border-right: 1px solid #9AAAC6 - - > a - display: inline-flex - height: 2.5rem - line-height: 2.5rem - font-size: 0.9375rem - color: #8E8E8E - border-bottom: 1px solid #9AAAC6 - cursor: pointer - - .dashboard-content - width: 82% - height: 100% - // background-color: #555 \ No newline at end of file diff --git a/frontend/src/styles/history.sass b/frontend/src/styles/history.sass index 35619a0..29c1829 100644 --- a/frontend/src/styles/history.sass +++ b/frontend/src/styles/history.sass @@ -3,7 +3,18 @@ flex-direction: column align-items: center height: 651px - padding: 7rem 1rem 4rem 1rem + padding: 6.5rem 1rem 4rem 1rem + + .history-clear + width: calc(100% - 20rem) + text-align: right + margin-bottom: 0.5rem + + > a + font-size: 0.875rem + color: var(--primary) + cursor: pointer + // text-decoration: underline .history-card display: flex @@ -33,15 +44,39 @@ display: flex align-items: center justify-content: space-between + gap: 1rem > h3 + width: 6rem color: #333333 font-size: 1rem font-weight: bold + text-align: left - > span - color: #cccccc - font-size: 0.8rem + .card-actions + display: flex + flex: 1 + justify-content: space-between + align-items: flex-start + gap: 0.75rem + + > span + color: #cccccc + font-size: 0.8rem + + > button + display: flex + align-items: center + justify-content: center + width: 1rem + height: 1rem + font-size: 1rem + padding: 0 + background: none + border: none + border-radius: 0.35rem + color: #9ca3af + cursor: pointer .right-overview width: 100% @@ -49,9 +84,12 @@ color: #cccccc font-size: 0.9rem text-align: left + overflow: hidden + text-overflow: ellipsis + white-space: nowrap .history-pagination width: calc(100% - 20rem) - margin-top: 1rem + margin-top: 0.5rem diff --git a/frontend/src/styles/pagination.sass b/frontend/src/styles/pagination.sass index 283df3c..bad9d85 100644 --- a/frontend/src/styles/pagination.sass +++ b/frontend/src/styles/pagination.sass @@ -59,7 +59,6 @@ .pagination-word font-size: 0.75rem - font-weight: bold margin: 0 0.3125rem 0 0.3125rem color: #333333 diff --git a/frontend/src/utils/toast.ts b/frontend/src/utils/toast.ts new file mode 100644 index 0000000..2e07d99 --- /dev/null +++ b/frontend/src/utils/toast.ts @@ -0,0 +1,53 @@ +interface MessageAPI { + success: (text: string) => void + error: (text: string) => void +} + +export const message: MessageAPI = { + success: (text: string) => { + const div = document.createElement('div') + div.className = 'message' + + const subDiv = document.createElement('div') + subDiv.className = 'success' + + const subSpan = document.createElement('span') + subSpan.innerHTML = `` + + const subP = document.createElement('p') + subP.innerText = text + + subDiv.appendChild(subSpan) + subDiv.appendChild(subP) + + div.appendChild(subDiv) + document.body.appendChild(div) + + setTimeout(() => { + div.remove() + }, 3000) + }, + error: (text: string) => { + const div = document.createElement('div') + div.className = 'message' + + const subDiv = document.createElement('div') + subDiv.className = 'error' + + const subSpan = document.createElement('span') + subSpan.innerHTML = `` + + const subP = document.createElement('p') + subP.innerText = text + + subDiv.appendChild(subSpan) + subDiv.appendChild(subP) + + div.appendChild(subDiv) + document.body.appendChild(div) + + setTimeout(() => { + div.remove() + }, 3000) + } +} diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 7dea2e2..0898305 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -1,13 +1,18 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT import {main} from '../models'; +import {gorm} from '../models'; -export function GetImage(arg1:string):Promise; +export function DeleteOneHistory(arg1:number):Promise; + +export function Detect(arg1:string):Promise; + +export function GetHistory(arg1:number,arg2:number):Promise; + +export function GetImage(arg1:string):Promise; + +export function GormDB():Promise; export function Greet(arg1:string):Promise; -export function Login():Promise; - -export function Register():Promise; - -export function UploadImage(arg1:Array,arg2:string):Promise; +export function UploadImage(arg1:Array,arg2:string):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index 92d2332..af0f25a 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -2,22 +2,30 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function DeleteOneHistory(arg1) { + return window['go']['main']['App']['DeleteOneHistory'](arg1); +} + +export function Detect(arg1) { + return window['go']['main']['App']['Detect'](arg1); +} + +export function GetHistory(arg1, arg2) { + return window['go']['main']['App']['GetHistory'](arg1, arg2); +} + export function GetImage(arg1) { return window['go']['main']['App']['GetImage'](arg1); } +export function GormDB() { + return window['go']['main']['App']['GormDB'](); +} + export function Greet(arg1) { return window['go']['main']['App']['Greet'](arg1); } -export function Login() { - return window['go']['main']['App']['Login'](); -} - -export function Register() { - return window['go']['main']['App']['Register'](); -} - export function UploadImage(arg1, arg2) { return window['go']['main']['App']['UploadImage'](arg1, arg2); } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index 1e55eeb..6c63e7a 100644 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -1,12 +1,343 @@ -export namespace main { +export namespace clause { - export class ImageResult { - code: number; - message: string; - data?: string; + export class Clause { + Name: string; + BeforeExpression: any; + AfterNameExpression: any; + AfterExpression: any; + Expression: any; static createFrom(source: any = {}) { - return new ImageResult(source); + return new Clause(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Name = source["Name"]; + this.BeforeExpression = source["BeforeExpression"]; + this.AfterNameExpression = source["AfterNameExpression"]; + this.AfterExpression = source["AfterExpression"]; + this.Expression = source["Expression"]; + } + } + export class Expr { + SQL: string; + Vars: any[]; + WithoutParentheses: boolean; + + static createFrom(source: any = {}) { + return new Expr(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.SQL = source["SQL"]; + this.Vars = source["Vars"]; + this.WithoutParentheses = source["WithoutParentheses"]; + } + } + export class Where { + Exprs: any[]; + + static createFrom(source: any = {}) { + return new Where(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Exprs = source["Exprs"]; + } + } + +} + +export namespace gorm { + + export class result { + Result: any; + RowsAffected: number; + Error: any; + + static createFrom(source: any = {}) { + return new result(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Result = source["Result"]; + this.RowsAffected = source["RowsAffected"]; + this.Error = source["Error"]; + } + } + export class join { + Name: string; + Alias: string; + Conds: any[]; + On?: clause.Where; + Selects: string[]; + Omits: string[]; + Expression: any; + JoinType: string; + + static createFrom(source: any = {}) { + return new join(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Name = source["Name"]; + this.Alias = source["Alias"]; + this.Conds = source["Conds"]; + this.On = this.convertValues(source["On"], clause.Where); + this.Selects = source["Selects"]; + this.Omits = source["Omits"]; + this.Expression = source["Expression"]; + this.JoinType = source["JoinType"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class Statement { + SkipDefaultTransaction: boolean; + DefaultTransactionTimeout: number; + DefaultContextTimeout: number; + NamingStrategy: any; + FullSaveAssociations: boolean; + Logger: any; + DryRun: boolean; + PrepareStmt: boolean; + PrepareStmtMaxSize: number; + PrepareStmtTTL: number; + DisableAutomaticPing: boolean; + DisableForeignKeyConstraintWhenMigrating: boolean; + IgnoreRelationshipsWhenMigrating: boolean; + DisableNestedTransaction: boolean; + AllowGlobalUpdate: boolean; + QueryFields: boolean; + CreateBatchSize: number; + TranslateError: boolean; + PropagateUnscoped: boolean; + ClauseBuilders: Record; + ConnPool: any; + Dialector: any; + Plugins: Record; + Error: any; + RowsAffected: number; + Statement?: Statement; + TableExpr?: clause.Expr; + Table: string; + Model: any; + Unscoped: boolean; + Dest: any; + // Go type: reflect + ReflectValue: any; + Clauses: Record; + BuildClauses: string[]; + Distinct: boolean; + Selects: string[]; + Omits: string[]; + ColumnMapping: Record; + Joins: join[]; + Preloads: Record>; + // Go type: sync + Settings: any; + ConnPool: any; + Schema?: schema.Schema; + Context: any; + RaiseErrorOnNotFound: boolean; + SkipHooks: boolean; + // Go type: strings + SQL: any; + Vars: any[]; + CurDestIndex: number; + Result?: result; + + static createFrom(source: any = {}) { + return new Statement(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.SkipDefaultTransaction = source["SkipDefaultTransaction"]; + this.DefaultTransactionTimeout = source["DefaultTransactionTimeout"]; + this.DefaultContextTimeout = source["DefaultContextTimeout"]; + this.NamingStrategy = source["NamingStrategy"]; + this.FullSaveAssociations = source["FullSaveAssociations"]; + this.Logger = source["Logger"]; + this.DryRun = source["DryRun"]; + this.PrepareStmt = source["PrepareStmt"]; + this.PrepareStmtMaxSize = source["PrepareStmtMaxSize"]; + this.PrepareStmtTTL = source["PrepareStmtTTL"]; + this.DisableAutomaticPing = source["DisableAutomaticPing"]; + this.DisableForeignKeyConstraintWhenMigrating = source["DisableForeignKeyConstraintWhenMigrating"]; + this.IgnoreRelationshipsWhenMigrating = source["IgnoreRelationshipsWhenMigrating"]; + this.DisableNestedTransaction = source["DisableNestedTransaction"]; + this.AllowGlobalUpdate = source["AllowGlobalUpdate"]; + this.QueryFields = source["QueryFields"]; + this.CreateBatchSize = source["CreateBatchSize"]; + this.TranslateError = source["TranslateError"]; + this.PropagateUnscoped = source["PropagateUnscoped"]; + this.ClauseBuilders = source["ClauseBuilders"]; + this.ConnPool = source["ConnPool"]; + this.Dialector = source["Dialector"]; + this.Plugins = source["Plugins"]; + this.Error = source["Error"]; + this.RowsAffected = source["RowsAffected"]; + this.Statement = this.convertValues(source["Statement"], Statement); + this.TableExpr = this.convertValues(source["TableExpr"], clause.Expr); + this.Table = source["Table"]; + this.Model = source["Model"]; + this.Unscoped = source["Unscoped"]; + this.Dest = source["Dest"]; + this.ReflectValue = this.convertValues(source["ReflectValue"], null); + this.Clauses = this.convertValues(source["Clauses"], clause.Clause, true); + this.BuildClauses = source["BuildClauses"]; + this.Distinct = source["Distinct"]; + this.Selects = source["Selects"]; + this.Omits = source["Omits"]; + this.ColumnMapping = source["ColumnMapping"]; + this.Joins = this.convertValues(source["Joins"], join); + this.Preloads = source["Preloads"]; + this.Settings = this.convertValues(source["Settings"], null); + this.ConnPool = source["ConnPool"]; + this.Schema = this.convertValues(source["Schema"], schema.Schema); + this.Context = source["Context"]; + this.RaiseErrorOnNotFound = source["RaiseErrorOnNotFound"]; + this.SkipHooks = source["SkipHooks"]; + this.SQL = this.convertValues(source["SQL"], null); + this.Vars = source["Vars"]; + this.CurDestIndex = source["CurDestIndex"]; + this.Result = this.convertValues(source["Result"], result); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class DB { + SkipDefaultTransaction: boolean; + DefaultTransactionTimeout: number; + DefaultContextTimeout: number; + NamingStrategy: any; + FullSaveAssociations: boolean; + Logger: any; + DryRun: boolean; + PrepareStmt: boolean; + PrepareStmtMaxSize: number; + PrepareStmtTTL: number; + DisableAutomaticPing: boolean; + DisableForeignKeyConstraintWhenMigrating: boolean; + IgnoreRelationshipsWhenMigrating: boolean; + DisableNestedTransaction: boolean; + AllowGlobalUpdate: boolean; + QueryFields: boolean; + CreateBatchSize: number; + TranslateError: boolean; + PropagateUnscoped: boolean; + ClauseBuilders: Record; + ConnPool: any; + Dialector: any; + Plugins: Record; + Error: any; + RowsAffected: number; + Statement?: Statement; + + static createFrom(source: any = {}) { + return new DB(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.SkipDefaultTransaction = source["SkipDefaultTransaction"]; + this.DefaultTransactionTimeout = source["DefaultTransactionTimeout"]; + this.DefaultContextTimeout = source["DefaultContextTimeout"]; + this.NamingStrategy = source["NamingStrategy"]; + this.FullSaveAssociations = source["FullSaveAssociations"]; + this.Logger = source["Logger"]; + this.DryRun = source["DryRun"]; + this.PrepareStmt = source["PrepareStmt"]; + this.PrepareStmtMaxSize = source["PrepareStmtMaxSize"]; + this.PrepareStmtTTL = source["PrepareStmtTTL"]; + this.DisableAutomaticPing = source["DisableAutomaticPing"]; + this.DisableForeignKeyConstraintWhenMigrating = source["DisableForeignKeyConstraintWhenMigrating"]; + this.IgnoreRelationshipsWhenMigrating = source["IgnoreRelationshipsWhenMigrating"]; + this.DisableNestedTransaction = source["DisableNestedTransaction"]; + this.AllowGlobalUpdate = source["AllowGlobalUpdate"]; + this.QueryFields = source["QueryFields"]; + this.CreateBatchSize = source["CreateBatchSize"]; + this.TranslateError = source["TranslateError"]; + this.PropagateUnscoped = source["PropagateUnscoped"]; + this.ClauseBuilders = source["ClauseBuilders"]; + this.ConnPool = source["ConnPool"]; + this.Dialector = source["Dialector"]; + this.Plugins = source["Plugins"]; + this.Error = source["Error"]; + this.RowsAffected = source["RowsAffected"]; + this.Statement = this.convertValues(source["Statement"], Statement); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + + + +} + +export namespace main { + + export class Response { + code: number; + message: string; + data?: any; + + static createFrom(source: any = {}) { + return new Response(source); } constructor(source: any = {}) { @@ -17,5 +348,378 @@ export namespace main { } } +} + +export namespace reflect { + + export class StructField { + Name: string; + PkgPath: string; + Type: any; + Tag: string; + Offset: any; + Index: number[]; + Anonymous: boolean; + + static createFrom(source: any = {}) { + return new StructField(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Name = source["Name"]; + this.PkgPath = source["PkgPath"]; + this.Type = source["Type"]; + this.Tag = source["Tag"]; + this.Offset = source["Offset"]; + this.Index = source["Index"]; + this.Anonymous = source["Anonymous"]; + } + } + +} + +export namespace schema { + + export class Reference { + PrimaryKey?: Field; + PrimaryValue: string; + ForeignKey?: Field; + OwnPrimaryKey: boolean; + + static createFrom(source: any = {}) { + return new Reference(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.PrimaryKey = this.convertValues(source["PrimaryKey"], Field); + this.PrimaryValue = source["PrimaryValue"]; + this.ForeignKey = this.convertValues(source["ForeignKey"], Field); + this.OwnPrimaryKey = source["OwnPrimaryKey"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class Polymorphic { + PolymorphicID?: Field; + PolymorphicType?: Field; + Value: string; + + static createFrom(source: any = {}) { + return new Polymorphic(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.PolymorphicID = this.convertValues(source["PolymorphicID"], Field); + this.PolymorphicType = this.convertValues(source["PolymorphicType"], Field); + this.Value = source["Value"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class Relationship { + Name: string; + Type: string; + Field?: Field; + Polymorphic?: Polymorphic; + References: Reference[]; + Schema?: Schema; + FieldSchema?: Schema; + JoinTable?: Schema; + + static createFrom(source: any = {}) { + return new Relationship(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Name = source["Name"]; + this.Type = source["Type"]; + this.Field = this.convertValues(source["Field"], Field); + this.Polymorphic = this.convertValues(source["Polymorphic"], Polymorphic); + this.References = this.convertValues(source["References"], Reference); + this.Schema = this.convertValues(source["Schema"], Schema); + this.FieldSchema = this.convertValues(source["FieldSchema"], Schema); + this.JoinTable = this.convertValues(source["JoinTable"], Schema); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class Relationships { + HasOne: Relationship[]; + BelongsTo: Relationship[]; + HasMany: Relationship[]; + Many2Many: Relationship[]; + Relations: Record; + EmbeddedRelations: Record; + // Go type: sync + Mux: any; + + static createFrom(source: any = {}) { + return new Relationships(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.HasOne = this.convertValues(source["HasOne"], Relationship); + this.BelongsTo = this.convertValues(source["BelongsTo"], Relationship); + this.HasMany = this.convertValues(source["HasMany"], Relationship); + this.Many2Many = this.convertValues(source["Many2Many"], Relationship); + this.Relations = this.convertValues(source["Relations"], Relationship, true); + this.EmbeddedRelations = this.convertValues(source["EmbeddedRelations"], Relationships, true); + this.Mux = this.convertValues(source["Mux"], null); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class Schema { + Name: string; + ModelType: any; + Table: string; + PrioritizedPrimaryField?: Field; + DBNames: string[]; + PrimaryFields: Field[]; + PrimaryFieldDBNames: string[]; + Fields: Field[]; + FieldsByName: Record; + FieldsByBindName: Record; + FieldsByDBName: Record; + FieldsWithDefaultDBValue: Field[]; + Relationships: Relationships; + CreateClauses: any[]; + QueryClauses: any[]; + UpdateClauses: any[]; + DeleteClauses: any[]; + BeforeCreate: boolean; + AfterCreate: boolean; + BeforeUpdate: boolean; + AfterUpdate: boolean; + BeforeDelete: boolean; + AfterDelete: boolean; + BeforeSave: boolean; + AfterSave: boolean; + AfterFind: boolean; + + static createFrom(source: any = {}) { + return new Schema(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Name = source["Name"]; + this.ModelType = source["ModelType"]; + this.Table = source["Table"]; + this.PrioritizedPrimaryField = this.convertValues(source["PrioritizedPrimaryField"], Field); + this.DBNames = source["DBNames"]; + this.PrimaryFields = this.convertValues(source["PrimaryFields"], Field); + this.PrimaryFieldDBNames = source["PrimaryFieldDBNames"]; + this.Fields = this.convertValues(source["Fields"], Field); + this.FieldsByName = this.convertValues(source["FieldsByName"], Field, true); + this.FieldsByBindName = this.convertValues(source["FieldsByBindName"], Field, true); + this.FieldsByDBName = this.convertValues(source["FieldsByDBName"], Field, true); + this.FieldsWithDefaultDBValue = this.convertValues(source["FieldsWithDefaultDBValue"], Field); + this.Relationships = this.convertValues(source["Relationships"], Relationships); + this.CreateClauses = source["CreateClauses"]; + this.QueryClauses = source["QueryClauses"]; + this.UpdateClauses = source["UpdateClauses"]; + this.DeleteClauses = source["DeleteClauses"]; + this.BeforeCreate = source["BeforeCreate"]; + this.AfterCreate = source["AfterCreate"]; + this.BeforeUpdate = source["BeforeUpdate"]; + this.AfterUpdate = source["AfterUpdate"]; + this.BeforeDelete = source["BeforeDelete"]; + this.AfterDelete = source["AfterDelete"]; + this.BeforeSave = source["BeforeSave"]; + this.AfterSave = source["AfterSave"]; + this.AfterFind = source["AfterFind"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class Field { + Name: string; + DBName: string; + BindNames: string[]; + EmbeddedBindNames: string[]; + DataType: string; + GORMDataType: string; + PrimaryKey: boolean; + AutoIncrement: boolean; + AutoIncrementIncrement: number; + Creatable: boolean; + Updatable: boolean; + Readable: boolean; + AutoCreateTime: number; + AutoUpdateTime: number; + HasDefaultValue: boolean; + DefaultValue: string; + DefaultValueInterface: any; + NotNull: boolean; + Unique: boolean; + Comment: string; + Size: number; + Precision: number; + Scale: number; + IgnoreMigration: boolean; + FieldType: any; + IndirectFieldType: any; + StructField: reflect.StructField; + Tag: string; + TagSettings: Record; + Schema?: Schema; + EmbeddedSchema?: Schema; + OwnerSchema?: Schema; + Serializer: any; + NewValuePool: any; + UniqueIndex: string; + + static createFrom(source: any = {}) { + return new Field(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.Name = source["Name"]; + this.DBName = source["DBName"]; + this.BindNames = source["BindNames"]; + this.EmbeddedBindNames = source["EmbeddedBindNames"]; + this.DataType = source["DataType"]; + this.GORMDataType = source["GORMDataType"]; + this.PrimaryKey = source["PrimaryKey"]; + this.AutoIncrement = source["AutoIncrement"]; + this.AutoIncrementIncrement = source["AutoIncrementIncrement"]; + this.Creatable = source["Creatable"]; + this.Updatable = source["Updatable"]; + this.Readable = source["Readable"]; + this.AutoCreateTime = source["AutoCreateTime"]; + this.AutoUpdateTime = source["AutoUpdateTime"]; + this.HasDefaultValue = source["HasDefaultValue"]; + this.DefaultValue = source["DefaultValue"]; + this.DefaultValueInterface = source["DefaultValueInterface"]; + this.NotNull = source["NotNull"]; + this.Unique = source["Unique"]; + this.Comment = source["Comment"]; + this.Size = source["Size"]; + this.Precision = source["Precision"]; + this.Scale = source["Scale"]; + this.IgnoreMigration = source["IgnoreMigration"]; + this.FieldType = source["FieldType"]; + this.IndirectFieldType = source["IndirectFieldType"]; + this.StructField = this.convertValues(source["StructField"], reflect.StructField); + this.Tag = source["Tag"]; + this.TagSettings = source["TagSettings"]; + this.Schema = this.convertValues(source["Schema"], Schema); + this.EmbeddedSchema = this.convertValues(source["EmbeddedSchema"], Schema); + this.OwnerSchema = this.convertValues(source["OwnerSchema"], Schema); + this.Serializer = source["Serializer"]; + this.NewValuePool = source["NewValuePool"]; + this.UniqueIndex = source["UniqueIndex"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + + + + + } diff --git a/go.mod b/go.mod index 2854220..a5a0134 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,11 @@ module dissertation go 1.25.0 -require github.com/wailsapp/wails/v2 v2.13.0 +require ( + github.com/wailsapp/wails/v2 v2.13.0 + gorm.io/driver/sqlite v1.6.0 + gorm.io/gorm v1.31.2 +) require ( git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect @@ -12,6 +16,8 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/labstack/echo/v4 v4.13.3 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/leaanthony/go-ansi-parser v1.6.1 // indirect @@ -20,6 +26,7 @@ require ( github.com/leaanthony/u v1.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.48 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -31,8 +38,8 @@ require ( github.com/wailsapp/mimetype v1.4.1 // indirect golang.org/x/crypto v0.51.0 // indirect golang.org/x/net v0.54.0 // indirect - golang.org/x/sys v0.44.0 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/sys v0.46.0 // indirect + golang.org/x/text v0.40.0 // indirect ) // replace github.com/wailsapp/wails/v2 v2.12.0 => C:\Users\夏东亮\go\pkg\mod diff --git a/go.sum b/go.sum index b664a33..0450608 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,10 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -36,6 +40,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs= +github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -72,12 +78,16 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo= +gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/main.go b/main.go index dd31d20..ba21798 100644 --- a/main.go +++ b/main.go @@ -17,9 +17,10 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "dissertation", - Width: 1024, - Height: 768, + Title: "分类喵", + Width: 1024, + Height: 768, + DisableResize: true, AssetServer: &assetserver.Options{ Assets: assets, }, diff --git a/types.go b/types.go index f52de4e..bea5971 100644 --- a/types.go +++ b/types.go @@ -1,7 +1,55 @@ package main -type ImageResult struct { - Code int `json:"code"` - Message string `json:"message"` - Data string `json:"data,omitempty"` // 成功时返回文件路径 -} +import ( + "context" +) + +type ( + App struct { + ctx context.Context + } + + Response struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` + } + + HistoryItem struct { + Id uint `gorm:"primaryKey"` + Img string `gorm:"column:img"` + Breed int `gorm:"column:breed"` + Date int `gorm:"column:date"` + } + + HistoryWithBreed struct { + Id uint `gorm:"column:id" json:"id"` + Img string `gorm:"column:img" json:"img"` + Breed int `gorm:"column:breed" json:"breed"` + Date int `gorm:"column:date" json:"date"` + Name string `gorm:"column:name" json:"name"` + Brief string `gorm:"column:brief" json:"brief"` + } + + HistoryData struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + Total int64 `json:"total"` + List []HistoryWithBreed `json:"list"` + } + + Breed struct { + Id uint `gorm:"primaryKey"` + Code string `gorm:"column:code"` + Name string `gorm:"column:name"` + Brief string `gorm:"column:brief"` + } + + DetectData struct { + Id uint `json:"id"` + Code string `json:"code"` + Name string `json:"name"` + Brief string `json:"brief"` + ConfidenceLevel float32 `json:"confidence_level"` + } +)