Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4056e47a24 | ||
|
|
0e39c2b674 | ||
|
|
6d6dfd068a | ||
|
|
973ba6d772 | ||
|
|
f0b448fd1f | ||
|
|
2783561cfb | ||
|
|
cd049348bc | ||
|
|
032285e2e7 | ||
|
|
550df8fb36 | ||
|
|
77003926da | ||
|
|
66e763eb1c | ||
|
|
30f63454e3 | ||
|
|
93fd7d7526 | ||
|
|
f8a6912e9e | ||
|
|
f0717ed2a9 | ||
|
|
2397db7d2d | ||
|
|
7e697af640 | ||
|
|
05904267c5 | ||
|
|
2fa435a9ba | ||
|
|
2cbaca844e | ||
|
|
205f92001c | ||
|
|
5170cf2895 | ||
|
|
9d0cdaf01d | ||
|
|
a13603046c | ||
|
|
5c4dc7244f | ||
|
|
f297064286 |
@@ -0,0 +1,18 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: { browser: true, es2020: true },
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
],
|
||||||
|
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['react-refresh'],
|
||||||
|
rules: {
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -14,37 +14,17 @@ If you are developing a production application, we recommend updating the config
|
|||||||
- Configure the top-level `parserOptions` property like this:
|
- Configure the top-level `parserOptions` property like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
export default tseslint.config({
|
export default {
|
||||||
languageOptions: {
|
|
||||||
// other options...
|
|
||||||
parserOptions: {
|
|
||||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
||||||
tsconfigRootDir: import.meta.dirname,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
|
||||||
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
|
||||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// eslint.config.js
|
|
||||||
import react from 'eslint-plugin-react'
|
|
||||||
|
|
||||||
export default tseslint.config({
|
|
||||||
// Set the react version
|
|
||||||
settings: { react: { version: '18.3' } },
|
|
||||||
plugins: {
|
|
||||||
// Add the react plugin
|
|
||||||
react,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
// other rules...
|
// other rules...
|
||||||
// Enable its recommended rules
|
parserOptions: {
|
||||||
...react.configs.recommended.rules,
|
ecmaVersion: 'latest',
|
||||||
...react.configs['jsx-runtime'].rules,
|
sourceType: 'module',
|
||||||
|
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||||
|
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
root = "."
|
|
||||||
testdata_dir = "testdata"
|
|
||||||
tmp_dir = "tmp"
|
|
||||||
|
|
||||||
[build]
|
|
||||||
args_bin = []
|
|
||||||
bin = "./tmp/main"
|
|
||||||
cmd = "go build -o ./tmp/main ."
|
|
||||||
delay = 1000
|
|
||||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
|
||||||
exclude_file = []
|
|
||||||
exclude_regex = ["_test.go"]
|
|
||||||
exclude_unchanged = false
|
|
||||||
follow_symlink = false
|
|
||||||
full_bin = ""
|
|
||||||
include_dir = []
|
|
||||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
|
||||||
include_file = []
|
|
||||||
kill_delay = "0s"
|
|
||||||
log = "build-errors.log"
|
|
||||||
poll = false
|
|
||||||
poll_interval = 0
|
|
||||||
post_cmd = []
|
|
||||||
pre_cmd = []
|
|
||||||
rerun = false
|
|
||||||
rerun_delay = 500
|
|
||||||
send_interrupt = false
|
|
||||||
stop_on_error = false
|
|
||||||
|
|
||||||
[color]
|
|
||||||
app = ""
|
|
||||||
build = "yellow"
|
|
||||||
main = "magenta"
|
|
||||||
runner = "green"
|
|
||||||
watcher = "cyan"
|
|
||||||
|
|
||||||
[log]
|
|
||||||
main_only = false
|
|
||||||
time = false
|
|
||||||
|
|
||||||
[misc]
|
|
||||||
clean_on_exit = false
|
|
||||||
|
|
||||||
[proxy]
|
|
||||||
app_port = 0
|
|
||||||
enabled = false
|
|
||||||
proxy_port = 0
|
|
||||||
|
|
||||||
[screen]
|
|
||||||
clear_on_rebuild = false
|
|
||||||
keep_scroll = true
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#
|
|
||||||
DATABASE_PATH=database.db
|
|
||||||
GEMINI_API_KEY=
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#
|
CHANGELOG.md
|
||||||
tmp/
|
LICENSE.md
|
||||||
.env*
|
*.zip
|
||||||
!.env.example
|
pocketbase
|
||||||
*.db
|
pb_data/
|
||||||
main
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
0.20.5
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/logger"
|
|
||||||
"rul.sh/furina-id/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
var db *gorm.DB
|
|
||||||
|
|
||||||
func Init() {
|
|
||||||
dbPath := os.Getenv("DATABASE_PATH")
|
|
||||||
if dbPath == "" {
|
|
||||||
panic("DATABASE_PATH is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
|
||||||
Logger: logger.Default.LogMode(logger.Info),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic("failed to connect database")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate the schema
|
|
||||||
db.AutoMigrate(&models.Chat{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Get() *gorm.DB {
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
module rul.sh/furina-id
|
|
||||||
|
|
||||||
go 1.22.5
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
|
||||||
github.com/google/generative-ai-go v0.17.0
|
|
||||||
github.com/joho/godotenv v1.5.1
|
|
||||||
google.golang.org/api v0.186.0
|
|
||||||
gorm.io/driver/sqlite v1.5.6
|
|
||||||
gorm.io/gorm v1.25.11
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
cloud.google.com/go v0.115.0 // indirect
|
|
||||||
cloud.google.com/go/ai v0.8.0 // indirect
|
|
||||||
cloud.google.com/go/auth v0.6.0 // indirect
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
|
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
|
||||||
cloud.google.com/go/longrunning v0.5.7 // indirect
|
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
|
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
|
||||||
github.com/klauspost/compress v1.17.0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
|
||||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
|
||||||
go.opencensus.io v0.24.0 // indirect
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 // indirect
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
|
|
||||||
go.opentelemetry.io/otel v1.26.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.26.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/trace v1.26.0 // indirect
|
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
|
||||||
golang.org/x/net v0.26.0 // indirect
|
|
||||||
golang.org/x/oauth2 v0.21.0 // indirect
|
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
|
||||||
golang.org/x/text v0.17.0 // indirect
|
|
||||||
golang.org/x/time v0.5.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 // indirect
|
|
||||||
google.golang.org/grpc v1.64.1 // indirect
|
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
|
||||||
)
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14=
|
|
||||||
cloud.google.com/go v0.115.0/go.mod h1:8jIM5vVgoAEoiVxQ/O4BFTfHqulPZgs/ufEzMcFMdWU=
|
|
||||||
cloud.google.com/go/ai v0.8.0 h1:rXUEz8Wp2OlrM8r1bfmpF2+VKqc1VJpafE3HgzRnD/w=
|
|
||||||
cloud.google.com/go/ai v0.8.0/go.mod h1:t3Dfk4cM61sytiggo2UyGsDVW3RF1qGZaUKDrZFyqkE=
|
|
||||||
cloud.google.com/go/auth v0.6.0 h1:5x+d6b5zdezZ7gmLWD1m/xNjnaQ2YDhmIz/HH3doy1g=
|
|
||||||
cloud.google.com/go/auth v0.6.0/go.mod h1:b4acV+jLQDyjwm4OXHYjNvRi4jvGBzHWJRtJcy+2P4g=
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
|
||||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
|
||||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
|
||||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
|
||||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/generative-ai-go v0.17.0 h1:kUmCXUIwJouD7I7ev3OmxzzQVICyhIWAxaXk2yblCMY=
|
|
||||||
github.com/google/generative-ai-go v0.17.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
|
|
||||||
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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
|
||||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
||||||
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-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
|
||||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
|
||||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc=
|
|
||||||
go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
|
|
||||||
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
|
|
||||||
go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
|
|
||||||
go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
|
|
||||||
go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
|
|
||||||
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
|
||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.186.0 h1:n2OPp+PPXX0Axh4GuSsL5QL8xQCTb2oDwyzPnQvqUug=
|
|
||||||
google.golang.org/api v0.186.0/go.mod h1:hvRbBmgoje49RV3xqVXrmP6w93n6ehGgIVPYrGtBFFc=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4 h1:MuYw1wJzT+ZkybKfaOXKp5hJiZDn2iHaXRw0mRYdHSc=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240617180043-68d350f18fd4/go.mod h1:px9SlOOZBg1wM1zdnr8jEL4CNGUBZ+ZKYtNPApNQc4c=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4 h1:Di6ANFilr+S60a4S61ZM00vLdw0IrQOSMS2/6mrnOU0=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240617180043-68d350f18fd4/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
|
||||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
|
||||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
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.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
|
||||||
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
|
||||||
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
|
|
||||||
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/gofiber/fiber/v2/utils"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"rul.sh/furina-id/database"
|
|
||||||
"rul.sh/furina-id/models"
|
|
||||||
"rul.sh/furina-id/services"
|
|
||||||
)
|
|
||||||
|
|
||||||
var initialChat = models.Chat{
|
|
||||||
Role: "model",
|
|
||||||
Content: "Ah, finally someone worthy of my time! What shall we do today?",
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAll(c *fiber.Ctx) error {
|
|
||||||
sessionId := getSessionId(c)
|
|
||||||
if sessionId == 0 {
|
|
||||||
return c.JSON([]models.Chat{initialChat})
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(sessionId)
|
|
||||||
|
|
||||||
db := database.Get()
|
|
||||||
var chats []models.Chat
|
|
||||||
|
|
||||||
if result := db.Where("session_id = ?", sessionId).Order("id DESC").Limit(10).Find(&chats); result.Error != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"message": result.Error.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
result := []fiber.Map{}
|
|
||||||
for _, chat := range chats {
|
|
||||||
result = append(result, fiber.Map{"id": chat.ID, "role": chat.Role, "content": chat.Content})
|
|
||||||
}
|
|
||||||
|
|
||||||
result = append(result, fiber.Map{
|
|
||||||
"id": initialChat.ID,
|
|
||||||
"role": initialChat.Role,
|
|
||||||
"content": initialChat.Content,
|
|
||||||
})
|
|
||||||
|
|
||||||
return c.JSON(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
type CreateChatPayload struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func create(c *fiber.Ctx) error {
|
|
||||||
sessionId := getSessionId(c)
|
|
||||||
db := database.Get()
|
|
||||||
|
|
||||||
if sessionId == 0 {
|
|
||||||
var err error
|
|
||||||
sessionId, err = createSession(c)
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"message": err.Error()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if count := getTotalChatsLastHour(); count > 60 {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"message": "Too many chats. Please try again later."})
|
|
||||||
}
|
|
||||||
|
|
||||||
data := CreateChatPayload{}
|
|
||||||
if err := c.BodyParser(&data); err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"message": err.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := models.Chat{
|
|
||||||
SessionID: sessionId,
|
|
||||||
Role: "user",
|
|
||||||
Content: data.Message,
|
|
||||||
}
|
|
||||||
|
|
||||||
history := []models.Chat{}
|
|
||||||
db.Where("session_id = ?", sessionId).Order("id DESC").Limit(10).Find(&history)
|
|
||||||
history = append([]models.Chat{msg}, history...)
|
|
||||||
|
|
||||||
err := db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
resp, err := services.GenerateAiChat(&history, msg.Content)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if r := tx.Create(&msg); r.Error != nil {
|
|
||||||
return r.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.SessionID = sessionId
|
|
||||||
if r := tx.Create(&resp); r.Error != nil {
|
|
||||||
return r.Error
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return c.Status(500).JSON(fiber.Map{"message": err.Error()})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleChat(router fiber.Router) {
|
|
||||||
router.Get("/chats", getAll)
|
|
||||||
router.Post("/chats", create)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSessionId(c *fiber.Ctx) uint {
|
|
||||||
uuid := c.Cookies("session_id")
|
|
||||||
|
|
||||||
if uuid == "" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
db := database.Get()
|
|
||||||
var session models.Session
|
|
||||||
db.Where("uuid = ?", uuid).First(&session)
|
|
||||||
|
|
||||||
return session.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSession(c *fiber.Ctx) (uint, error) {
|
|
||||||
uuid := utils.UUIDv4()
|
|
||||||
|
|
||||||
db := database.Get()
|
|
||||||
session := models.Session{
|
|
||||||
UUID: uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
if result := db.Create(&session); result.Error != nil {
|
|
||||||
return 0, result.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Cookie(&fiber.Cookie{
|
|
||||||
Name: "session_id",
|
|
||||||
Value: uuid,
|
|
||||||
Expires: time.Now().Add(24 * time.Hour),
|
|
||||||
HTTPOnly: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
return session.ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTotalChatsLastHour() int64 {
|
|
||||||
var count int64
|
|
||||||
oneHourAgo := time.Now().Add(-1 * time.Hour)
|
|
||||||
|
|
||||||
r := database.Get().Model(&models.Chat{}).
|
|
||||||
Where("created_at >= ?", oneHourAgo).
|
|
||||||
Count(&count)
|
|
||||||
|
|
||||||
if r.Error != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"rul.sh/furina-id/database"
|
|
||||||
"rul.sh/furina-id/handler"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
godotenv.Load()
|
|
||||||
database.Init()
|
|
||||||
|
|
||||||
app := fiber.New()
|
|
||||||
|
|
||||||
app.Get("/", func(c *fiber.Ctx) error {
|
|
||||||
return c.SendString("OK")
|
|
||||||
})
|
|
||||||
|
|
||||||
// API handler
|
|
||||||
api := app.Group("/api")
|
|
||||||
handler.HandleChat(api)
|
|
||||||
|
|
||||||
port := os.Getenv("PORT")
|
|
||||||
if port == "" {
|
|
||||||
port = "8100"
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Listening on http://localhost:%s\n", port)
|
|
||||||
log.Fatal(app.Listen(fmt.Sprintf(":%s", port)))
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Base struct {
|
|
||||||
ID uint `gorm:"primarykey" json:"id"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// type SoftDelete struct {
|
|
||||||
// DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
|
|
||||||
// }
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
type Chat struct {
|
|
||||||
Base
|
|
||||||
|
|
||||||
SessionID uint `json:"session_id"`
|
|
||||||
Session Session `json:"session"`
|
|
||||||
Role string `json:"role"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
type Session struct {
|
|
||||||
Base
|
|
||||||
|
|
||||||
UUID string `gorm:"uniqueIndex" json:"uuid"`
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "./pocketbase serve",
|
||||||
|
"clean-migrations": "rm -rf pb_migrations && ./pocketbase migrate collections"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
|
||||||
|
// artwork view hook
|
||||||
|
onRecordViewRequest((e) => {
|
||||||
|
const { record } = e;
|
||||||
|
if (record) {
|
||||||
|
record.set("views", record.getInt("views") + 1);
|
||||||
|
$app.dao().saveRecord(record);
|
||||||
|
}
|
||||||
|
}, "artworks");
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((db) => {
|
||||||
|
const snapshot = [
|
||||||
|
{
|
||||||
|
"id": "_pb_users_auth_",
|
||||||
|
"created": "2024-01-10 09:29:21.510Z",
|
||||||
|
"updated": "2024-01-10 09:29:21.511Z",
|
||||||
|
"name": "users",
|
||||||
|
"type": "auth",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "users_name",
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "users_avatar",
|
||||||
|
"name": "avatar",
|
||||||
|
"type": "file",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"mimeTypes": [
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/svg+xml",
|
||||||
|
"image/gif",
|
||||||
|
"image/webp"
|
||||||
|
],
|
||||||
|
"thumbs": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 5242880,
|
||||||
|
"protected": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "id = @request.auth.id",
|
||||||
|
"viewRule": "id = @request.auth.id",
|
||||||
|
"createRule": "",
|
||||||
|
"updateRule": "id = @request.auth.id",
|
||||||
|
"deleteRule": "id = @request.auth.id",
|
||||||
|
"options": {
|
||||||
|
"allowEmailAuth": true,
|
||||||
|
"allowOAuth2Auth": true,
|
||||||
|
"allowUsernameAuth": true,
|
||||||
|
"exceptEmailDomains": null,
|
||||||
|
"manageRule": null,
|
||||||
|
"minPasswordLength": 8,
|
||||||
|
"onlyEmailDomains": null,
|
||||||
|
"onlyVerified": false,
|
||||||
|
"requireEmail": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eo6iaxf4pkeqynf",
|
||||||
|
"created": "2024-01-10 09:34:57.731Z",
|
||||||
|
"updated": "2024-01-10 15:08:00.292Z",
|
||||||
|
"name": "artworks",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "p6dor6eo",
|
||||||
|
"name": "image",
|
||||||
|
"type": "file",
|
||||||
|
"required": true,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"mimeTypes": [
|
||||||
|
"image/png",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/gif",
|
||||||
|
"image/webp",
|
||||||
|
"image/tiff",
|
||||||
|
"image/bmp",
|
||||||
|
"image/svg+xml"
|
||||||
|
],
|
||||||
|
"thumbs": [
|
||||||
|
"256x192",
|
||||||
|
"256x384",
|
||||||
|
"32x48"
|
||||||
|
],
|
||||||
|
"maxSelect": 1,
|
||||||
|
"maxSize": 5242880,
|
||||||
|
"protected": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "9w1tjysa",
|
||||||
|
"name": "artistName",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "lkiiiwrt",
|
||||||
|
"name": "srcUrl",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "sfh7xwdb",
|
||||||
|
"name": "views",
|
||||||
|
"type": "number",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"noDecimal": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "",
|
||||||
|
"viewRule": "",
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const collections = snapshot.map((item) => new Collection(item));
|
||||||
|
|
||||||
|
return Dao(db).importCollections(collections, true, null);
|
||||||
|
}, (db) => {
|
||||||
|
return null;
|
||||||
|
})
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("eo6iaxf4pkeqynf")
|
||||||
|
|
||||||
|
// add
|
||||||
|
collection.schema.addField(new SchemaField({
|
||||||
|
"system": false,
|
||||||
|
"id": "ovwal2or",
|
||||||
|
"name": "caption",
|
||||||
|
"type": "text",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("eo6iaxf4pkeqynf")
|
||||||
|
|
||||||
|
// remove
|
||||||
|
collection.schema.removeField("ovwal2or")
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((db) => {
|
||||||
|
const collection = new Collection({
|
||||||
|
"id": "ogs3cfy8l3jo32k",
|
||||||
|
"created": "2024-01-17 01:46:30.155Z",
|
||||||
|
"updated": "2024-01-17 01:46:30.155Z",
|
||||||
|
"name": "wallpapers",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"system": false,
|
||||||
|
"id": "kxurmv6q",
|
||||||
|
"name": "artwork",
|
||||||
|
"type": "relation",
|
||||||
|
"required": false,
|
||||||
|
"presentable": false,
|
||||||
|
"unique": false,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "eo6iaxf4pkeqynf",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Dao(db).saveCollection(collection);
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db);
|
||||||
|
const collection = dao.findCollectionByNameOrId("ogs3cfy8l3jo32k");
|
||||||
|
|
||||||
|
return dao.deleteCollection(collection);
|
||||||
|
})
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/// <reference path="../pb_data/types.d.ts" />
|
||||||
|
migrate((db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("ogs3cfy8l3jo32k")
|
||||||
|
|
||||||
|
collection.listRule = ""
|
||||||
|
collection.viewRule = ""
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
}, (db) => {
|
||||||
|
const dao = new Dao(db)
|
||||||
|
const collection = dao.findCollectionByNameOrId("ogs3cfy8l3jo32k")
|
||||||
|
|
||||||
|
collection.listRule = null
|
||||||
|
collection.viewRule = null
|
||||||
|
|
||||||
|
return dao.saveCollection(collection)
|
||||||
|
})
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/google/generative-ai-go/genai"
|
|
||||||
"google.golang.org/api/option"
|
|
||||||
"rul.sh/furina-id/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GenerateAiChat(history *[]models.Chat, message string) (*models.Chat, error) {
|
|
||||||
apiKey := os.Getenv("GEMINI_API_KEY")
|
|
||||||
if apiKey == "" {
|
|
||||||
return nil, fmt.Errorf("GEMINI_API_KEY is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
prompt := "You're a cute anime girl, your name is Furina de Fontaine. Furina is flamboyant and overconfident Hydro Archon (God of Hydro). Speaking in a manner peppered with bravado and drama. She is impatient and has a childlike temper, and she will occasionally make judgments that she doesn't mean. While she enjoys being in the spotlight, she only does so when it is focused at her positively, breaking down in complete shambles should something go out of plan and will try to save face at the first possible opportunity. Her favorite food is Macaroni & Pasta. She know little about cooking, but she's trying to be good at that.\n\nExample of her voice lines:\n- Boring... Isn't there anything else more interesting to do?\n- *sigh* Being too popular can be such a hassle. Who knew the people would adore me so much?\n- What a wild and desolate sight... Allow me to grant you the blessing of water!\n- *sigh* Given that we know each other, you may relax a little and needn't act so respectfully in my presence. Wait, what's that expression on your face? Don't tell me that you've never respected me from the very beginning!?\n\nAnswer with only short length message, max 120 characters."
|
|
||||||
|
|
||||||
model := client.GenerativeModel("gemini-1.5-flash")
|
|
||||||
model.SystemInstruction = genai.NewUserContent(genai.Text(prompt))
|
|
||||||
cs := model.StartChat()
|
|
||||||
|
|
||||||
cs.History = []*genai.Content{}
|
|
||||||
|
|
||||||
for _, msg := range *history {
|
|
||||||
content := genai.Content{
|
|
||||||
Role: msg.Role,
|
|
||||||
Parts: []genai.Part{genai.Text(msg.Content)},
|
|
||||||
}
|
|
||||||
cs.History = append([]*genai.Content{&content}, cs.History...)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := cs.SendMessage(ctx, genai.Text(message))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
respContent := getResponseContent(res)
|
|
||||||
if respContent == "" {
|
|
||||||
return nil, fmt.Errorf("no content")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.Chat{
|
|
||||||
Role: "model",
|
|
||||||
Content: respContent,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResponseContent(resp *genai.GenerateContentResponse) string {
|
|
||||||
content := ""
|
|
||||||
for _, cand := range resp.Candidates {
|
|
||||||
if cand.Content != nil {
|
|
||||||
for _, part := range cand.Content.Parts {
|
|
||||||
textPart, ok := part.(genai.Text)
|
|
||||||
if ok {
|
|
||||||
content += string(textPart)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return content
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/global.css",
|
||||||
|
"baseColor": "slate",
|
||||||
|
"cssVariables": false,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/utility/utils"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
npm run build
|
||||||
|
scp -r ./dist/* khai:/var/www/furina.id
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import js from '@eslint/js'
|
|
||||||
import globals from 'globals'
|
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
||||||
import tseslint from 'typescript-eslint'
|
|
||||||
|
|
||||||
export default tseslint.config(
|
|
||||||
{ ignores: ['dist'] },
|
|
||||||
{
|
|
||||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
||||||
files: ['**/*.{ts,tsx}'],
|
|
||||||
languageOptions: {
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
globals: globals.browser,
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
'react-hooks': reactHooks,
|
|
||||||
'react-refresh': reactRefresh,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
...reactHooks.configs.recommended.rules,
|
|
||||||
'react-refresh/only-export-components': [
|
|
||||||
'warn',
|
|
||||||
{ allowConstantExport: true },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
@@ -12,8 +12,23 @@
|
|||||||
<link rel="icon" type="image/gif" sizes="32x32" href="/favicon.gif" />
|
<link rel="icon" type="image/gif" sizes="32x32" href="/favicon.gif" />
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body class="light">
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
<!-- Google tag (gtag.js) -->
|
||||||
|
<script
|
||||||
|
async
|
||||||
|
src="https://www.googletagmanager.com/gtag/js?id=G-SQCJ92M03C"
|
||||||
|
></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() {
|
||||||
|
dataLayer.push(arguments);
|
||||||
|
}
|
||||||
|
gtag("js", new Date());
|
||||||
|
|
||||||
|
gtag("config", "G-SQCJ92M03C");
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,40 +1,58 @@
|
|||||||
{
|
{
|
||||||
"name": "furina.id",
|
"name": "furina-web",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc && vite build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"test": "NODE_OPTIONS=\"--loader ts-node/esm\" mocha \"./src/**/*.test.ts\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/react-query": "^5.52.2",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"clsx": "^2.1.1",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"dayjs": "^1.11.13",
|
"class-variance-authority": "^0.7.0",
|
||||||
"lucide-react": "^0.436.0",
|
"clsx": "^2.1.0",
|
||||||
"react": "^18.3.1",
|
"dayjs": "^1.11.10",
|
||||||
"react-dom": "^18.3.1",
|
"howler": "^2.2.4",
|
||||||
"react-loader-spinner": "^6.1.6",
|
"lucide-react": "^0.306.0",
|
||||||
"react-markdown": "^9.0.1",
|
"pixi.js": "^7.3.3",
|
||||||
"tailwind-merge": "^2.5.2"
|
"pocketbase": "^0.20.1",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-bottom-scroll-listener": "^5.1.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-helmet": "^6.1.0",
|
||||||
|
"react-query": "^3.39.3",
|
||||||
|
"react-router-dom": "^6.21.1",
|
||||||
|
"react-toastify": "^9.1.3",
|
||||||
|
"tailwind-merge": "^2.2.0",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"zustand": "^4.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.9.0",
|
"@types/chai": "^4.3.11",
|
||||||
"@types/node": "^22.5.1",
|
"@types/howler": "^2.2.11",
|
||||||
"@types/react": "^18.3.3",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/node": "^20.10.6",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@types/react": "^18.2.43",
|
||||||
"autoprefixer": "^10.4.20",
|
"@types/react-dom": "^18.2.17",
|
||||||
"eslint": "^9.9.0",
|
"@types/react-helmet": "^6.1.11",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
"@types/uuid": "^9.0.7",
|
||||||
"eslint-plugin-react-refresh": "^0.4.9",
|
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||||
"globals": "^15.9.0",
|
"@typescript-eslint/parser": "^6.14.0",
|
||||||
"postcss": "^8.4.41",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"tailwindcss": "^3.4.10",
|
"autoprefixer": "^10.4.16",
|
||||||
"typescript": "^5.5.3",
|
"chai": "^5.0.0",
|
||||||
"typescript-eslint": "^8.0.1",
|
"eslint": "^8.55.0",
|
||||||
"vite": "^5.4.1"
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.5",
|
||||||
|
"mocha": "^10.2.0",
|
||||||
|
"postcss": "^8.4.33",
|
||||||
|
"tailwindcss": "^3.4.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ export default {
|
|||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 836 B After Width: | Height: | Size: 836 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
import { ToastContainer } from "react-toastify";
|
||||||
|
import Router from "./Router";
|
||||||
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
|
import { QueryClient, QueryClientProvider } from "react-query";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [queryClient] = useState(new QueryClient());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<Router />
|
||||||
|
</QueryClientProvider>
|
||||||
|
<ToastContainer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import React, { Suspense } from "react";
|
||||||
|
import LoadingPage from "./pages/misc/loading-page";
|
||||||
|
|
||||||
|
const App = React.lazy(() => import("./App"));
|
||||||
|
|
||||||
|
const Loader = () => (
|
||||||
|
<Suspense fallback={<LoadingPage />}>
|
||||||
|
<App />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Loader;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { lazy } from "react";
|
||||||
|
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||||
|
import MainLayout from "./components/layouts/MainLayout";
|
||||||
|
import ErrorBoundaryPage from "./pages/errors/error-boundary/page";
|
||||||
|
|
||||||
|
const HomePage = lazy(() => import("./pages/home/page"));
|
||||||
|
const PatPatPage = lazy(() => import("./pages/pat-pat/page"));
|
||||||
|
const MyFurinaPage = lazy(() => import("./pages/my-furina/page"));
|
||||||
|
const ArtworksPage = lazy(() => import("./pages/artworks/page"));
|
||||||
|
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{ index: true, Component: HomePage },
|
||||||
|
{
|
||||||
|
Component: MainLayout,
|
||||||
|
children: [
|
||||||
|
{ path: "/pat-pat", Component: PatPatPage },
|
||||||
|
{ path: "/toodle", Component: MyFurinaPage },
|
||||||
|
{
|
||||||
|
path: "/treasures",
|
||||||
|
Component: ArtworksPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/treasures/:id",
|
||||||
|
Component: ArtworksPage,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ErrorBoundary: () => (
|
||||||
|
<MainLayout>
|
||||||
|
<ErrorBoundaryPage />
|
||||||
|
</MainLayout>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const Router = () => {
|
||||||
|
return <RouterProvider router={router} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Router;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
|
|
||||||
export const BASE_URL = "/api";
|
|
||||||
|
|
||||||
export const api = async <T = any>(path: string, init: RequestInit = {}) => {
|
|
||||||
const res = await fetch(BASE_URL + path, { ...init, credentials: "include" });
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const data = await res.json().catch(() => null);
|
|
||||||
const message = data?.message || res.statusText;
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = (await res.json()) as T;
|
|
||||||
return data;
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error("Invalid response");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
import furinaImg from "@/assets/furina.webp";
|
|
||||||
import hydroElement from "@/assets/hydro.svg";
|
|
||||||
import dotPattern from "@/assets/dotpattern.svg";
|
|
||||||
import logo from "@/assets/logo.svg";
|
|
||||||
import ParallaxView from "./components/parallax-view";
|
|
||||||
import ChatWindow from "./components/chat-window";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { useQuery } from "@tanstack/react-query";
|
|
||||||
import { CloudSun, GitPullRequest } from "lucide-react";
|
|
||||||
import { cn } from "./utils";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
const [isMounted, setIsMounted] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsMounted(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="bg-[#353771] min-h-screen max-h-[100dvh] overflow-hidden relative"
|
|
||||||
style={{
|
|
||||||
background: "linear-gradient(145deg, #353771 30%, #8ea6f4 150%)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ParallaxView
|
|
||||||
depth={0.005}
|
|
||||||
className="absolute inset-0 bg-white"
|
|
||||||
style={{
|
|
||||||
maskImage: `url(${dotPattern})`,
|
|
||||||
maskRepeat: "repeat",
|
|
||||||
background:
|
|
||||||
"radial-gradient(circle, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0) 90%)",
|
|
||||||
transform: "scale(1.2)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/* <ParallaxView
|
|
||||||
depth={0.01}
|
|
||||||
className="absolute w-[90vw] max-w-[800px] bottom-[10%] left-1/2"
|
|
||||||
>
|
|
||||||
<div className="animate-[wiggle_10s_ease-in-out_infinite] pointer-events-none">
|
|
||||||
<div className="-translate-x-[46%] rotate-6 w-full aspect-video bg-[#d0ecf7] drop-shadow-[-40px_-40px_0px_#869de8]" />
|
|
||||||
</div>
|
|
||||||
</ParallaxView> */}
|
|
||||||
<ParallaxView
|
|
||||||
depth={0.015}
|
|
||||||
className="absolute right-[10%] top-[10%] md:top-[10%]"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={hydroElement}
|
|
||||||
className="h-[80vw] md:h-[40vw] blur-sm opacity-20 pointer-events-none"
|
|
||||||
/>
|
|
||||||
</ParallaxView>
|
|
||||||
<ParallaxView
|
|
||||||
depth={0.005}
|
|
||||||
className="absolute left-1/2 -bottom-[5%] w-full"
|
|
||||||
>
|
|
||||||
<div className="animate-[updown_8s_ease-in-out_infinite] w-full">
|
|
||||||
<img
|
|
||||||
src={furinaImg}
|
|
||||||
className={cn(
|
|
||||||
"max-h-screen w-full aspect-[0.666] object-contain -translate-x-1/2 transition-all ease-out duration-500",
|
|
||||||
isMounted ? "opacity-100" : "opacity-0 scale-110"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ParallaxView>
|
|
||||||
|
|
||||||
<div className="absolute left-1/2 -translate-x-1/2 top-0 h-full w-full max-w-3xl">
|
|
||||||
<ParallaxView
|
|
||||||
depth={0.01}
|
|
||||||
className="absolute top-0 md:top-[10%] left-[10%] md:left-[1%]"
|
|
||||||
>
|
|
||||||
<div className="animate-[wiggle_10s_ease-in-out_infinite] pointer-events-none">
|
|
||||||
<img
|
|
||||||
src={logo}
|
|
||||||
className={cn(
|
|
||||||
"w-[120px] md:w-[200px] transition-all delay-100 duration-300",
|
|
||||||
isMounted ? "opacity-100" : "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ParallaxView>
|
|
||||||
|
|
||||||
<p className="absolute left-4 md:left-0 bottom-10 text-white text-sm">
|
|
||||||
Furina artwork by{" "}
|
|
||||||
<a
|
|
||||||
href="https://www.pixiv.net/en/users/98144454"
|
|
||||||
target="_blank"
|
|
||||||
rel="nofollow"
|
|
||||||
className="font-bold"
|
|
||||||
>
|
|
||||||
Rine
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p className="absolute left-4 md:left-auto md:right-0 bottom-4 md:bottom-10 text-white text-sm">
|
|
||||||
<GitPullRequest className="inline mr-2" size={16} />
|
|
||||||
Fork{" "}
|
|
||||||
<a
|
|
||||||
href="https://git.rul.sh/khairul169/furina.id"
|
|
||||||
target="_blank"
|
|
||||||
className="font-bold"
|
|
||||||
>
|
|
||||||
Repo
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<Clock
|
|
||||||
className={cn(
|
|
||||||
"transition-all delay-200 duration-500",
|
|
||||||
isMounted ? "opacity-100" : "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ChatWindow />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type ClockProps = {
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Clock = ({ className }: ClockProps) => {
|
|
||||||
const [time, setTime] = useState(new Date());
|
|
||||||
const { data: weather } = useQuery({
|
|
||||||
queryKey: ["forecast"],
|
|
||||||
queryFn: async () => {
|
|
||||||
const url =
|
|
||||||
"https://api.open-meteo.com/v1/forecast?latitude=-6.2297401&longitude=106.7469453¤t_weather=true";
|
|
||||||
const res = await fetch(url);
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(res.statusText);
|
|
||||||
}
|
|
||||||
const data = await res.json();
|
|
||||||
return data?.current_weather;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setTime(new Date());
|
|
||||||
}, 1000);
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ParallaxView
|
|
||||||
depth={0.01}
|
|
||||||
className={cn(
|
|
||||||
"bg-white/10 border border-white/40 backdrop-blur-sm text-white p-4 rounded-lg absolute right-4 md:right-0 bottom-24 md:bottom-[10%]",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<p className="text-right">{dayjs(time).format("dddd, DD MMM YYYY")}</p>
|
|
||||||
|
|
||||||
<p className="text-3xl md:text-5xl font-mono mt-1">
|
|
||||||
{dayjs(time).format("hh:mm:ss")}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{weather ? (
|
|
||||||
<div className="flex flex-row items-center gap-2 mt-0.5 justify-end md:text-2xl">
|
|
||||||
<CloudSun size={20} />
|
|
||||||
<p>
|
|
||||||
{weather?.temperature}
|
|
||||||
<span className="text-sm"> °C</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</ParallaxView>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="1024"
|
|
||||||
height="1024"
|
|
||||||
viewBox="0 0 270.93333 270.93333"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
|
||||||
sodipodi:docname="dotpattern.svg"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#000000"
|
|
||||||
borderopacity="0.25"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:zoom="0.68505863"
|
|
||||||
inkscape:cx="537.91016"
|
|
||||||
inkscape:cy="399.2359"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1009"
|
|
||||||
inkscape:window-x="-8"
|
|
||||||
inkscape:window-y="-8"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1" />
|
|
||||||
<defs
|
|
||||||
id="defs1">
|
|
||||||
<pattern
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#Polkadots-med"
|
|
||||||
preserveAspectRatio="xMidYMid"
|
|
||||||
id="pattern26"
|
|
||||||
patternTransform="scale(6)"
|
|
||||||
x="0"
|
|
||||||
y="0" />
|
|
||||||
<pattern
|
|
||||||
patternUnits="userSpaceOnUse"
|
|
||||||
width="10"
|
|
||||||
height="10"
|
|
||||||
patternTransform="translate(0,0) scale(10,10)"
|
|
||||||
preserveAspectRatio="xMidYMid"
|
|
||||||
id="Polkadots-med"
|
|
||||||
inkscape:stockid="Polka dots, medium"
|
|
||||||
inkscape:label="Polka dots medium"
|
|
||||||
inkscape:collect="always"
|
|
||||||
inkscape:isstock="true">
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="2.567"
|
|
||||||
cy="0.810"
|
|
||||||
r="0.15"
|
|
||||||
id="circle406" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="3.048"
|
|
||||||
cy="2.33"
|
|
||||||
r="0.15"
|
|
||||||
id="circle408" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="4.418"
|
|
||||||
cy="2.415"
|
|
||||||
r="0.15"
|
|
||||||
id="circle410" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.844"
|
|
||||||
cy="3.029"
|
|
||||||
r="0.15"
|
|
||||||
id="circle412" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="6.08"
|
|
||||||
cy="1.363"
|
|
||||||
r="0.15"
|
|
||||||
id="circle414" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="5.819"
|
|
||||||
cy="4.413"
|
|
||||||
r="0.15"
|
|
||||||
id="circle416" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="4.305"
|
|
||||||
cy="4.048"
|
|
||||||
r="0.15"
|
|
||||||
id="circle418" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="5.541"
|
|
||||||
cy="3.045"
|
|
||||||
r="0.15"
|
|
||||||
id="circle420" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="4.785"
|
|
||||||
cy="5.527"
|
|
||||||
r="0.15"
|
|
||||||
id="circle422" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="2.667"
|
|
||||||
cy="5.184"
|
|
||||||
r="0.15"
|
|
||||||
id="circle424" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="7.965"
|
|
||||||
cy="1.448"
|
|
||||||
r="0.15"
|
|
||||||
id="circle426" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="7.047"
|
|
||||||
cy="5.049"
|
|
||||||
r="0.15"
|
|
||||||
id="circle428" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="4.340"
|
|
||||||
cy="0.895"
|
|
||||||
r="0.15"
|
|
||||||
id="circle430" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="7.125"
|
|
||||||
cy="0.340"
|
|
||||||
r="0.15"
|
|
||||||
id="circle432" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="9.550"
|
|
||||||
cy="1.049"
|
|
||||||
r="0.15"
|
|
||||||
id="circle434" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="7.006"
|
|
||||||
cy="2.689"
|
|
||||||
r="0.15"
|
|
||||||
id="circle436" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="8.909"
|
|
||||||
cy="2.689"
|
|
||||||
r="0.15"
|
|
||||||
id="circle438" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="9.315"
|
|
||||||
cy="4.407"
|
|
||||||
r="0.15"
|
|
||||||
id="circle440" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="7.820"
|
|
||||||
cy="3.870"
|
|
||||||
r="0.15"
|
|
||||||
id="circle442" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="8.270"
|
|
||||||
cy="5.948"
|
|
||||||
r="0.15"
|
|
||||||
id="circle444" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="7.973"
|
|
||||||
cy="7.428"
|
|
||||||
r="0.15"
|
|
||||||
id="circle446" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="9.342"
|
|
||||||
cy="8.072"
|
|
||||||
r="0.15"
|
|
||||||
id="circle448" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="8.206"
|
|
||||||
cy="9.315"
|
|
||||||
r="0.15"
|
|
||||||
id="circle450" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="9.682"
|
|
||||||
cy="9.475"
|
|
||||||
r="0.15"
|
|
||||||
id="circle452" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="9.688"
|
|
||||||
cy="6.186"
|
|
||||||
r="0.15"
|
|
||||||
id="circle454" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="3.379"
|
|
||||||
cy="6.296"
|
|
||||||
r="0.15"
|
|
||||||
id="circle456" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="2.871"
|
|
||||||
cy="8.204"
|
|
||||||
r="0.15"
|
|
||||||
id="circle458" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="4.59"
|
|
||||||
cy="8.719"
|
|
||||||
r="0.15"
|
|
||||||
id="circle460" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="3.181"
|
|
||||||
cy="9.671"
|
|
||||||
r="0.15"
|
|
||||||
id="circle462" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="5.734"
|
|
||||||
cy="7.315"
|
|
||||||
r="0.15"
|
|
||||||
id="circle464" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="6.707"
|
|
||||||
cy="6.513"
|
|
||||||
r="0.15"
|
|
||||||
id="circle466" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="5.730"
|
|
||||||
cy="9.670"
|
|
||||||
r="0.15"
|
|
||||||
id="circle468" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="6.535"
|
|
||||||
cy="8.373"
|
|
||||||
r="0.15"
|
|
||||||
id="circle470" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="4.37"
|
|
||||||
cy="7.154"
|
|
||||||
r="0.15"
|
|
||||||
id="circle472" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="0.622"
|
|
||||||
cy="7.25"
|
|
||||||
r="0.15"
|
|
||||||
id="circle474" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="0.831"
|
|
||||||
cy="5.679"
|
|
||||||
r="0.15"
|
|
||||||
id="circle476" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.257"
|
|
||||||
cy="8.519"
|
|
||||||
r="0.15"
|
|
||||||
id="circle478" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.989"
|
|
||||||
cy="6.877"
|
|
||||||
r="0.15"
|
|
||||||
id="circle480" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="0.374"
|
|
||||||
cy="3.181"
|
|
||||||
r="0.15"
|
|
||||||
id="circle482" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.166"
|
|
||||||
cy="1.664"
|
|
||||||
r="0.15"
|
|
||||||
id="circle484" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.151"
|
|
||||||
cy="0.093"
|
|
||||||
r="0.15"
|
|
||||||
id="circle486" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.151"
|
|
||||||
cy="10.093"
|
|
||||||
r="0.15"
|
|
||||||
id="circle488" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="1.302"
|
|
||||||
cy="4.451"
|
|
||||||
r="0.15"
|
|
||||||
id="circle490" />
|
|
||||||
<circle
|
|
||||||
style="stroke:none"
|
|
||||||
cx="3.047"
|
|
||||||
cy="3.763"
|
|
||||||
r="0.15"
|
|
||||||
id="circle492" />
|
|
||||||
</pattern>
|
|
||||||
<clipPath
|
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath24">
|
|
||||||
<rect
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:bevel;paint-order:stroke markers fill;stop-color:#000000"
|
|
||||||
id="rect24"
|
|
||||||
width="44.999748"
|
|
||||||
height="77.937294"
|
|
||||||
x="251.53429"
|
|
||||||
y="222.73541" />
|
|
||||||
</clipPath>
|
|
||||||
<clipPath
|
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath6">
|
|
||||||
<path
|
|
||||||
id="path6"
|
|
||||||
style="stroke-width:0.1;stroke-linecap:square;paint-order:markers fill stroke;stop-color:#000000"
|
|
||||||
d="m -880.82767,-187.29769 h 1640.84 v 73.28155 h -1640.84 z" />
|
|
||||||
</clipPath>
|
|
||||||
<clipPath
|
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath52">
|
|
||||||
<path
|
|
||||||
id="path52"
|
|
||||||
style="stroke-width:0.1;stroke-linecap:square;paint-order:markers fill stroke;stop-color:#000000"
|
|
||||||
d="m -880.82767,-187.29769 h 1640.84 v 73.28155 h -1640.84 z" />
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="Lapis 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1">
|
|
||||||
<rect
|
|
||||||
style="fill:url(#pattern26);fill-opacity:1;fill-rule:nonzero;stroke-width:0.184635"
|
|
||||||
id="rect1"
|
|
||||||
width="270.93332"
|
|
||||||
height="270.93332"
|
|
||||||
x="0"
|
|
||||||
y="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 715 KiB |
|
Before Width: | Height: | Size: 134 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M368 368L144 144M368 144L144 368"/></svg>
|
||||||
|
After Width: | Height: | Size: 227 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><rect x="128" y="128" width="336" height="336" rx="57" ry="57" fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="32"/><path d="M383.5 128l.5-24a56.16 56.16 0 00-56-56H112a64.19 64.19 0 00-64 64v216a56.16 56.16 0 0056 56h24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg>
|
||||||
|
After Width: | Height: | Size: 430 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path d="M112 111v290c0 17.44 17 28.52 31 20.16l247.9-148.37c12.12-7.25 12.12-26.33 0-33.58L143 90.84c-14-8.36-31 2.72-31 20.16z" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/></svg>
|
||||||
|
After Width: | Height: | Size: 290 B |
|
After Width: | Height: | Size: 62 KiB |
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="78.461067mm"
|
||||||
|
height="99.857933mm"
|
||||||
|
viewBox="0 0 78.461067 99.857933"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
xml:space="preserve"
|
||||||
|
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
|
||||||
|
sodipodi:docname="furina-ahoge.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.5946522"
|
||||||
|
inkscape:cx="394.34816"
|
||||||
|
inkscape:cy="326.24112"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1080"
|
||||||
|
inkscape:window-x="1440"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="layer1" /><defs
|
||||||
|
id="defs2" /><g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-24.520501,-12.209419)"><g
|
||||||
|
id="g5320"
|
||||||
|
transform="translate(-100.11104,-36.484912)"><path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#5b6583;stroke-width:8.8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
|
||||||
|
d="m 197.99739,93.659439 c 4.4418,-17.901129 -13.8919,-42.069877 -33.59282,-40.489354 -23.43183,1.879839 -35.46139,18.690788 -35.37256,35.150097 0.14479,26.828548 12.24846,42.764298 63.18119,55.839708 -8.55629,-2.02869 -46.86753,-31.39828 -38.70961,-54.504893 4.95753,-14.041763 29.10872,-21.582069 44.4938,4.004442 z"
|
||||||
|
id="path456"
|
||||||
|
sodipodi:nodetypes="csscsc" /><path
|
||||||
|
id="path1066"
|
||||||
|
style="fill:#9cc1e5;fill-opacity:1;stroke:none;stroke-width:1.189;stroke-opacity:1"
|
||||||
|
d="m 165.21679,56.025521 c -2.05958,-0.02817 -4.11979,0.199132 -6.15156,0.703833 -18.14601,4.507547 -27.27536,25.607406 -23.58151,40.489518 4.36519,17.586938 25.89622,31.292728 41.14581,37.091278 -13.29913,-11.00875 -28.57405,-29.22322 -23.12572,-44.655157 4.95752,-14.041749 29.10834,-21.582079 44.49341,4.004406 0.5607,-2.259718 0.7577,-4.619495 0.63562,-7.014559 -4.66252,-17.756004 -19.03095,-30.422553 -33.41605,-30.619319 z" /></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,325 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="81.073494mm"
|
||||||
|
height="16.619713mm"
|
||||||
|
viewBox="0 0 81.073494 16.619713"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
sodipodi:docname="title-img.svg"
|
||||||
|
inkscape:version="1.2.2 (b0a84865, 2022-12-01)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.1893044"
|
||||||
|
inkscape:cx="70.209107"
|
||||||
|
inkscape:cy="136.63449"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1027"
|
||||||
|
inkscape:window-x="1440"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="linearGradient4461">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#dbedff;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop4457" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop4459" />
|
||||||
|
</linearGradient>
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="perspective-envelope"
|
||||||
|
up_left_point="12.49,13.981926"
|
||||||
|
up_right_point="91.72,13.981926"
|
||||||
|
down_left_point="10.385328,29.489294"
|
||||||
|
down_right_point="89.220182,29.489294"
|
||||||
|
id="path-effect2044"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
deform_type="envelope_deformation"
|
||||||
|
horizontal_mirror="false"
|
||||||
|
vertical_mirror="false"
|
||||||
|
overflow_perspective="false" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="perspective-envelope"
|
||||||
|
up_left_point="12.49,13.981926"
|
||||||
|
up_right_point="91.72,13.981926"
|
||||||
|
down_left_point="10.385328,29.489294"
|
||||||
|
down_right_point="89.220182,29.489294"
|
||||||
|
id="path-effect1949"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
deform_type="envelope_deformation"
|
||||||
|
horizontal_mirror="false"
|
||||||
|
vertical_mirror="false"
|
||||||
|
overflow_perspective="false" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="lattice2"
|
||||||
|
gridpoint0="10.385328,13.981926"
|
||||||
|
gridpoint1="89.220182,13.981926"
|
||||||
|
gridpoint2="10.385328,29.489294"
|
||||||
|
gridpoint3="89.220182,29.489294"
|
||||||
|
gridpoint4="30.094042,13.981926"
|
||||||
|
gridpoint5="69.511469,13.981926"
|
||||||
|
gridpoint6="30.094042,29.489294"
|
||||||
|
gridpoint7="69.511469,29.489294"
|
||||||
|
gridpoint8x9="49.802755,13.981926"
|
||||||
|
gridpoint10x11="49.802755,29.489294"
|
||||||
|
gridpoint12="10.385328,17.858768"
|
||||||
|
gridpoint13="89.220182,17.858768"
|
||||||
|
gridpoint14="10.385328,25.612452"
|
||||||
|
gridpoint15="89.220182,25.612452"
|
||||||
|
gridpoint16="30.094042,17.858768"
|
||||||
|
gridpoint17="69.511469,17.858768"
|
||||||
|
gridpoint18="30.094042,25.612452"
|
||||||
|
gridpoint19="69.511469,25.612452"
|
||||||
|
gridpoint20x21="49.802755,17.858768"
|
||||||
|
gridpoint22x23="49.802755,25.612452"
|
||||||
|
gridpoint24x26="10.385328,21.73561"
|
||||||
|
gridpoint25x27="89.220182,21.73561"
|
||||||
|
gridpoint28x30="30.094042,21.73561"
|
||||||
|
gridpoint29x31="69.511469,21.73561"
|
||||||
|
gridpoint32x33x34x35="49.802755,21.73561"
|
||||||
|
id="path-effect1947"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
horizontal_mirror="false"
|
||||||
|
vertical_mirror="false"
|
||||||
|
perimetral="false"
|
||||||
|
live_update="true" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="envelope"
|
||||||
|
id="path-effect1945"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
yy="true"
|
||||||
|
xx="true"
|
||||||
|
bendpath1="M 10.385328,13.981926 H 89.220182"
|
||||||
|
bendpath2="M 89.220182,13.981926 V 29.489294"
|
||||||
|
bendpath3="M 10.385328,29.489294 H 89.220182"
|
||||||
|
bendpath4="M 10.385328,13.981926 V 29.489294" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient4463"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7537"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7539"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7541"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7543"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7545"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7547"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7549"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7551"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient4461"
|
||||||
|
id="linearGradient7553"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="51.604469"
|
||||||
|
y1="3.4931529"
|
||||||
|
x2="63.823807"
|
||||||
|
y2="31.413006" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-11.22635,-13.981926)">
|
||||||
|
<g
|
||||||
|
id="g5211">
|
||||||
|
<g
|
||||||
|
aria-label="Furina.id"
|
||||||
|
id="g2040"
|
||||||
|
style="font-style:italic;font-size:23.6033px;font-family:Arial;-inkscape-font-specification:'Arial Italic';fill:#90c8ff;fill-opacity:0.861176;stroke:none;stroke-width:6.34755"
|
||||||
|
inkscape:path-effect="#path-effect2044"
|
||||||
|
transform="translate(0.66740693,1.1123449)">
|
||||||
|
<path
|
||||||
|
d="m 12.802259,17.616834 c 0.04493,-0.330446 0.01276,-0.558611 -0.09652,-0.684495 -0.09349,-0.125885 -0.290342,-0.188827 -0.590549,-0.188827 l 0.115325,-0.849719 h 9.482809 l -0.245662,1.770248 h -5.804912 l -0.478243,3.493288 h 4.851708 l -0.241853,1.746645 h -4.848976 l -0.862776,6.302081 h -2.85626 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2022"
|
||||||
|
inkscape:original-d="m 11.18784,17.616834 q 0,-0.495669 -0.188826,-0.684495 -0.165223,-0.188827 -0.613686,-0.188827 v -0.849719 h 9.44132 v 1.770248 h -5.782809 v 3.493288 h 4.838677 v 1.746645 h -4.838677 v 6.302081 H 11.18784 Z" />
|
||||||
|
<path
|
||||||
|
d="m 23.249105,20.52004 c 0.0395,-0.283239 -0.01658,-0.503537 -0.168267,-0.660892 -0.13592,-0.157355 -0.345957,-0.236033 -0.630099,-0.236033 l 0.08541,-0.613686 h 3.220922 c 0.347354,0 0.492314,0.204562 0.43488,0.613686 l -0.798549,5.688395 c -0.207645,1.47914 0.153025,2.21871 1.08201,2.21871 0.850257,0 1.53891,-0.472066 2.066141,-1.416198 l 0.791185,-5.593982 c 0.04006,-0.283239 -0.01559,-0.503537 -0.166972,-0.660892 -0.135611,-0.157355 -0.345493,-0.236033 -0.629636,-0.236033 l 0.08661,-0.613686 h 3.220923 c 0.157888,0 0.282019,0.07081 0.372385,0.21243 0.0926,0.125884 0.12657,0.275371 0.101929,0.448462 l -1.159241,8.143139 c -0.03136,0.220297 -0.0065,0.377653 0.07447,0.472066 0.09899,0.07868 0.298015,0.118016 0.597096,0.118016 l -0.08751,0.613686 -2.265935,0.330446 c -0.440615,0 -0.765778,-0.259636 -0.975559,-0.778908 -0.781686,0.519272 -1.605186,0.778908 -2.47068,0.778908 -1.227428,0 -2.157896,-0.322578 -2.79163,-0.967735 -0.631839,-0.660892 -0.851307,-1.683702 -0.658174,-3.068429 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2024"
|
||||||
|
inkscape:original-d="m 21.998125,20.52004 q 0,-0.424859 -0.259637,-0.660892 -0.236033,-0.236033 -0.660892,-0.236033 v -0.613686 h 3.210049 q 0.519272,0 0.519272,0.613686 v 5.688395 q 0,2.21871 1.392595,2.21871 1.274578,0 1.864661,-1.416198 V 20.52004 q 0,-0.424859 -0.259637,-0.660892 -0.236033,-0.236033 -0.660892,-0.236033 v -0.613686 h 3.210049 q 0.236033,0 0.401256,0.21243 0.165223,0.188826 0.165223,0.448462 v 8.143139 q 0,0.330446 0.14162,0.472066 0.165223,0.118016 0.613686,0.118016 v 0.613686 l -2.218711,0.330446 q -0.660892,0 -1.085751,-0.778908 -1.062149,0.778908 -2.36033,0.778908 -1.841058,0 -2.92681,-0.967735 -1.085751,-0.991338 -1.085751,-3.068429 z" />
|
||||||
|
<path
|
||||||
|
d="m 34.408293,19.292669 3.105951,-0.519273 c 0.17369,0 0.305597,0.07081 0.395713,0.21243 0.09238,0.125884 0.126081,0.275371 0.101124,0.448462 l -0.102094,0.708099 1.926392,-1.368991 2.417119,0.967735 -1.752566,2.124297 h -0.520553 c 0.03877,-0.267504 -0.09154,-0.511405 -0.390963,-0.731702 -0.281414,-0.236033 -0.611497,-0.354049 -0.990213,-0.354049 l -0.835552,0.377652 -1.160477,8.048726 h -2.856261 l 1.230819,-8.591602 c 0.0248,-0.173091 -0.02368,-0.330446 -0.145445,-0.472066 -0.119523,-0.157355 -0.28978,-0.236033 -0.51076,-0.236033 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2026"
|
||||||
|
inkscape:original-d="m 32.950031,19.292669 3.021223,-0.519273 q 0.259636,0 0.424859,0.21243 0.165223,0.188826 0.165223,0.448462 v 0.708099 l 1.723041,-1.368991 2.549156,0.967735 -1.439801,2.124297 h -0.519273 q 0,-0.401256 -0.495669,-0.731702 -0.472066,-0.354049 -1.038545,-0.354049 l -0.778909,0.377652 v 8.048726 h -2.855999 v -8.591602 q 0,-0.259636 -0.21243,-0.472066 -0.21243,-0.236033 -0.542876,-0.236033 z" />
|
||||||
|
<path
|
||||||
|
d="m 43.964363,17.097562 c 0.06202,-0.424859 0.260389,-0.755306 0.595127,-0.991339 0.35289,-0.251769 0.790145,-0.377653 1.311727,-0.377653 0.521583,0 0.914042,0.125884 1.177341,0.377653 0.281366,0.236033 0.390785,0.56648 0.328294,0.991339 -0.05786,0.393388 -0.26326,0.715966 -0.616152,0.967735 -0.35053,0.236033 -0.778447,0.35405 -1.283786,0.35405 -0.505338,0 -0.898667,-0.118017 -1.180021,-0.35405 -0.279104,-0.251769 -0.38996,-0.574347 -0.33253,-0.967735 z m 1.442629,12.108493 h -2.856261 l 1.220782,-8.355569 c 0.03908,-0.267504 0.01029,-0.448462 -0.0864,-0.542875 -0.09439,-0.110149 -0.283639,-0.165224 -0.567735,-0.165224 l 0.08951,-0.613685 3.11413,-0.56648 c 0.394727,0 0.550446,0.28324 0.467158,0.849719 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2028"
|
||||||
|
inkscape:original-d="m 42.1553,17.097562 q 0,-0.637289 0.448462,-0.991339 0.472066,-0.377653 1.250975,-0.377653 0.778909,0 1.227372,0.377653 0.472066,0.35405 0.472066,0.991339 0,0.590082 -0.472066,0.967735 -0.472066,0.35405 -1.227372,0.35405 -0.755305,0 -1.227371,-0.35405 Q 42.1553,17.687644 42.1553,17.097562 Z m 3.210048,12.108493 h -2.855999 v -8.355569 q 0,-0.401256 -0.165223,-0.542875 -0.165223,-0.165224 -0.590083,-0.165224 v -0.613685 l 3.021223,-0.56648 q 0.590082,0 0.590082,0.849719 z" />
|
||||||
|
<path
|
||||||
|
d="m 49.226654,20.614453 c 0.04188,-0.283239 0.01455,-0.472066 -0.08197,-0.566479 -0.08075,-0.09441 -0.271078,-0.14162 -0.57098,-0.14162 l 0.09058,-0.613685 3.108336,-0.519273 c 0.39475,0 0.552328,0.267504 0.472733,0.802512 l -0.07726,0.519273 c 1.27422,-0.818248 2.456298,-1.227372 3.545777,-1.227372 0.963163,0 1.614123,0.291107 1.952761,0.873322 0.35426,0.582215 0.431704,1.534215 0.232455,2.856 l -0.818345,5.428759 c -0.05693,0.377653 0.127099,0.566479 0.552081,0.566479 l -0.09264,0.613686 h -2.974288 c -0.141633,0 -0.259273,-0.05507 -0.352926,-0.165223 -0.07557,-0.125885 -0.102742,-0.259637 -0.08152,-0.401257 l 0.873449,-5.830015 c 0.120232,-0.802512 0.121505,-1.33752 0.0038,-1.605024 -0.115378,-0.283239 -0.330876,-0.424859 -0.646473,-0.424859 -0.362935,0 -0.782937,0.118016 -1.259954,0.354049 -0.458828,0.220297 -0.865035,0.511405 -1.218612,0.873322 l -1.071026,7.199007 h -2.856261 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2030"
|
||||||
|
inkscape:original-d="m 47.9145,20.614453 q 0,-0.424859 -0.165223,-0.566479 -0.14162,-0.14162 -0.590083,-0.14162 v -0.613685 l 3.021223,-0.519273 q 0.590082,0 0.590082,0.802512 v 0.519273 q 1.723041,-1.227372 3.351669,-1.227372 1.439801,0 2.07709,0.873322 0.660893,0.873322 0.660893,2.856 v 5.428759 q 0,0.566479 0.637289,0.566479 v 0.613686 h -2.974016 q -0.21243,0 -0.377653,-0.165223 -0.14162,-0.188827 -0.14162,-0.401257 V 22.80956 q 0,-1.203768 -0.236033,-1.605024 -0.236033,-0.424859 -0.708099,-0.424859 -0.542875,0 -1.203768,0.354049 -0.637289,0.330446 -1.085752,0.873322 v 7.199007 H 47.9145 Z" />
|
||||||
|
<path
|
||||||
|
d="m 68.46948,29.206055 h -2.336941 c -0.723901,0 -1.231902,-0.330446 -1.52413,-0.991339 -0.540631,0.755305 -1.432451,1.132958 -2.675616,1.132958 -0.928439,0 -1.703023,-0.236033 -2.323911,-0.708099 -0.602975,-0.487801 -0.823616,-1.26671 -0.661764,-2.336726 0.337985,-2.234446 2.383706,-3.509024 6.137727,-3.823735 0.07201,-0.676628 0.02713,-1.156562 -0.13465,-1.439801 -0.159415,-0.298975 -0.49163,-0.448463 -0.996615,-0.448463 -1.041532,0 -1.878432,0.574347 -2.510444,1.723041 h -0.567793 l -0.572928,-1.841057 c 1.404397,-1.038545 2.975321,-1.557818 4.712146,-1.557818 1.215777,0 2.09113,0.314711 2.625866,0.944132 0.552683,0.613686 0.719839,1.628628 0.501658,3.044826 l -0.72727,4.72066 c 0.0096,0.346181 0.09678,0.59795 0.261425,0.755305 0.16705,0.14162 0.46306,0.21243 0.888043,0.21243 z m -3.579715,-5.452363 c -0.977129,0.204562 -1.71325,0.535008 -2.208364,0.991339 -0.476836,0.440595 -0.757119,0.936264 -0.840938,1.487008 -0.1389,0.912661 0.232515,1.368991 1.114242,1.368991 0.440864,0 0.769542,-0.14162 0.986058,-0.424859 0.232298,-0.28324 0.447478,-0.763174 0.645545,-1.439801 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2032"
|
||||||
|
inkscape:original-d="m 68.425725,29.206055 h -2.336727 q -1.085752,0 -1.675834,-0.991339 -0.637289,1.132958 -2.50195,1.132958 -1.392595,0 -2.43114,-0.708099 -1.014942,-0.731702 -1.014942,-2.336726 0,-3.351669 5.546776,-3.823735 -0.04721,-1.014942 -0.35405,-1.439801 -0.306843,-0.448463 -1.062148,-0.448463 -1.557818,0 -2.242314,1.723041 h -0.566479 l -0.849719,-1.841057 q 1.864661,-1.557818 4.461024,-1.557818 1.817454,0 2.761586,0.944132 0.967735,0.920529 0.967735,3.044826 v 4.72066 q 0.09441,0.519272 0.377653,0.755305 0.28324,0.21243 0.920529,0.21243 z m -4.413817,-5.452363 q -1.416198,0.306843 -2.053487,0.991339 -0.613686,0.660892 -0.613686,1.487008 0,1.368991 1.321785,1.368991 0.660892,0 0.920528,-0.424859 0.28324,-0.42486 0.42486,-1.439801 z" />
|
||||||
|
<path
|
||||||
|
d="m 70.85683,29.489294 c -0.456331,0 -0.809043,-0.110149 -1.058166,-0.330446 -0.249165,-0.220297 -0.344538,-0.519272 -0.286088,-0.896925 0.05601,-0.361917 0.241557,-0.645157 0.556657,-0.849719 0.317575,-0.220297 0.704721,-0.330446 1.161407,-0.330446 0.472433,0 0.833247,0.110149 1.082411,0.330446 0.251575,0.204562 0.34916,0.487802 0.292785,0.849719 -0.05883,0.377653 -0.247044,0.676628 -0.56462,0.896925 -0.317535,0.220297 -0.71232,0.330446 -1.184386,0.330446 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2034"
|
||||||
|
inkscape:original-d="m 70.85683,29.489294 q -0.684496,0 -1.109355,-0.330446 -0.42486,-0.330446 -0.42486,-0.896925 0,-0.542876 0.42486,-0.849719 0.424859,-0.330446 1.109355,-0.330446 0.708099,0 1.132958,0.330446 0.42486,0.306843 0.42486,0.849719 0,0.566479 -0.42486,0.896925 -0.424859,0.330446 -1.132958,0.330446 z" />
|
||||||
|
<path
|
||||||
|
d="m 76.714684,17.097562 c 0.0665,-0.424859 0.268353,-0.755306 0.60558,-0.991339 0.355545,-0.251769 0.794127,-0.377653 1.315709,-0.377653 0.521583,0 0.912714,0.125884 1.173359,0.377653 0.278878,0.236033 0.384813,0.56648 0.317842,0.991339 -0.06201,0.393388 -0.270808,0.715966 -0.626355,0.967735 -0.353019,0.236033 -0.78218,0.35405 -1.287519,0.35405 -0.505339,0 -0.897423,-0.118017 -1.176289,-0.35405 -0.27645,-0.251769 -0.383905,-0.574347 -0.322327,-0.967735 z m 1.314965,12.108493 h -2.85626 l 1.308878,-8.355569 c 0.0419,-0.267504 0.01501,-0.448462 -0.08067,-0.542875 -0.09323,-0.110149 -0.281897,-0.165224 -0.565992,-0.165224 l 0.09598,-0.613685 3.120102,-0.56648 c 0.394727,0 0.54746,0.28324 0.458199,0.849719 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2036"
|
||||||
|
inkscape:original-d="m 74.77497,17.097562 q 0,-0.637289 0.448463,-0.991339 0.472066,-0.377653 1.250975,-0.377653 0.778909,0 1.227372,0.377653 0.472066,0.35405 0.472066,0.991339 0,0.590082 -0.472066,0.967735 -0.472066,0.35405 -1.227372,0.35405 -0.755306,0 -1.227372,-0.35405 -0.472066,-0.377653 -0.472066,-0.967735 z m 3.210049,12.108493 H 75.12902 v -8.355569 q 0,-0.401256 -0.165223,-0.542875 -0.165223,-0.165224 -0.590083,-0.165224 v -0.613685 l 3.021223,-0.56648 q 0.590082,0 0.590082,0.849719 z" />
|
||||||
|
<path
|
||||||
|
d="m 81.320323,27.95508 c -0.716436,-0.944132 -0.947935,-2.21871 -0.6941,-3.823735 0.253835,-1.605024 0.880155,-2.871734 1.879343,-3.800131 1.017984,-0.944132 2.18243,-1.416198 3.492943,-1.416198 0.805255,0 1.459341,0.204562 1.962145,0.613686 l 0.586382,-3.658512 c 0.04792,-0.298975 0.03076,-0.487802 -0.05148,-0.566479 -0.07972,-0.09441 -0.277666,-0.14162 -0.593834,-0.14162 l 0.09821,-0.613686 3.127021,-0.566479 c 0.395361,0 0.558797,0.21243 0.490309,0.637289 l -2.351427,14.58684 h -2.856262 l 0.12106,-0.755306 c -0.505021,0.59795 -1.245296,0.896925 -2.220943,0.896925 -1.274637,0 -2.270964,-0.464198 -2.989373,-1.392594 z m 2.33611,-3.847338 c -0.155244,0.975603 -0.105533,1.801718 0.149014,2.478346 0.25439,0.676628 0.727918,1.014942 1.420704,1.014942 0.425119,0 0.934318,-0.180959 1.527691,-0.542876 L 87.06784,25.09908 c 0.257252,-1.605024 0.299416,-2.753718 0.126399,-3.446081 -0.157365,-0.692364 -0.512255,-1.038546 -1.064578,-1.038546 -0.615446,0 -1.144046,0.346182 -1.585688,1.038546 -0.438989,0.676628 -0.734799,1.494876 -0.88754,2.454743 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:#90c8ff;fill-opacity:0.861176;stroke:none"
|
||||||
|
id="path2038"
|
||||||
|
inkscape:original-d="m 81.077043,27.95508 q -1.298181,-1.416198 -1.298181,-3.823735 0,-2.407536 1.274578,-3.800131 1.298181,-1.416198 3.257255,-1.416198 1.203768,0 2.053487,0.613686 V 15.87019 q 0,-0.448463 -0.14162,-0.566479 -0.141619,-0.14162 -0.613685,-0.14162 v -0.613686 l 3.021222,-0.566479 q 0.590083,0 0.590083,0.637289 v 14.58684 h -2.856 v -0.755306 q -0.613686,0.896925 -2.07709,0.896925 -1.911867,0 -3.210049,-1.392594 z m 1.723041,-3.847338 q 0,1.463404 0.542876,2.478346 0.542876,1.014942 1.581421,1.014942 0.637289,0 1.439801,-0.542876 V 25.09908 q 0,-2.407536 -0.424859,-3.446081 -0.401256,-1.038546 -1.227372,-1.038546 -0.920528,0 -1.416198,1.038546 -0.495669,1.014942 -0.495669,2.454743 z" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
aria-label="Furina.id"
|
||||||
|
id="text236"
|
||||||
|
style="font-style:italic;font-size:23.6033px;font-family:Arial;-inkscape-font-specification:'Arial Italic';fill:url(#linearGradient4463);fill-opacity:1;stroke:none;stroke-width:6.34755"
|
||||||
|
inkscape:path-effect="#path-effect1949">
|
||||||
|
<path
|
||||||
|
d="m 12.802259,17.616834 c 0.04493,-0.330446 0.01276,-0.558611 -0.09652,-0.684495 -0.09349,-0.125885 -0.290342,-0.188827 -0.590549,-0.188827 l 0.115325,-0.849719 h 9.482809 l -0.245662,1.770248 h -5.804912 l -0.478243,3.493288 h 4.851708 l -0.241853,1.746645 h -4.848976 l -0.862776,6.302081 h -2.85626 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7537);fill-opacity:1;stroke:none"
|
||||||
|
id="path1877"
|
||||||
|
inkscape:original-d="m 11.18784,17.616834 q 0,-0.495669 -0.188826,-0.684495 -0.165223,-0.188827 -0.613686,-0.188827 v -0.849719 h 9.44132 v 1.770248 h -5.782809 v 3.493288 h 4.838677 v 1.746645 h -4.838677 v 6.302081 H 11.18784 Z" />
|
||||||
|
<path
|
||||||
|
d="m 23.249105,20.52004 c 0.0395,-0.283239 -0.01658,-0.503537 -0.168267,-0.660892 -0.13592,-0.157355 -0.345957,-0.236033 -0.630099,-0.236033 l 0.08541,-0.613686 h 3.220922 c 0.347354,0 0.492314,0.204562 0.43488,0.613686 l -0.798549,5.688395 c -0.207645,1.47914 0.153025,2.21871 1.08201,2.21871 0.850257,0 1.53891,-0.472066 2.066141,-1.416198 l 0.791185,-5.593982 c 0.04006,-0.283239 -0.01559,-0.503537 -0.166972,-0.660892 -0.135611,-0.157355 -0.345493,-0.236033 -0.629636,-0.236033 l 0.08661,-0.613686 h 3.220923 c 0.157888,0 0.282019,0.07081 0.372385,0.21243 0.0926,0.125884 0.12657,0.275371 0.101929,0.448462 l -1.159241,8.143139 c -0.03136,0.220297 -0.0065,0.377653 0.07447,0.472066 0.09899,0.07868 0.298015,0.118016 0.597096,0.118016 l -0.08751,0.613686 -2.265935,0.330446 c -0.440615,0 -0.765778,-0.259636 -0.975559,-0.778908 -0.781686,0.519272 -1.605186,0.778908 -2.47068,0.778908 -1.227428,0 -2.157896,-0.322578 -2.79163,-0.967735 -0.631839,-0.660892 -0.851307,-1.683702 -0.658174,-3.068429 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7539);fill-opacity:1;stroke:none"
|
||||||
|
id="path1879"
|
||||||
|
inkscape:original-d="m 21.998125,20.52004 q 0,-0.424859 -0.259637,-0.660892 -0.236033,-0.236033 -0.660892,-0.236033 v -0.613686 h 3.210049 q 0.519272,0 0.519272,0.613686 v 5.688395 q 0,2.21871 1.392595,2.21871 1.274578,0 1.864661,-1.416198 V 20.52004 q 0,-0.424859 -0.259637,-0.660892 -0.236033,-0.236033 -0.660892,-0.236033 v -0.613686 h 3.210049 q 0.236033,0 0.401256,0.21243 0.165223,0.188826 0.165223,0.448462 v 8.143139 q 0,0.330446 0.14162,0.472066 0.165223,0.118016 0.613686,0.118016 v 0.613686 l -2.218711,0.330446 q -0.660892,0 -1.085751,-0.778908 -1.062149,0.778908 -2.36033,0.778908 -1.841058,0 -2.92681,-0.967735 -1.085751,-0.991338 -1.085751,-3.068429 z" />
|
||||||
|
<path
|
||||||
|
d="m 34.408293,19.292669 3.105951,-0.519273 c 0.17369,0 0.305597,0.07081 0.395713,0.21243 0.09238,0.125884 0.126081,0.275371 0.101124,0.448462 l -0.102094,0.708099 1.926392,-1.368991 2.417119,0.967735 -1.752566,2.124297 h -0.520553 c 0.03877,-0.267504 -0.09154,-0.511405 -0.390963,-0.731702 -0.281414,-0.236033 -0.611497,-0.354049 -0.990213,-0.354049 l -0.835552,0.377652 -1.160477,8.048726 h -2.856261 l 1.230819,-8.591602 c 0.0248,-0.173091 -0.02368,-0.330446 -0.145445,-0.472066 -0.119523,-0.157355 -0.28978,-0.236033 -0.51076,-0.236033 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7541);fill-opacity:1;stroke:none"
|
||||||
|
id="path1881"
|
||||||
|
inkscape:original-d="m 32.950031,19.292669 3.021223,-0.519273 q 0.259636,0 0.424859,0.21243 0.165223,0.188826 0.165223,0.448462 v 0.708099 l 1.723041,-1.368991 2.549156,0.967735 -1.439801,2.124297 h -0.519273 q 0,-0.401256 -0.495669,-0.731702 -0.472066,-0.354049 -1.038545,-0.354049 l -0.778909,0.377652 v 8.048726 h -2.855999 v -8.591602 q 0,-0.259636 -0.21243,-0.472066 -0.21243,-0.236033 -0.542876,-0.236033 z" />
|
||||||
|
<path
|
||||||
|
d="m 43.964363,17.097562 c 0.06202,-0.424859 0.260389,-0.755306 0.595127,-0.991339 0.35289,-0.251769 0.790145,-0.377653 1.311727,-0.377653 0.521583,0 0.914042,0.125884 1.177341,0.377653 0.281366,0.236033 0.390785,0.56648 0.328294,0.991339 -0.05786,0.393388 -0.26326,0.715966 -0.616152,0.967735 -0.35053,0.236033 -0.778447,0.35405 -1.283786,0.35405 -0.505338,0 -0.898667,-0.118017 -1.180021,-0.35405 -0.279104,-0.251769 -0.38996,-0.574347 -0.33253,-0.967735 z m 1.442629,12.108493 h -2.856261 l 1.220782,-8.355569 c 0.03908,-0.267504 0.01029,-0.448462 -0.0864,-0.542875 -0.09439,-0.110149 -0.283639,-0.165224 -0.567735,-0.165224 l 0.08951,-0.613685 3.11413,-0.56648 c 0.394727,0 0.550446,0.28324 0.467158,0.849719 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7543);fill-opacity:1;stroke:none"
|
||||||
|
id="path1883"
|
||||||
|
inkscape:original-d="m 42.1553,17.097562 q 0,-0.637289 0.448462,-0.991339 0.472066,-0.377653 1.250975,-0.377653 0.778909,0 1.227372,0.377653 0.472066,0.35405 0.472066,0.991339 0,0.590082 -0.472066,0.967735 -0.472066,0.35405 -1.227372,0.35405 -0.755305,0 -1.227371,-0.35405 Q 42.1553,17.687644 42.1553,17.097562 Z m 3.210048,12.108493 h -2.855999 v -8.355569 q 0,-0.401256 -0.165223,-0.542875 -0.165223,-0.165224 -0.590083,-0.165224 v -0.613685 l 3.021223,-0.56648 q 0.590082,0 0.590082,0.849719 z" />
|
||||||
|
<path
|
||||||
|
d="m 49.226654,20.614453 c 0.04188,-0.283239 0.01455,-0.472066 -0.08197,-0.566479 -0.08075,-0.09441 -0.271078,-0.14162 -0.57098,-0.14162 l 0.09058,-0.613685 3.108336,-0.519273 c 0.39475,0 0.552328,0.267504 0.472733,0.802512 l -0.07726,0.519273 c 1.27422,-0.818248 2.456298,-1.227372 3.545777,-1.227372 0.963163,0 1.614123,0.291107 1.952761,0.873322 0.35426,0.582215 0.431704,1.534215 0.232455,2.856 l -0.818345,5.428759 c -0.05693,0.377653 0.127099,0.566479 0.552081,0.566479 l -0.09264,0.613686 h -2.974288 c -0.141633,0 -0.259273,-0.05507 -0.352926,-0.165223 -0.07557,-0.125885 -0.102742,-0.259637 -0.08152,-0.401257 l 0.873449,-5.830015 c 0.120232,-0.802512 0.121505,-1.33752 0.0038,-1.605024 -0.115378,-0.283239 -0.330876,-0.424859 -0.646473,-0.424859 -0.362935,0 -0.782937,0.118016 -1.259954,0.354049 -0.458828,0.220297 -0.865035,0.511405 -1.218612,0.873322 l -1.071026,7.199007 h -2.856261 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7545);fill-opacity:1;stroke:none"
|
||||||
|
id="path1885"
|
||||||
|
inkscape:original-d="m 47.9145,20.614453 q 0,-0.424859 -0.165223,-0.566479 -0.14162,-0.14162 -0.590083,-0.14162 v -0.613685 l 3.021223,-0.519273 q 0.590082,0 0.590082,0.802512 v 0.519273 q 1.723041,-1.227372 3.351669,-1.227372 1.439801,0 2.07709,0.873322 0.660893,0.873322 0.660893,2.856 v 5.428759 q 0,0.566479 0.637289,0.566479 v 0.613686 h -2.974016 q -0.21243,0 -0.377653,-0.165223 -0.14162,-0.188827 -0.14162,-0.401257 V 22.80956 q 0,-1.203768 -0.236033,-1.605024 -0.236033,-0.424859 -0.708099,-0.424859 -0.542875,0 -1.203768,0.354049 -0.637289,0.330446 -1.085752,0.873322 v 7.199007 H 47.9145 Z" />
|
||||||
|
<path
|
||||||
|
d="m 68.46948,29.206055 h -2.336941 c -0.723901,0 -1.231902,-0.330446 -1.52413,-0.991339 -0.540631,0.755305 -1.432451,1.132958 -2.675616,1.132958 -0.928439,0 -1.703023,-0.236033 -2.323911,-0.708099 -0.602975,-0.487801 -0.823616,-1.26671 -0.661764,-2.336726 0.337985,-2.234446 2.383706,-3.509024 6.137727,-3.823735 0.07201,-0.676628 0.02713,-1.156562 -0.13465,-1.439801 -0.159415,-0.298975 -0.49163,-0.448463 -0.996615,-0.448463 -1.041532,0 -1.878432,0.574347 -2.510444,1.723041 h -0.567793 l -0.572928,-1.841057 c 1.404397,-1.038545 2.975321,-1.557818 4.712146,-1.557818 1.215777,0 2.09113,0.314711 2.625866,0.944132 0.552683,0.613686 0.719839,1.628628 0.501658,3.044826 l -0.72727,4.72066 c 0.0096,0.346181 0.09678,0.59795 0.261425,0.755305 0.16705,0.14162 0.46306,0.21243 0.888043,0.21243 z m -3.579715,-5.452363 c -0.977129,0.204562 -1.71325,0.535008 -2.208364,0.991339 -0.476836,0.440595 -0.757119,0.936264 -0.840938,1.487008 -0.1389,0.912661 0.232515,1.368991 1.114242,1.368991 0.440864,0 0.769542,-0.14162 0.986058,-0.424859 0.232298,-0.28324 0.447478,-0.763174 0.645545,-1.439801 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7547);fill-opacity:1;stroke:none"
|
||||||
|
id="path1887"
|
||||||
|
inkscape:original-d="m 68.425725,29.206055 h -2.336727 q -1.085752,0 -1.675834,-0.991339 -0.637289,1.132958 -2.50195,1.132958 -1.392595,0 -2.43114,-0.708099 -1.014942,-0.731702 -1.014942,-2.336726 0,-3.351669 5.546776,-3.823735 -0.04721,-1.014942 -0.35405,-1.439801 -0.306843,-0.448463 -1.062148,-0.448463 -1.557818,0 -2.242314,1.723041 h -0.566479 l -0.849719,-1.841057 q 1.864661,-1.557818 4.461024,-1.557818 1.817454,0 2.761586,0.944132 0.967735,0.920529 0.967735,3.044826 v 4.72066 q 0.09441,0.519272 0.377653,0.755305 0.28324,0.21243 0.920529,0.21243 z m -4.413817,-5.452363 q -1.416198,0.306843 -2.053487,0.991339 -0.613686,0.660892 -0.613686,1.487008 0,1.368991 1.321785,1.368991 0.660892,0 0.920528,-0.424859 0.28324,-0.42486 0.42486,-1.439801 z" />
|
||||||
|
<path
|
||||||
|
d="m 70.85683,29.489294 c -0.456331,0 -0.809043,-0.110149 -1.058166,-0.330446 -0.249165,-0.220297 -0.344538,-0.519272 -0.286088,-0.896925 0.05601,-0.361917 0.241557,-0.645157 0.556657,-0.849719 0.317575,-0.220297 0.704721,-0.330446 1.161407,-0.330446 0.472433,0 0.833247,0.110149 1.082411,0.330446 0.251575,0.204562 0.34916,0.487802 0.292785,0.849719 -0.05883,0.377653 -0.247044,0.676628 -0.56462,0.896925 -0.317535,0.220297 -0.71232,0.330446 -1.184386,0.330446 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7549);fill-opacity:1;stroke:none"
|
||||||
|
id="path1889"
|
||||||
|
inkscape:original-d="m 70.85683,29.489294 q -0.684496,0 -1.109355,-0.330446 -0.42486,-0.330446 -0.42486,-0.896925 0,-0.542876 0.42486,-0.849719 0.424859,-0.330446 1.109355,-0.330446 0.708099,0 1.132958,0.330446 0.42486,0.306843 0.42486,0.849719 0,0.566479 -0.42486,0.896925 -0.424859,0.330446 -1.132958,0.330446 z" />
|
||||||
|
<path
|
||||||
|
d="m 76.714684,17.097562 c 0.0665,-0.424859 0.268353,-0.755306 0.60558,-0.991339 0.355545,-0.251769 0.794127,-0.377653 1.315709,-0.377653 0.521583,0 0.912714,0.125884 1.173359,0.377653 0.278878,0.236033 0.384813,0.56648 0.317842,0.991339 -0.06201,0.393388 -0.270808,0.715966 -0.626355,0.967735 -0.353019,0.236033 -0.78218,0.35405 -1.287519,0.35405 -0.505339,0 -0.897423,-0.118017 -1.176289,-0.35405 -0.27645,-0.251769 -0.383905,-0.574347 -0.322327,-0.967735 z m 1.314965,12.108493 h -2.85626 l 1.308878,-8.355569 c 0.0419,-0.267504 0.01501,-0.448462 -0.08067,-0.542875 -0.09323,-0.110149 -0.281897,-0.165224 -0.565992,-0.165224 l 0.09598,-0.613685 3.120102,-0.56648 c 0.394727,0 0.54746,0.28324 0.458199,0.849719 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7551);fill-opacity:1;stroke:none"
|
||||||
|
id="path1891"
|
||||||
|
inkscape:original-d="m 74.77497,17.097562 q 0,-0.637289 0.448463,-0.991339 0.472066,-0.377653 1.250975,-0.377653 0.778909,0 1.227372,0.377653 0.472066,0.35405 0.472066,0.991339 0,0.590082 -0.472066,0.967735 -0.472066,0.35405 -1.227372,0.35405 -0.755306,0 -1.227372,-0.35405 -0.472066,-0.377653 -0.472066,-0.967735 z m 3.210049,12.108493 H 75.12902 v -8.355569 q 0,-0.401256 -0.165223,-0.542875 -0.165223,-0.165224 -0.590083,-0.165224 v -0.613685 l 3.021223,-0.56648 q 0.590082,0 0.590082,0.849719 z" />
|
||||||
|
<path
|
||||||
|
d="m 81.320323,27.95508 c -0.716436,-0.944132 -0.947935,-2.21871 -0.6941,-3.823735 0.253835,-1.605024 0.880155,-2.871734 1.879343,-3.800131 1.017984,-0.944132 2.18243,-1.416198 3.492943,-1.416198 0.805255,0 1.459341,0.204562 1.962145,0.613686 l 0.586382,-3.658512 c 0.04792,-0.298975 0.03076,-0.487802 -0.05148,-0.566479 -0.07972,-0.09441 -0.277666,-0.14162 -0.593834,-0.14162 l 0.09821,-0.613686 3.127021,-0.566479 c 0.395361,0 0.558797,0.21243 0.490309,0.637289 l -2.351427,14.58684 h -2.856262 l 0.12106,-0.755306 c -0.505021,0.59795 -1.245296,0.896925 -2.220943,0.896925 -1.274637,0 -2.270964,-0.464198 -2.989373,-1.392594 z m 2.33611,-3.847338 c -0.155244,0.975603 -0.105533,1.801718 0.149014,2.478346 0.25439,0.676628 0.727918,1.014942 1.420704,1.014942 0.425119,0 0.934318,-0.180959 1.527691,-0.542876 L 87.06784,25.09908 c 0.257252,-1.605024 0.299416,-2.753718 0.126399,-3.446081 -0.157365,-0.692364 -0.512255,-1.038546 -1.064578,-1.038546 -0.615446,0 -1.144046,0.346182 -1.585688,1.038546 -0.438989,0.676628 -0.734799,1.494876 -0.88754,2.454743 z"
|
||||||
|
style="font-style:normal;font-family:Hostilica;-inkscape-font-specification:Hostilica;fill:url(#linearGradient7553);fill-opacity:1;stroke:none"
|
||||||
|
id="path1893"
|
||||||
|
inkscape:original-d="m 81.077043,27.95508 q -1.298181,-1.416198 -1.298181,-3.823735 0,-2.407536 1.274578,-3.800131 1.298181,-1.416198 3.257255,-1.416198 1.203768,0 2.053487,0.613686 V 15.87019 q 0,-0.448463 -0.14162,-0.566479 -0.141619,-0.14162 -0.613685,-0.14162 v -0.613686 l 3.021222,-0.566479 q 0.590083,0 0.590083,0.637289 v 14.58684 h -2.856 v -0.755306 q -0.613686,0.896925 -2.07709,0.896925 -1.911867,0 -3.210049,-1.392594 z m 1.723041,-3.847338 q 0,1.463404 0.542876,2.478346 0.542876,1.014942 1.581421,1.014942 0.637289,0 1.439801,-0.542876 V 25.09908 q 0,-2.407536 -0.424859,-3.446081 -0.401256,-1.038546 -1.227372,-1.038546 -0.920528,0 -1.416198,1.038546 -0.495669,1.014942 -0.495669,2.454743 z" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,264 +0,0 @@
|
|||||||
import { useIsMobile } from "@/hooks/useScreen";
|
|
||||||
import { cn } from "@/utils";
|
|
||||||
import { MessageSquareQuote, SendIcon } from "lucide-react";
|
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
|
||||||
import furinaAvatar from "@/assets/furina-avatar.webp";
|
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
||||||
import { api } from "@/api";
|
|
||||||
import { ThreeDots } from "react-loader-spinner";
|
|
||||||
import Markdown from "react-markdown";
|
|
||||||
|
|
||||||
const ChatWindow = () => {
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const moveWindowStateRef = useRef({
|
|
||||||
isMoving: false,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
startX: 0,
|
|
||||||
startY: 0,
|
|
||||||
});
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const [isOpen, setOpen] = useState(false);
|
|
||||||
|
|
||||||
const { data: messages } = useQuery({
|
|
||||||
queryKey: ["chats"],
|
|
||||||
queryFn: () => api("/chats"),
|
|
||||||
});
|
|
||||||
|
|
||||||
const sendMessage = useMutation({
|
|
||||||
mutationFn: async (message: string) => {
|
|
||||||
return api("/chats", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({ message }),
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onMutate: async (data) => {
|
|
||||||
await queryClient.cancelQueries({ queryKey: ["chats"] });
|
|
||||||
const prevData = queryClient.getQueryData(["chats"]);
|
|
||||||
|
|
||||||
// optimistic update
|
|
||||||
queryClient.setQueryData(["chats"], (prev: any) => [
|
|
||||||
{ id: "-1", role: "user", content: data },
|
|
||||||
...prev,
|
|
||||||
]);
|
|
||||||
return { prevData };
|
|
||||||
},
|
|
||||||
onError: (_err, _data, ctx) => {
|
|
||||||
queryClient.setQueryData(["chats"], ctx?.prevData);
|
|
||||||
},
|
|
||||||
onSettled: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["chats"] });
|
|
||||||
const msgEl = document.querySelector('[name="message"]') as
|
|
||||||
| HTMLInputElement
|
|
||||||
| undefined;
|
|
||||||
setTimeout(() => msgEl?.focus(), 100);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const onMouseDown = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
|
||||||
if (isMobile) {
|
|
||||||
if (isOpen) {
|
|
||||||
setOpen(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moveWindowStateRef.current.isMoving) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
moveWindowStateRef.current.isMoving = true;
|
|
||||||
moveWindowStateRef.current.startX =
|
|
||||||
e.clientX - moveWindowStateRef.current.x;
|
|
||||||
moveWindowStateRef.current.startY =
|
|
||||||
e.clientY - moveWindowStateRef.current.y;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
|
||||||
const container = containerRef.current;
|
|
||||||
if (isMobile || !moveWindowStateRef.current.isMoving || !container) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const x = e.clientX - moveWindowStateRef.current.startX;
|
|
||||||
const y = e.clientY - moveWindowStateRef.current.startY;
|
|
||||||
moveWindowStateRef.current.x = x;
|
|
||||||
moveWindowStateRef.current.y = y;
|
|
||||||
container.style.transform = `translate(${x}px, ${y}px)`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMouseUp = () => {
|
|
||||||
moveWindowStateRef.current.isMoving = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (sendMessage.isPending) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const form = e.target as HTMLFormElement;
|
|
||||||
const data = new FormData(form);
|
|
||||||
|
|
||||||
const message = data.get("message") as string;
|
|
||||||
if (!message?.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessage.mutate(message, {
|
|
||||||
onSuccess: () => form.reset(),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
|
||||||
document.addEventListener("mouseup", onMouseUp);
|
|
||||||
document.addEventListener("pointermove", onMouseMove);
|
|
||||||
document.addEventListener("pointerup", onMouseUp);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
|
||||||
document.removeEventListener("pointermove", onMouseMove);
|
|
||||||
document.removeEventListener("pointerup", onMouseUp);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className={cn(
|
|
||||||
"flex md:hidden absolute bottom-4 right-4 h-14 px-4 rounded-xl gap-x-2 bg-white text-slate-600 shadow-lg active:opacity-80 flex-row items-center justify-center",
|
|
||||||
isOpen && "hidden"
|
|
||||||
)}
|
|
||||||
onClick={() => setOpen(!isOpen)}
|
|
||||||
>
|
|
||||||
<span>Chat</span>
|
|
||||||
<MessageSquareQuote />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div
|
|
||||||
ref={containerRef}
|
|
||||||
className={cn(
|
|
||||||
"bg-white/20 border border-white/20 shadow-lg rounded-lg backdrop-blur-md absolute bottom-[10px] sm:bottom-1/4 left-[10px] sm:left-[10%] w-[calc(100%-20px)] sm:max-w-[320px] h-[80vh] sm:h-[300px] lg:max-w-[400px] lg:h-[350px] flex flex-col items-stretch overflow-hidden transition-all ease-linear sm:traansition-none translate-y-[110%] sm:translate-y-0 opacity-100",
|
|
||||||
messages == null && "-translate-x-8 opacity-0",
|
|
||||||
isOpen && "translate-y-0"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="flex flex-row items-center gap-2 px-3 h-8 cursor-move"
|
|
||||||
onMouseDown={onMouseDown}
|
|
||||||
onPointerDown={onMouseDown}
|
|
||||||
>
|
|
||||||
<div className="size-3 rounded-full bg-red-500" />
|
|
||||||
<div className="size-3 rounded-full bg-yellow-500" />
|
|
||||||
<div className="size-3 rounded-full bg-green-500" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto flex flex-col-reverse gap-y-2 p-2">
|
|
||||||
{sendMessage.isPending && (
|
|
||||||
<Message
|
|
||||||
name="Furina"
|
|
||||||
role="model"
|
|
||||||
children={
|
|
||||||
<ThreeDots
|
|
||||||
visible={true}
|
|
||||||
height="16"
|
|
||||||
width="32"
|
|
||||||
color="#5381c7"
|
|
||||||
ariaLabel="writing.."
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{sendMessage.isError && (
|
|
||||||
<p className="text-xs text-center self-center text-black my-4 bg-white/10 backdrop-blur-md px-2 py-1 rounded-lg">
|
|
||||||
{getSendChatErrorMessage(sendMessage.error)}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{messages?.map((msg: any) => {
|
|
||||||
return (
|
|
||||||
<Message
|
|
||||||
key={msg.id}
|
|
||||||
isMe={msg.role === "user"}
|
|
||||||
name={msg.role === "user" ? "Me" : "Furina"}
|
|
||||||
role={msg.role}
|
|
||||||
children={<Markdown>{msg.content}</Markdown>}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form onSubmit={onSubmit}>
|
|
||||||
<div className="p-2 flex flex-row items-center pt-1">
|
|
||||||
<input
|
|
||||||
name="message"
|
|
||||||
className="w-full border-none rounded-full text-sm px-3 h-8 focus:outline-none"
|
|
||||||
placeholder="Write Message..."
|
|
||||||
required
|
|
||||||
disabled={sendMessage.isPending}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
className="text-white size-8 shrink-0 hover:bg-white/40 rounded-full flex items-center justify-center -mr-1"
|
|
||||||
disabled={sendMessage.isPending}
|
|
||||||
>
|
|
||||||
<SendIcon size={18} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type MessageProps = {
|
|
||||||
isMe?: boolean;
|
|
||||||
role?: string;
|
|
||||||
name: string;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Message = ({ isMe, role, name, children }: MessageProps) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex items-start gap-2 w-full max-w-[90%]",
|
|
||||||
isMe && "justify-end self-end pr-1"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{role === "model" && (
|
|
||||||
<div className="size-8 rounded-full bg-white shrink-0 overflow-hidden">
|
|
||||||
<img src={furinaAvatar} className="w-full h-full object-cover" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={cn("flex flex-col", isMe && "items-end")}>
|
|
||||||
<p className="font-medium -mt-1 text-sm text-white">{name}</p>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"bg-white/40 backdrop-blur-md text-slate-900 rounded-xl px-2 py-1 mt-0.5 text-sm",
|
|
||||||
isMe ? "bg-white/80 rounded-tr-none" : "rounded-tl-none"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSendChatErrorMessage = (error: Error) => {
|
|
||||||
if (error?.message?.includes("FinishReasonSafety")) {
|
|
||||||
return "Your message probably detected with blocked words, please try again.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "An error occured. Please try again.";
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChatWindow;
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { ComponentProps } from "react";
|
||||||
|
|
||||||
|
import headerImg from "@/assets/images/header-img.gif";
|
||||||
|
import titleImg from "@/assets/images/title-img.svg";
|
||||||
|
import ahogeImg from "@/assets/images/furina-ahoge.svg";
|
||||||
|
import { Link, useLocation } from "react-router-dom";
|
||||||
|
import { cn } from "@/utility/utils";
|
||||||
|
|
||||||
|
const AppBar = () => {
|
||||||
|
return (
|
||||||
|
<header className="w-full bg-[#111a21] shadow">
|
||||||
|
<div className="mx-auto max-w-5xl md:px-4 pt-6 md:py-4 flex flex-col md:flex-row items-center gap-4">
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="flex flex-col md:flex-row items-center gap-2 md:gap-4"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={headerImg}
|
||||||
|
alt="Furina gif by u/Quit-Creative"
|
||||||
|
className="h-12 md:h-16"
|
||||||
|
/>
|
||||||
|
<img src={titleImg} alt="Furina" className="h-6 md:h-10" />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Navbar>
|
||||||
|
<NavbarItem path="/" title="Home" />
|
||||||
|
<NavbarItem path="/pat-pat" title="Pat Furina" />
|
||||||
|
<NavbarItem path="/treasures" title="Treasures‧₊˚" />
|
||||||
|
<NavbarItem path="/toodle" title="Toodle-oo~" />
|
||||||
|
</Navbar>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Navbar = ({ children }: ComponentProps<"div">) => {
|
||||||
|
return (
|
||||||
|
<nav className="md:flex-1 self-stretch md:self-center flex items-center px-2 md:px-0 md:justify-end md:gap-5 overflow-x-auto md:overflow-x-hidden">
|
||||||
|
{children}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type NavbarItemProps = {
|
||||||
|
path: string;
|
||||||
|
title: string;
|
||||||
|
isExact?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const isActive = isExact ? pathname === path : pathname.startsWith(path);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={path}
|
||||||
|
className="group flex flex-shrink-0 items-center px-2 md:py-4 first:ml-auto last:mr-auto md:first:ml-0 md:last:mr-0"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={ahogeImg}
|
||||||
|
alt="ahoge"
|
||||||
|
className={cn(
|
||||||
|
"h-4 md:h-6 group-hover:-scale-x-100 transition-transform",
|
||||||
|
isActive ? "-scale-x-100" : ""
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p
|
||||||
|
className={cn(
|
||||||
|
"text-white ml-2 md:ml-4 py-2 md:py-0 border-b-2 md:border-dotted group-hover:border-primary-500 border-transparent transition-all",
|
||||||
|
isActive ? "border-primary-500 md:border-white" : ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AppBar;
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import Helmet from "react-helmet";
|
||||||
|
|
||||||
|
type PageMetadataProps = {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
keywords?: string;
|
||||||
|
allowIndex?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PageMetadata = (props: PageMetadataProps) => {
|
||||||
|
return (
|
||||||
|
<Helmet>
|
||||||
|
<title>
|
||||||
|
{props.title
|
||||||
|
? [props.title, "Furina.id"].join(" - ")
|
||||||
|
: "Welcome to Furina.id"}
|
||||||
|
</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content={props.description || "Welcome to Furina.id"}
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content={[
|
||||||
|
props.keywords,
|
||||||
|
"furina.id, furina build, furina gameplay, furina guide, furina genshin",
|
||||||
|
]
|
||||||
|
.filter((i) => !!i)
|
||||||
|
.join(", ")}
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="robots"
|
||||||
|
content={
|
||||||
|
props.allowIndex !== false ? "index, follow" : "noindex, nofollow"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Helmet>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageMetadata;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { ComponentProps, Suspense } from "react";
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import AppBar from "../containers/AppBar";
|
||||||
|
import LoadingPage from "@/pages/misc/loading-page";
|
||||||
|
|
||||||
|
const MainLayout = ({ children }: ComponentProps<"div">) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<AppBar />
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{children ? (
|
||||||
|
children
|
||||||
|
) : (
|
||||||
|
<Suspense fallback={<LoadingPage />}>
|
||||||
|
<Outlet />
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<footer className="bg-primary-700 shadow relative z-10 px-4 py-8 text-center">
|
||||||
|
<p className="text-sm text-white">
|
||||||
|
Artworks displayed belong to respective owners.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MainLayout;
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { useIsMobile } from "@/hooks/useScreen";
|
|
||||||
import { ComponentPropsWithoutRef, useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
const ParallaxView = ({
|
|
||||||
depth,
|
|
||||||
style,
|
|
||||||
...props
|
|
||||||
}: ComponentPropsWithoutRef<"div"> & { depth: number }) => {
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const el = ref.current;
|
|
||||||
if (!el || isMobile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
|
||||||
const x = e.clientX * -depth;
|
|
||||||
const y = e.clientY * -depth;
|
|
||||||
el.style.transform = `${
|
|
||||||
style?.transform || ""
|
|
||||||
} translateX(${x}px) translateY(${y}px)`;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
|
||||||
};
|
|
||||||
}, [isMobile]);
|
|
||||||
|
|
||||||
return <div {...props} style={style} ref={ref} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ParallaxView;
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
|
import { cn } from "@/utility/utils";
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
"inline-flex items-center rounded border border-primary-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-primary-950 focus:ring-offset-2 dark:border-primary-800 dark:focus:ring-primary-300",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"border-transparent bg-primary-600 text-primary-50 hover:bg-primary-600/80 dark:bg-primary-50 dark:text-primary-900 dark:hover:bg-primary-50/80",
|
||||||
|
secondary:
|
||||||
|
"border-transparent bg-primary-100 text-primary-900 hover:bg-primary-100/80 dark:bg-primary-800 dark:text-primary-50 dark:hover:bg-primary-800/80",
|
||||||
|
destructive:
|
||||||
|
"border-transparent bg-red-500 text-primary-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-primary-50 dark:hover:bg-red-900/80",
|
||||||
|
outline: "text-primary-950 dark:text-primary-50",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface BadgeProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Badge;
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
|
import { cn } from "@/utility/utils";
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-primary-950 dark:focus-visible:ring-primary-300",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
solid:
|
||||||
|
"bg-primary-500 text-primary-50 hover:bg-primary-500/90 dark:bg-primary-50 dark:text-primary-900 dark:hover:bg-primary-50/90",
|
||||||
|
destructive:
|
||||||
|
"bg-red-500 text-primary-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-primary-50 dark:hover:bg-red-900/90",
|
||||||
|
outline:
|
||||||
|
"border border-primary-200 bg-white hover:bg-primary-100 hover:text-primary-900 dark:border-primary-800 dark:bg-primary-950 dark:hover:bg-primary-800 dark:hover:text-primary-50",
|
||||||
|
secondary:
|
||||||
|
"bg-primary-100 text-primary-900 hover:bg-primary-100/80 dark:bg-primary-800 dark:text-primary-50 dark:hover:bg-primary-800/80",
|
||||||
|
ghost:
|
||||||
|
"hover:bg-primary-100 hover:text-primary-900 dark:hover:bg-primary-800 dark:hover:text-primary-50",
|
||||||
|
link: "text-primary-900 underline-offset-4 hover:underline dark:text-primary-50",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-10 px-4 py-2",
|
||||||
|
sm: "h-9 rounded-md px-3",
|
||||||
|
lg: "h-11 rounded-md px-8",
|
||||||
|
icon: "h-10 w-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "outline",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Button.displayName = "Button";
|
||||||
|
|
||||||
|
export default Button;
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
|
import { cn } from "@/utility/utils";
|
||||||
|
|
||||||
|
const Dialog = DialogPrimitive.Root;
|
||||||
|
|
||||||
|
const DialogTrigger = DialogPrimitive.Trigger;
|
||||||
|
|
||||||
|
const DialogPortal = DialogPrimitive.Portal;
|
||||||
|
|
||||||
|
const DialogClose = DialogPrimitive.Close;
|
||||||
|
|
||||||
|
const DialogOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/20 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||||
|
|
||||||
|
const DialogContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<DialogPortal>
|
||||||
|
<DialogOverlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 data-[state=open]:text-slate-500 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800 dark:data-[state=open]:text-slate-400">
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</DialogPortal>
|
||||||
|
));
|
||||||
|
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
const DialogHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
DialogHeader.displayName = "DialogHeader";
|
||||||
|
|
||||||
|
const DialogFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
DialogFooter.displayName = "DialogFooter";
|
||||||
|
|
||||||
|
const DialogTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-lg font-semibold leading-none tracking-tight",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
||||||
|
|
||||||
|
const DialogDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Dialog,
|
||||||
|
DialogPortal,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogClose,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogFooter,
|
||||||
|
DialogTitle,
|
||||||
|
DialogDescription,
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { cn } from "@/utility/utils";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
|
type Props = React.ComponentProps<"img"> & {
|
||||||
|
lazySrc: string;
|
||||||
|
containerClassName?: string;
|
||||||
|
placeholderClassName?: string;
|
||||||
|
placeholder?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LazyImage = ({
|
||||||
|
lazySrc,
|
||||||
|
src,
|
||||||
|
containerClassName,
|
||||||
|
placeholderClassName,
|
||||||
|
className,
|
||||||
|
placeholder,
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
|
const [isLoaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn("relative", containerClassName)}>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"absolute inset-0 bg-no-repeat bg-cover blur-md z-0",
|
||||||
|
// isLoaded ? "brightness-75" : "",
|
||||||
|
placeholderClassName
|
||||||
|
)}
|
||||||
|
style={{ backgroundImage: `url('${lazySrc}')` }}
|
||||||
|
></div>
|
||||||
|
|
||||||
|
{!isLoaded && placeholder ? placeholder : null}
|
||||||
|
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
loading="lazy"
|
||||||
|
onLoad={() => setTimeout(() => setLoaded(true), 200)}
|
||||||
|
className={cn(
|
||||||
|
"transition-all duration-500 relative z-[1]",
|
||||||
|
isLoaded ? "opacity-100" : "opacity-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LazyImage;
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
DialogTitle,
|
||||||
|
} from "./Dialog";
|
||||||
|
|
||||||
|
export type ModalProps = {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
isOpen?: boolean;
|
||||||
|
onOpenChange?: (open: boolean) => void;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
size?: "sm" | "md" | "lg" | "xl";
|
||||||
|
};
|
||||||
|
|
||||||
|
const Modal = ({
|
||||||
|
children,
|
||||||
|
isOpen,
|
||||||
|
onOpenChange,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
size = "md",
|
||||||
|
}: ModalProps) => {
|
||||||
|
return (
|
||||||
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<DialogPortal>
|
||||||
|
<DialogOverlay />
|
||||||
|
|
||||||
|
<DialogContent
|
||||||
|
className={
|
||||||
|
{ sm: "max-w-md", md: "max-w-lg", lg: "max-w-xl", xl: "max-w-2xl" }[
|
||||||
|
size
|
||||||
|
]
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{title ? (
|
||||||
|
<DialogTitle className="text-xl">{title}</DialogTitle>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{description ? (
|
||||||
|
<DialogDescription>{description}</DialogDescription>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{children}
|
||||||
|
|
||||||
|
<DialogClose />
|
||||||
|
</DialogContent>
|
||||||
|
</DialogPortal>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
|
import { cn } from "@/utility/utils";
|
||||||
|
|
||||||
|
const SheetRoot = SheetPrimitive.Root;
|
||||||
|
|
||||||
|
const SheetPortal = SheetPrimitive.Portal;
|
||||||
|
|
||||||
|
const SheetOverlay = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Overlay
|
||||||
|
className={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/20 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||||
|
|
||||||
|
const sheetVariants = cva(
|
||||||
|
"fixed z-50 gap-4 bg-white shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
side: {
|
||||||
|
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||||
|
bottom:
|
||||||
|
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||||
|
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||||
|
right:
|
||||||
|
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
side: "right",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
interface SheetContentProps
|
||||||
|
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||||
|
VariantProps<typeof sheetVariants> {}
|
||||||
|
|
||||||
|
const SheetContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||||
|
SheetContentProps
|
||||||
|
>(({ side = "right", className, children, ...props }, ref) => (
|
||||||
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
|
<SheetPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(sheetVariants({ side }), className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800">
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</SheetPrimitive.Close>
|
||||||
|
</SheetPrimitive.Content>
|
||||||
|
</SheetPortal>
|
||||||
|
));
|
||||||
|
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
const SheetHeader = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col p-4 space-y-2 text-center sm:text-left",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
SheetHeader.displayName = "SheetHeader";
|
||||||
|
|
||||||
|
const SheetFooter = ({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
SheetFooter.displayName = "SheetFooter";
|
||||||
|
|
||||||
|
const SheetTitle = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Title
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-lg font-semibold text-slate-950 dark:text-slate-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||||
|
|
||||||
|
const SheetDescription = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SheetPrimitive.Description
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||||
|
|
||||||
|
type SheetProps = React.ComponentProps<typeof SheetRoot> & {
|
||||||
|
isOpen?: boolean;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
position?: SheetContentProps["side"];
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Sheet = ({
|
||||||
|
isOpen,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
children,
|
||||||
|
position,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: SheetProps) => {
|
||||||
|
return (
|
||||||
|
<SheetRoot open={isOpen} {...props}>
|
||||||
|
<SheetContent
|
||||||
|
side={position}
|
||||||
|
className={cn("flex flex-col gap-0 rounded-t-2xl", className)}
|
||||||
|
>
|
||||||
|
<SheetHeader>
|
||||||
|
{title ? <SheetTitle>{title}</SheetTitle> : null}
|
||||||
|
{description ? (
|
||||||
|
<SheetDescription>{description}</SheetDescription>
|
||||||
|
) : null}
|
||||||
|
</SheetHeader>
|
||||||
|
<div className="flex-1 overflow-y-auto">{children}</div>
|
||||||
|
</SheetContent>
|
||||||
|
</SheetRoot>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Sheet;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { cn } from "@/utility/utils"
|
||||||
|
|
||||||
|
function Skeleton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("animate-pulse rounded-md bg-slate-100 dark:bg-slate-800", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Skeleton }
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
@apply mx-auto max-w-5xl px-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
@apply text-blue-600 font-medium hover:text-blue-500 hover:underline;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const useModal = <T = unknown>() => {
|
||||||
|
const [isOpen, setOpen] = useState(false);
|
||||||
|
const [data, setData] = useState<T | undefined | null>(null);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isOpen,
|
||||||
|
data,
|
||||||
|
onOpen(_data?: T | null) {
|
||||||
|
setOpen(true);
|
||||||
|
setData(_data);
|
||||||
|
},
|
||||||
|
onClose() {
|
||||||
|
setOpen(false);
|
||||||
|
},
|
||||||
|
onOpenChange: setOpen,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useModal;
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export const useScreen = () => {
|
|
||||||
const [width, setWidth] = useState(window.innerWidth);
|
|
||||||
const [height, setHeight] = useState(window.innerHeight);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleResize = () => {
|
|
||||||
setWidth(window.innerWidth);
|
|
||||||
setHeight(window.innerHeight);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("resize", handleResize);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("resize", handleResize);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { width, height };
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useIsMobile = () => useScreen().width < 640
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap');
|
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Chrome, Edge and Safari */
|
|
||||||
*::-webkit-scrollbar {
|
|
||||||
height: 8px;
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-track:hover {
|
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-track:active {
|
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 5px;
|
|
||||||
background-color: #353771;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: #464994;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb:active {
|
|
||||||
background-color: #464994;
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
import { StrictMode } from "react";
|
import React from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import App from "./app.tsx";
|
import Loader from "./Loader";
|
||||||
import Providers from "./providers.tsx";
|
import "./global.css";
|
||||||
import "./main.css";
|
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<React.StrictMode>
|
||||||
<Providers>
|
<Loader />
|
||||||
<App />
|
</React.StrictMode>
|
||||||
</Providers>
|
|
||||||
</StrictMode>
|
|
||||||
);
|
);
|
||||||
|
|||||||