Compare commits

...

16 Commits

Author SHA1 Message Date
26874f634d perf(font): 优化字体渲染并添加抗锯齿设置
- 在所有字体文件中添加 keep_rounding_remainders 属性,提高字体渲染质量
- 更新项目设置,启用抗锯齿功能
- 调整场景文件中的资源引用,添加 UID 信息
2025-05-27 09:20:14 +08:00
MakerYang
3441b34ef3 for 4.3 2024-08-20 09:24:17 +08:00
MakerYang
a902dd3de7 new 2024-08-06 18:30:21 +08:00
makeyangcom
f363be1fc8 Merge branch '3.0.0' 2024-04-06 15:11:55 +08:00
makeyangcom
03d44f4c48 3.0.0 2024-04-06 15:11:32 +08:00
makeyangcom
b543c0d8d8 Merge branch '3.0.0' 2024-03-16 16:01:13 +08:00
makeyangcom
1b1f3fac22 3.0.0 2024-03-16 16:00:57 +08:00
makeyangcom
865ea763c1 3.0.0 2024-03-16 12:34:38 +08:00
makeyangcom
a0076078c8 main 2024-03-16 11:52:44 +08:00
makeyangcom
cd7b5f7b07 Merge branch '3.0.0' 2024-03-14 19:01:51 +08:00
makeyangcom
00f3e439ff 3.0.0 2024-03-14 19:01:27 +08:00
makeyangcom
88e80ebab5 3.0.0 2024-03-14 19:00:27 +08:00
makeyangcom
764869a545 3.0.0 2024-03-14 18:49:59 +08:00
makeyangcom
7feb2d6454 3.0.0 2024-03-14 18:27:34 +08:00
makeyangcom
1ae4b6d08b 3.0.0 2024-03-14 10:48:48 +08:00
makeyangcom
c7f5991a44 3.0.0 2024-03-14 09:29:07 +08:00
1712 changed files with 438760 additions and 2347 deletions

21
.gitignore vendored
View File

@@ -3,14 +3,15 @@
.git
.vscode
.godot
*.cfg
*.tmp
node_modules
engine/release/*
!engine/release/.gitkeep
service/release/*
!service/release/.gitkeep
desktop/release/*
desktop/template/yarn.lock
desktop/template/yarn-error.log
desktop/template/node_modules
desktop/template/dist
game/release/*
!game/release/.gitkeep
!game/export_presets.cfg
!desktop/release/appicon.png
!desktop/release/.gitkeep
tools/export/*
!tools/export/export.exe
yarn.lock
yarn-error.log
*.tmp

View File

@@ -1,34 +0,0 @@
/**
******************************************************************************
* @file config.go
* @author MakerYang
******************************************************************************
*/
package Config
import (
"embed"
"encoding/json"
"golang.org/x/sync/errgroup"
)
var Get = &Data{}
type Data struct {
Info struct {
CompanyName string `json:"companyName"`
ProductName string `json:"productName"`
ProductVersion string `json:"productVersion"`
} `json:"info"`
Group errgroup.Group
}
func Init(version embed.FS) {
wails, _ := version.ReadFile("wails.json")
var wailsJson Data
json.Unmarshal(wails, &wailsJson)
Get.Info.ProductVersion = wailsJson.Info.ProductVersion
Get.Info.ProductName = wailsJson.Info.ProductName
Get.Info.CompanyName = wailsJson.Info.CompanyName
}

View File

@@ -1,62 +0,0 @@
/**
******************************************************************************
* @file framework.go
* @author MakerYang
******************************************************************************
*/
package Framework
import (
"cnc/framework/config"
"cnc/framework/windows/start"
"embed"
"fmt"
"github.com/gookit/color"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v2/pkg/options/linux"
"github.com/wailsapp/wails/v2/pkg/options/windows"
)
func Init(template embed.FS, version embed.FS) {
Config.Init(version)
start := StartWindows.Init()
err := wails.Run(&options.App{
Title: "",
Width: 1200,
Height: 768,
MinWidth: 1200,
MinHeight: 768,
AssetServer: &assetserver.Options{
Assets: template,
},
BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 1},
OnStartup: start.Startup,
OnShutdown: start.Shutdown,
Bind: []interface{}{
start,
},
WindowStartState: options.Normal,
Windows: &windows.Options{
WebviewDisableRendererCodeIntegrity: true,
DisableWindowIcon: true,
},
Linux: &linux.Options{
Icon: []byte(""),
WindowIsTranslucent: false,
WebviewGpuPolicy: linux.WebviewGpuPolicyNever,
},
Debug: options.Debug{
OpenInspectorOnStartup: false,
},
})
if err != nil {
fmt.Println("[desktop][framework]" + color.Gray.Text(err.Error()))
}
}

View File

@@ -1,74 +0,0 @@
/**
******************************************************************************
* @file index.go
* @author MakerYang
******************************************************************************
*/
package StartWindows
import (
"cnc/framework/config"
"context"
"os"
"runtime"
"strings"
)
type Api struct {
ctx context.Context
}
type ReturnResponse struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
}
func Init() *Api {
return &Api{}
}
func (start *Api) Startup(ctx context.Context) {
start.ctx = ctx
}
func (start *Api) Shutdown(ctx context.Context) {
}
func (start *Api) GetPlatform() string {
platform := ""
switch runtime.GOOS {
case "windows":
platform = "Windows"
case "darwin":
platform = "Darwin"
case "linux":
platform = "Linux"
content, err := os.ReadFile("/etc/os-release")
if err == nil {
lines := strings.Split(string(content), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "ID=") {
switch {
case strings.Contains(line, "ubuntu"):
platform = "Ubuntu"
case strings.Contains(line, "debian"):
platform = "Debian"
default:
platform = "Linux"
}
}
}
}
default:
platform = "-"
}
return platform
}
func (start *Api) GetVersion() []string {
return []string{Config.Get.Info.ProductName, Config.Get.Info.ProductVersion}
}

View File

@@ -1,43 +0,0 @@
module cnc
go 1.18
require (
github.com/gookit/color v1.5.2
github.com/wailsapp/wails/v2 v2.7.1
golang.org/x/sync v0.5.0
)
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.10.2 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
github.com/leaanthony/gosod v1.0.3 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/stretchr/testify v1.8.3 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.10 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
)
// replace github.com/wailsapp/wails/v2 v2.3.1 => C:\Users\admin\go\pkg\mod

View File

@@ -1,104 +0,0 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
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/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
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/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
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/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhyYyDV/w=
github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v2 v2.7.1 h1:HAzp2c5ODOzsLC6ZMDVtNOB72ozM7/SJecJPB2Ur+UU=
github.com/wailsapp/wails/v2 v2.7.1/go.mod h1:oIJVwwso5fdOgprBYWXBBqtx6PaSvxg8/KTQHNGkadc=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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.0-20210107192922-496545a6307b/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=

View File

@@ -1,26 +0,0 @@
/**
******************************************************************************
* @file main.go
* @author Makeryang
******************************************************************************
*/
package main
import (
"cnc/framework"
"embed"
"fmt"
"github.com/gookit/color"
)
//go:embed all:template/dist
var Template embed.FS
//go:embed all:wails.json
var VersionInfo embed.FS
func main() {
fmt.Println("[desktop][main]" + color.Gray.Text("starting..."))
Framework.Init(Template, VersionInfo)
}

175
desktop/package.json Normal file
View File

@@ -0,0 +1,175 @@
{
"name": "mir2tools",
"title": "Mir2Tools",
"version": "1.0.0",
"author": "MakerYang",
"description": "mir2.makeryang.com",
"license": "MIT",
"type": "module",
"main": "release/dist/main/index.cjs",
"scripts": {
"dev": "node tools/script/watch.mjs",
"rebuild": "electron-rebuild -f -w",
"build": "rimraf release && node --max-old-space-size=6096 tools/script/build.mjs && node --max-old-space-size=6096 tools/script/index.mjs",
"win64": "electron-builder --win --x64",
"win32": "electron-builder --win --ia32",
"mac": "electron-builder --mac --x64",
"linux": "electron-builder --linux --amd64"
},
"build": {
"productName": "Mir2Tools",
"appId": "com.makeryang.Mir2Tools",
"asar": true,
"buildDependenciesFromSource": true,
"directories": {
"output": "release/${version}"
},
"fileAssociations": [],
"publish": [
{
"provider": "generic",
"url": ""
}
],
"files": [
"release/dist"
],
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "tools/icons/macos.icns",
"artifactName": "${productName}_${version}.${ext}"
},
"win": {
"icon": "tools/icons/windows.ico",
"artifactName": "${productName}_${version}.${ext}",
"target": "nsis",
"requestedExecutionLevel": "highestAvailable"
},
"linux": {
"icon": "tools/icons/icon.png",
"target": [
"AppImage"
],
"artifactName": "${productName}_${version}.${ext}"
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"installerIcon": "tools/icons/windows.ico",
"uninstallerIcon": "tools/icons/windows.ico",
"shortcutName": "Mir2Tools"
}
},
"dependencies": {
"@fingerprintjs/fingerprintjs": "^4.3.0",
"@formkit/auto-animate": "^0.8.2",
"@internationalized/date": "^3.5.4",
"@radix-icons/vue": "^1.0.0",
"@stackblitz/sdk": "^1.10.0",
"@tanstack/vue-table": "^8.17.3",
"@unovis/ts": "^1.4.1",
"@unovis/vue": "^1.4.1",
"@vee-validate/zod": "^4.12.8",
"@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.38.2",
"@vue-flow/minimap": "^1.5.0",
"@vue-flow/node-toolbar": "^1.1.0",
"@vue/runtime-dom": "^3.4.27",
"@vueuse/core": "^10.9.0",
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"codesandbox": "^2.2.3",
"date-fns": "^3.6.0",
"electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.2.0",
"electron-localstorage": "^1.0.5",
"electron-store": "^8.2.0",
"electron-updater": "^5.3.0",
"embla-carousel": "^8.1.3",
"embla-carousel-autoplay": "^8.1.3",
"embla-carousel-vue": "^8.1.3",
"lucide-vue-next": "^0.400.0",
"magic-string": "^0.30.10",
"postcss": "^8.4.38",
"radix-vue": "^1.8.1",
"tailwindcss-animate": "^1.0.7",
"terser": "^5.31.1",
"uuid": "^10.0.0",
"v-calendar": "^3.1.2",
"vaul-vue": "^0.1.2",
"vee-validate": "4.12.6",
"vue": "^3.4.27",
"vue-i18n": "^9.13.1",
"vue-router": "^4.1.5",
"vue-sonner": "^1.1.2",
"vue-wrap-balancer": "^1.1.3",
"zod": "^3.23.8"
},
"devDependencies": {
"@babel/traverse": "^7.24.5",
"@iconify-json/gravity-ui": "^1.1.2",
"@iconify-json/lucide": "^1.1.187",
"@iconify-json/ph": "^1.1.13",
"@iconify-json/radix-icons": "^1.1.14",
"@iconify-json/ri": "^1.1.20",
"@iconify-json/simple-icons": "^1.1.102",
"@iconify-json/tabler": "^1.1.112",
"@iconify/vue": "^4.1.2",
"@oxc-parser/wasm": "^0.1.0",
"@shikijs/transformers": "^1.6.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.12.12",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/compiler-core": "^3.4.27",
"@vue/compiler-dom": "^3.4.27",
"@vue/tsconfig": "^0.5.1",
"autoprefixer": "^10.4.19",
"electron": "30.1.2",
"electron-builder": "24.13.3",
"electron-rebuild": "3.2.9",
"fast-glob": "^3.3.2",
"lodash-es": "^4.17.21",
"markdown-it": "^14.1.0",
"pathe": "^1.1.2",
"rimraf": "^5.0.7",
"sass": "^1.77.4",
"shiki": "^1.6.0",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.3",
"tsx": "^4.10.5",
"typescript": "^5.4.5",
"unplugin-icons": "^0.19.0",
"vite": "^5.3.1",
"vite-plugin-electron": "^0.11.1",
"vite-plugin-top-level-await": "^1.3.0",
"vite-plugin-wasm": "^3.2.2",
"vitepress": "^1.2.3",
"vue-component-meta": "^2.0.19",
"vue-tsc": "^2.0.19"
},
"env": {
"VITE_DEV_SERVER_HOST": "127.0.0.1",
"VITE_DEV_SERVER_PORT": 7676
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,87 @@
import defaultTheme from "tailwindcss/defaultTheme";
import tailwindcssAnimate from "tailwindcss-animate";
export default {
darkMode: "class",
content: ["./template/src/**/*.{vue,js,ts,jsx,tsx,md}"],
theme: {
container: {
center: true,
padding: "0rem"
},
extend: {
fontFamily: {
sans: ['var(--font-geist-sans)', ...defaultTheme.fontFamily.sans],
},
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
xl: 'calc(var(--radius) + 4px)',
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
boxShadow: {
switch: 'rgba(0, 0, 0, 0.3) 0px 0px 1px, rgba(0, 0, 0, 0.2) 0px 1px 2px',
},
keyframes: {
'accordion-down': {
from: { height: 0 },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: 0 },
},
'collapsible-down': {
from: { height: 0 },
to: { height: 'var(--radix-collapsible-content-height)' },
},
'collapsible-up': {
from: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 0 },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-in-out',
'accordion-up': 'accordion-up 0.2s ease-in-out',
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
'collapsible-up': 'collapsible-up 0.2s ease-in-out',
},
},
},
plugins: [tailwindcssAnimate]
}

View File

@@ -1,15 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Index</title>
<title></title>
</head>
<body>
<div id="app"></div>
<script src="./src/main.ts" type="module"></script>
<div id="app"></div>
<script src="./src/main.ts" type="module"></script>
</body>
</html>

View File

@@ -1,33 +0,0 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"rebuild": "node-gyp rebuild",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"engines": {
"node": ">=18"
},
"dependencies": {
"@element-plus/icons": "^0.0.11",
"@element-plus/icons-vue": "^2.0.9",
"axios": "^0.27.2",
"element-plus": "2.3.7",
"uuid": "^9.0.0",
"vue": "3.2.37",
"vue-router": "4.1.5"
},
"devDependencies": {
"@babel/types": "^7.18.10",
"@types/react": "^18.2.56",
"@types/node": "^18.11.18",
"@vitejs/plugin-vue": "^3.0.3",
"@vue/tsconfig": "0.1.3",
"typescript": "4.6.4",
"vite": "3.0.7",
"vue-tsc": "0.39.5"
}
}

View File

@@ -1 +0,0 @@
d24d6b406b5af24715d4d957ed80e892

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,36 +1,104 @@
<template>
<router-view ref="routerView" v-slot="{Component}">
<component :is="Component" :cnc="appData" />
<component :is="Component" :data="data" :base="base" />
</router-view>
<Toaster ref="toaster" />
</template>
<script lang="ts">
import {ref, defineComponent} from "vue";
import {ElLoading} from "element-plus";
export default defineComponent({
name: "App",
emits: [],
props: [],
components: {},
setup(props, context) {
<script setup lang="ts">
import {onBeforeMount, onMounted, onBeforeUnmount, onUnmounted, nextTick, ref} from "vue";
import {useRoute, useRouter, useI18n, useDark, useStore, Request, ThemeColors} from "./packages";
import {useForm, toTypedSchema, object, string} from "./packages";
import {Toaster, useToast} from "@/lib/toast";
const appData: any = ref({
loading: ElLoading.service({
lock: true,
background: "rgba(0, 0, 0, .5)",
customClass: "desktop",
}),
data: false
});
const $route = useRoute();
const $router = useRouter();
const $store = useStore();
const {toast} = useToast();
return {
props,
appData
const i18n = useI18n();
const base: any = (window as any).base;
const data: any = ref({
base: (window as any).base,
route: $route,
router: $router,
store: $store,
theme: {
colors: ThemeColors,
dark: useDark()
},
header: {
search: {
status: false
}
},
page: {
current: "",
login: {
type: "login",
code: {
timer: null,
count: 60,
loading: false
},
loading: false
}
},
service: {
loading: false,
account: {
token: ""
},
},
browser: {
language: (navigator as any).language,
toast: toast,
request: Request,
form: {
use: useForm,
schema: toTypedSchema,
object: object,
string: string
},
network: {
status: false
}
}
});
onBeforeMount(() => {});
onMounted(() => {
data.value.base.process = process;
console.log("Mir2Tools " + data.value.browser.language + " " + data.value.base.environment(process) + " " + data.value.base.config.version + " " + data.value.base.config.author);
console.log("Platform:" + data.value.base.platform + " Electron:" + data.value.base.process.versions.electron + " Chromium:" + data.value.base.process.versions.chrome + " NodeJS:" + data.value.base.process.versions.node);
console.log("Path:" + data.value.base.app_path(process) + " Data:" + data.value.base.app_data_path(process) + " Home:" + data.value.base.app_home_path(process) + " Temp:" + data.value.base.app_temp_path(process));
document.documentElement.style.setProperty("--radius", `${$store.radius.value}rem`);
document.documentElement.classList.add(`theme-${$store.theme.value}`);
nextTick(() => {
data.value.base.lang.t = i18n.t;
data.value.base.lang.locale = i18n.locale;
if(data.value.base.lang.locale === "null"){
if(data.value.browser.language === "zh-CN"){
data.value.base.lang.locale = "zh";
}else{
data.value.base.lang.locale = "en";
}
}
window.addEventListener("resize", function() {
data.value.base.ipc.send("message", {type: "header:right:button", data: "resize"});
});
});
});
onBeforeUnmount(() => {});
onUnmounted(() => {});
</script>
<style>
@import "./assets/css/base.scss";
@import url("./assets/css/shiki.css");
@import url("./assets/css/tailwind.css");
@import url("./assets/css/themes.css");
@import url("./assets/css/markdown.css");
</style>

View File

@@ -1,55 +0,0 @@
@charset "UTF-8";
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
-webkit-appearance: none;
-webkit-touch-callout: none;
outline: none;
user-select: none;
margin: 0;
padding: 0;
}
body {
font-family: Inter, sans-serif;
font-feature-settings: "tnum";
font-variant: tabular-nums;
-webkit-font-smoothing: antialiased;
font-size: 12px;
color: #666666;
}
ul li, ol li {
list-style: none;
}
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
::-webkit-scrollbar-thumb {
border-radius: 0;
box-shadow: inset 0 0 3px rgba(68, 68, 71, 1);
background: rgba(68, 68, 71, .5);
}
::-webkit-scrollbar-track{
box-shadow: none;
border-radius: 0;
background: rgba(68, 68, 71, 0);
}
*:focus {
outline: none !important;
}
.page-main{
width: 100%;
height: 100%;
position: fixed;
z-index: 1;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

View File

@@ -0,0 +1,11 @@
:root {
--vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");
--vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
--vp-code-bg: hsl(var(--muted));
--vp-c-divider: hsl(var(--muted));
--vp-code-block-color: #fff
}
.vp-doc {
}

View File

@@ -0,0 +1,52 @@
:root {
--shiki-foreground: #FFF8;
--shiki-color-background: #ffffff;
--shiki-token-constant: #ffffff;
--shiki-token-string: #ffffff88;
--shiki-token-comment: #ffffff88;
--shiki-token-keyword: #ffffff88;
--shiki-token-parameter: #AA0000;
--shiki-token-function: #ffffff;
--shiki-token-string-expression: #ebebeb;
--shiki-token-punctuation: #ffffff;
--shiki-token-link: #EE0000;
}
.shiki .highlighted-word {
border-radius: calc(var(--radius) - 2px);
border-color: rgba(63,63,70,.7);
background-color: rgba(63,63,70,.5);
padding: 0.25rem;
}
.alert-default{
background-color: #f4f4f5;
color: #909399;
}
.dark .alert-default{
background-color: #202121;
color: #909399;
}
.alert-warning{
background-color: #fdf6ec;
color: #e6a23c;
}
.dark .alert-warning{
background-color: #292218;
color: #e6a23c;
}
.alert-error{
background-color: #fef0f0;
color: #f56c6c;
}
.dark .alert-error{
background-color: #2b1d1d;
color: #f56c6c;
}
.drag{
-webkit-app-region: drag;
}
.no-drag{
-webkit-app-region: no-drag;
}

View File

@@ -0,0 +1,188 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--font-geist-sans: "geist-sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 72.22% 50.59%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 5% 64.9%;
--radius: 0.5rem;
--vis-primary-color: var(--primary);
--vis-secondary-color: 160 81% 40%;
--vis-text-color: var(--muted-foreground);
--vis-font-family: inherit !important;
--vis-area-stroke-width: 2px !important;
--vis-donut-central-label-text-color: hsl(var(--muted-foreground)) !important;
--vis-tooltip-background-color: none !important;
--vis-tooltip-border-color: none !important;
--vis-tooltip-text-color: none !important;
--vis-tooltip-shadow-color: none !important;
--vis-tooltip-backdrop-filter: none !important;
--vis-tooltip-padding: none !important;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
}
* {
@apply border-border;
}
html {
-webkit-text-size-adjust: 100%;
font-variation-settings: normal;
}
body {
@apply bg-background text-foreground min-h-screen antialiased font-sans;
/* font-feature-settings: "rlig" 1, "calt" 1; */
font-synthesis-weight: none;
text-rendering: optimizeLegibility;
}
/* Mobile tap highlight */
/* https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-tap-highlight-color */
html {
-webkit-tap-highlight-color: rgba(128, 128, 128, 0.5);
}
/* Font face Geist font */
@font-face {
font-family: "geist-sans";
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("../fonts/Geist/GeistVariableVF.woff2") format("woff2");
}
/* === Scrollbars === */
::-webkit-scrollbar {
@apply w-2;
@apply h-2;
}
::-webkit-scrollbar-track {
@apply !bg-muted;
}
::-webkit-scrollbar-thumb {
@apply rounded-sm !bg-muted-foreground/30;
}
/* Firefox */
/* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color#browser_compatibility */
html {
scrollbar-color: hsl(215.4 16.3% 46.9% / 0.3);
}
html.dark {
scrollbar-color: hsl(215.4 16.3% 56.9% / 0.3);
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.antialised {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
@layer utilities {
.step {
counter-increment: step;
}
.step:before {
@apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background;
@apply -ml-[50px] -mt-1;
content: counter(step);
}
}
@media (max-width: 640px) {
.container {
@apply px-4;
}
}
div[class^="language-"] {
@apply mb-4 mt-6 max-h-[650px] overflow-x-auto md:rounded-lg border !bg-secondary-foreground dark:!bg-secondary
}
pre {
@apply py-4;
}
pre code {
@apply relative font-mono text-sm ;
}
.line-numbers-wrapper, code {
--vp-code-line-height: 1.7;
}
.line-numbers-wrapper {
@apply font-mono;
}
pre code .line {
@apply px-4 min-h-4 !py-0.5 w-full inline-block leading-[--vp-code-line-height];
}
.line-number {
@apply !text-[.75rem] !inline-block text-muted-foreground leading-[--vp-code-line-height];
}
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}

View File

@@ -0,0 +1,793 @@
html {
color-scheme: light;
--el-empty-fill-color-1: hsl(var(--muted));
--el-empty-fill-color-2: hsl(var(--muted));
--el-empty-fill-color-3: hsl(var(--muted));
--el-empty-fill-color-4: hsl(var(--muted));
--el-empty-fill-color-5: hsl(var(--muted));
--el-empty-fill-color-6: hsl(var(--muted));
--el-empty-fill-color-7: hsl(var(--muted));
--el-empty-fill-color-8: hsl(var(--muted));
--el-empty-fill-color-9: hsl(var(--muted));
}
html.dark {
color-scheme: dark;
--el-empty-fill-color-1: hsl(var(--muted));
--el-empty-fill-color-2: hsl(var(--muted));
--el-empty-fill-color-3: hsl(var(--muted));
--el-empty-fill-color-4: hsl(var(--muted));
--el-empty-fill-color-5: hsl(var(--muted));
--el-empty-fill-color-6: hsl(var(--muted));
--el-empty-fill-color-7: hsl(var(--muted));
--el-empty-fill-color-8: hsl(var(--muted));
--el-empty-fill-color-9: hsl(var(--muted));
}
.theme-zinc {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
}
.theme-zinc.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--ring: 240 4.9% 83.9%;
}
.theme-slate {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.theme-slate.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--ring: 212.7 26.8% 83.9;
}
.theme-stone {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--primary: 24 9.8% 10%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 20 14.3% 4.1%;
--radius: 0.95rem;
}
.theme-stone.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--primary: 60 9.1% 97.8%;
--primary-foreground: 24 9.8% 10%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 24 5.7% 82.9%;
}
.theme-gray {
--background: 0 0% 100%;
--foreground: 224 71.4% 4.1%;
--muted: 220 14.3% 95.9%;
--muted-foreground: 220 8.9% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 224 71.4% 4.1%;
--card: 0 0% 100%;
--card-foreground: 224 71.4% 4.1%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--primary: 220.9 39.3% 11%;
--primary-foreground: 210 20% 98%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
--accent: 220 14.3% 95.9%;
--accent-foreground: 220.9 39.3% 11%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 20% 98%;
--ring: 224 71.4% 4.1%;
--radius: 0.35rem;
}
.theme-gray.dark {
--background: 224 71.4% 4.1%;
--foreground: 210 20% 98%;
--muted: 215 27.9% 16.9%;
--muted-foreground: 217.9 10.6% 64.9%;
--popover: 224 71.4% 4.1%;
--popover-foreground: 210 20% 98%;
--card: 224 71.4% 4.1%;
--card-foreground: 210 20% 98%;
--border: 215 27.9% 16.9%;
--input: 215 27.9% 16.9%;
--primary: 210 20% 98%;
--primary-foreground: 220.9 39.3% 11%;
--secondary: 215 27.9% 16.9%;
--secondary-foreground: 210 20% 98%;
--accent: 215 27.9% 16.9%;
--accent-foreground: 210 20% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 20% 98%;
--ring: 216 12.2% 83.9%;
}
.theme-neutral {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 0 0% 3.9%;
--radius: ;
}
.theme-neutral.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--ring: 0 0% 83.1%;
}
.theme-red {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--primary: 0 72.2% 50.6%;
--primary-foreground: 0 85.7% 97.3%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 0 72.2% 50.6%;
--radius: 0.4rem;
}
.theme-red.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--primary: 0 72.2% 50.6%;
--primary-foreground: 0 85.7% 97.3%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--ring: 0 72.2% 50.6%;
}
.theme-rose {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--primary: 346.8 77.2% 49.8%;
--primary-foreground: 355.7 100% 97.3%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 346.8 77.2% 49.8%;
--radius: 0.5rem;
}
.theme-rose.dark {
--background: 20 14.3% 4.1%;
--foreground: 0 0% 95%;
--muted: 0 0% 15%;
--muted-foreground: 240 5% 64.9%;
--popover: 0 0% 9%;
--popover-foreground: 0 0% 95%;
--card: 24 9.8% 10%;
--card-foreground: 0 0% 95%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--primary: 346.8 77.2% 49.8%;
--primary-foreground: 355.7 100% 97.3%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--ring: 346.8 77.2% 49.8%;
}
.theme-orange {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--primary: 24.6 95% 53.1%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 24.6 95% 53.1%;
--radius: 0.95rem;
}
.theme-orange.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--primary: 20.5 90.2% 48.2%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 20.5 90.2% 48.2%;
}
.theme-green {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--primary: 142.1 76.2% 36.3%;
--primary-foreground: 355.7 100% 97.3%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 142.1 76.2% 36.3%;
--radius: ;
}
.theme-green.dark {
--background: 20 14.3% 4.1%;
--foreground: 0 0% 95%;
--muted: 0 0% 15%;
--muted-foreground: 240 5% 64.9%;
--popover: 0 0% 9%;
--popover-foreground: 0 0% 95%;
--card: 24 9.8% 10%;
--card-foreground: 0 0% 95%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--primary: 142.1 70.6% 45.3%;
--primary-foreground: 144.9 80.4% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 85.7% 97.3%;
--ring: 142.4 71.8% 29.2%;
}
.theme-blue {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--ring: 221.2 83.2% 53.3%;
--radius: ;
}
.theme-blue.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--ring: 224.3 76.3% 48%;
}
.theme-yellow {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--primary: 47.9 95.8% 53.1%;
--primary-foreground: 26 83.3% 14.1%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 20 14.3% 4.1%;
--radius: 0.95rem;
}
.theme-yellow.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--primary: 47.9 95.8% 53.1%;
--primary-foreground: 26 83.3% 14.1%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 35.5 91.7% 32.9%;
}
.theme-violet {
--background: 0 0% 100%;
--foreground: 224 71.4% 4.1%;
--muted: 220 14.3% 95.9%;
--muted-foreground: 220 8.9% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 224 71.4% 4.1%;
--card: 0 0% 100%;
--card-foreground: 224 71.4% 4.1%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--primary: 262.1 83.3% 57.8%;
--primary-foreground: 210 20% 98%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
--accent: 220 14.3% 95.9%;
--accent-foreground: 220.9 39.3% 11%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 20% 98%;
--ring: 262.1 83.3% 57.8%;
--radius: ;
}
.theme-violet.dark {
--background: 224 71.4% 4.1%;
--foreground: 210 20% 98%;
--muted: 215 27.9% 16.9%;
--muted-foreground: 217.9 10.6% 64.9%;
--popover: 224 71.4% 4.1%;
--popover-foreground: 210 20% 98%;
--card: 224 71.4% 4.1%;
--card-foreground: 210 20% 98%;
--border: 215 27.9% 16.9%;
--input: 215 27.9% 16.9%;
--primary: 263.4 70% 50.4%;
--primary-foreground: 210 20% 98%;
--secondary: 215 27.9% 16.9%;
--secondary-foreground: 210 20% 98%;
--accent: 215 27.9% 16.9%;
--accent-foreground: 210 20% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 20% 98%;
--ring: 263.4 70% 50.4%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,42 +1,12 @@
import {createApp} from "vue";
import App from "./app.vue";
import {router} from "./router";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import * as ElIcons from "@element-plus/icons-vue";
import {Router} from "./packages/router";
import {Language} from "./packages/language";
const app = createApp(App);
app.use(ElementPlus, {zIndex: 90000});
app.use(Router);
for (const [key, component] of Object.entries(ElIcons)) {
app.component(key, component);
}
app.use(router);
app.directive("resize", {
mounted(el, binding) {
let _this: any = this;
function debounce(fn: any, delay = 16) {
let time: any = null;
return function () {
if (time) {
clearTimeout(time);
}
const context = _this;
const args = arguments
time = setTimeout(function () {
fn.apply(context, args);
}, delay);
}
}
el._resizer = new window.ResizeObserver(debounce(binding.value, Number(binding.arg) || 16));
el._resizer.observe(el);
},
unmounted(el) {
el._resizer.disconnect();
}
});
app.use(Language);
app.mount("#app");

View File

@@ -1,6 +0,0 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function GetPlatform():Promise<string>;
export function GetVersion():Promise<Array<string>>;

View File

@@ -1,11 +0,0 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function GetPlatform() {
return window['go']['StartWindows']['Api']['GetPlatform']();
}
export function GetVersion() {
return window['go']['StartWindows']['Api']['GetVersion']();
}

View File

@@ -1,24 +0,0 @@
{
"name": "@wailsapp/runtime",
"version": "2.0.0",
"description": "Wails Javascript runtime library",
"main": "runtime.js",
"types": "runtime.d.ts",
"scripts": {
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}

View File

@@ -1,235 +0,0 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export interface Position {
x: number;
y: number;
}
export interface Size {
w: number;
h: number;
}
export interface Screen {
isCurrent: boolean;
isPrimary: boolean;
width : number
height : number
}
// Environment information such as platform, buildtype, ...
export interface EnvironmentInfo {
buildType: string;
platform: string;
arch: string;
}
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
// emits the given event. Optional data may be passed with the event.
// This will trigger any event listeners.
export function EventsEmit(eventName: string, ...data: any): void;
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
// sets up a listener for the given event name, but will only trigger a given number times.
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
// sets up a listener for the given event name, but will only trigger once.
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
// unregisters the listener for the given event name.
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
// unregisters all listeners.
export function EventsOffAll(): void;
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
// logs the given message as a raw message
export function LogPrint(message: string): void;
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
// logs the given message at the `trace` log level.
export function LogTrace(message: string): void;
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
// logs the given message at the `debug` log level.
export function LogDebug(message: string): void;
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
// logs the given message at the `error` log level.
export function LogError(message: string): void;
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
// logs the given message at the `fatal` log level.
// The application will quit after calling this method.
export function LogFatal(message: string): void;
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
// logs the given message at the `info` log level.
export function LogInfo(message: string): void;
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
// logs the given message at the `warning` log level.
export function LogWarning(message: string): void;
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
// Forces a reload by the main application as well as connected browsers.
export function WindowReload(): void;
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
// Reloads the application frontend.
export function WindowReloadApp(): void;
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
// Sets the window AlwaysOnTop or not on top.
export function WindowSetAlwaysOnTop(b: boolean): void;
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
// *Windows only*
// Sets window theme to system default (dark/light).
export function WindowSetSystemDefaultTheme(): void;
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
// *Windows only*
// Sets window to light theme.
export function WindowSetLightTheme(): void;
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
// *Windows only*
// Sets window to dark theme.
export function WindowSetDarkTheme(): void;
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
// Centers the window on the monitor the window is currently on.
export function WindowCenter(): void;
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
// Sets the text in the window title bar.
export function WindowSetTitle(title: string): void;
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
// Makes the window full screen.
export function WindowFullscreen(): void;
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
// Restores the previous window dimensions and position prior to full screen.
export function WindowUnfullscreen(): void;
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
export function WindowIsFullscreen(): Promise<boolean>;
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
// Sets the width and height of the window.
export function WindowSetSize(width: number, height: number): Promise<Size>;
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
// Gets the width and height of the window.
export function WindowGetSize(): Promise<Size>;
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMaxSize(width: number, height: number): void;
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMinSize(width: number, height: number): void;
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
// Sets the window position relative to the monitor the window is currently on.
export function WindowSetPosition(x: number, y: number): void;
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
// Gets the window position relative to the monitor the window is currently on.
export function WindowGetPosition(): Promise<Position>;
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
// Hides the window.
export function WindowHide(): void;
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
// Shows the window, if it is currently hidden.
export function WindowShow(): void;
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
// Maximises the window to fill the screen.
export function WindowMaximise(): void;
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
// Toggles between Maximised and UnMaximised.
export function WindowToggleMaximise(): void;
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
// Restores the window to the dimensions and position prior to maximising.
export function WindowUnmaximise(): void;
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
// Returns the state of the window, i.e. whether the window is maximised or not.
export function WindowIsMaximised(): Promise<boolean>;
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
// Minimises the window.
export function WindowMinimise(): void;
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
// Restores the window to the dimensions and position prior to minimising.
export function WindowUnminimise(): void;
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
// Returns the state of the window, i.e. whether the window is minimised or not.
export function WindowIsMinimised(): Promise<boolean>;
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
// Returns the state of the window, i.e. whether the window is normal or not.
export function WindowIsNormal(): Promise<boolean>;
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
export function ScreenGetAll(): Promise<Screen[]>;
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
// Opens the given URL in the system browser.
export function BrowserOpenURL(url: string): void;
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
// Returns information about the environment
export function Environment(): Promise<EnvironmentInfo>;
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
// Quits the application.
export function Quit(): void;
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
// Hides the application.
export function Hide(): void;
// [Show](https://wails.io/docs/reference/runtime/intro#show)
// Shows the application.
export function Show(): void;
// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
// Returns the current text stored on clipboard
export function ClipboardGetText(): Promise<string>;
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
// Sets a text on the clipboard
export function ClipboardSetText(text: string): Promise<boolean>;

View File

@@ -1,202 +0,0 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export function LogPrint(message) {
window.runtime.LogPrint(message);
}
export function LogTrace(message) {
window.runtime.LogTrace(message);
}
export function LogDebug(message) {
window.runtime.LogDebug(message);
}
export function LogInfo(message) {
window.runtime.LogInfo(message);
}
export function LogWarning(message) {
window.runtime.LogWarning(message);
}
export function LogError(message) {
window.runtime.LogError(message);
}
export function LogFatal(message) {
window.runtime.LogFatal(message);
}
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
}
export function EventsOn(eventName, callback) {
return EventsOnMultiple(eventName, callback, -1);
}
export function EventsOff(eventName, ...additionalEventNames) {
return window.runtime.EventsOff(eventName, ...additionalEventNames);
}
export function EventsOnce(eventName, callback) {
return EventsOnMultiple(eventName, callback, 1);
}
export function EventsEmit(eventName) {
let args = [eventName].slice.call(arguments);
return window.runtime.EventsEmit.apply(null, args);
}
export function WindowReload() {
window.runtime.WindowReload();
}
export function WindowReloadApp() {
window.runtime.WindowReloadApp();
}
export function WindowSetAlwaysOnTop(b) {
window.runtime.WindowSetAlwaysOnTop(b);
}
export function WindowSetSystemDefaultTheme() {
window.runtime.WindowSetSystemDefaultTheme();
}
export function WindowSetLightTheme() {
window.runtime.WindowSetLightTheme();
}
export function WindowSetDarkTheme() {
window.runtime.WindowSetDarkTheme();
}
export function WindowCenter() {
window.runtime.WindowCenter();
}
export function WindowSetTitle(title) {
window.runtime.WindowSetTitle(title);
}
export function WindowFullscreen() {
window.runtime.WindowFullscreen();
}
export function WindowUnfullscreen() {
window.runtime.WindowUnfullscreen();
}
export function WindowIsFullscreen() {
return window.runtime.WindowIsFullscreen();
}
export function WindowGetSize() {
return window.runtime.WindowGetSize();
}
export function WindowSetSize(width, height) {
window.runtime.WindowSetSize(width, height);
}
export function WindowSetMaxSize(width, height) {
window.runtime.WindowSetMaxSize(width, height);
}
export function WindowSetMinSize(width, height) {
window.runtime.WindowSetMinSize(width, height);
}
export function WindowSetPosition(x, y) {
window.runtime.WindowSetPosition(x, y);
}
export function WindowGetPosition() {
return window.runtime.WindowGetPosition();
}
export function WindowHide() {
window.runtime.WindowHide();
}
export function WindowShow() {
window.runtime.WindowShow();
}
export function WindowMaximise() {
window.runtime.WindowMaximise();
}
export function WindowToggleMaximise() {
window.runtime.WindowToggleMaximise();
}
export function WindowUnmaximise() {
window.runtime.WindowUnmaximise();
}
export function WindowIsMaximised() {
return window.runtime.WindowIsMaximised();
}
export function WindowMinimise() {
window.runtime.WindowMinimise();
}
export function WindowUnminimise() {
window.runtime.WindowUnminimise();
}
export function WindowSetBackgroundColour(R, G, B, A) {
window.runtime.WindowSetBackgroundColour(R, G, B, A);
}
export function ScreenGetAll() {
return window.runtime.ScreenGetAll();
}
export function WindowIsMinimised() {
return window.runtime.WindowIsMinimised();
}
export function WindowIsNormal() {
return window.runtime.WindowIsNormal();
}
export function BrowserOpenURL(url) {
window.runtime.BrowserOpenURL(url);
}
export function Environment() {
return window.runtime.Environment();
}
export function Quit() {
window.runtime.Quit();
}
export function Hide() {
window.runtime.Hide();
}
export function Show() {
window.runtime.Show();
}
export function ClipboardGetText() {
return window.runtime.ClipboardGetText();
}
export function ClipboardSetText(text) {
return window.runtime.ClipboardSetText(text);
}

View File

@@ -0,0 +1,3 @@
type Color = | 'zinc' | 'slate' | 'stone' | 'gray' | 'neutral' | 'red' | 'rose' | 'orange' | 'green' | 'blue' | 'yellow' | 'violet';
export const ThemeColors: Color[] = ["zinc", "rose", "blue", "green", "orange", "red", "slate", "stone", "gray", "neutral", "yellow", "violet"];

View File

@@ -0,0 +1,16 @@
import {shallowRef} from "vue";
import {Start, StartNode} from "./node/start";
export const Types = {
start: shallowRef(Start),
};
export const Nodes = [
StartNode,
];
export {VueFlow, Panel, useVueFlow} from "@vue-flow/core";
export {Controls, ControlButton} from "@vue-flow/controls";
export {NodeToolbar} from "@vue-flow/node-toolbar";
export {Background} from "@vue-flow/background";
export {MiniMap} from "@vue-flow/minimap";

View File

@@ -0,0 +1,10 @@
export { default as Start } from "./index.vue";
export const StartNode = {
id: "121212",
type: "start",
data: {
label: "开始对话",
},
position: {x: 250, y: 0}
};

View File

@@ -0,0 +1,60 @@
<template>
<div class="w-auto text-sm shadow-sm">
<NodeToolbar align="end" offset="5" :is-visible="props.data.toolbarVisible" :position="Position.Top">
<Button class="w-8 h-8 bg-background" variant="secondary" size="icon">
<DotsVerticalIcon class="w-3 h-3" />
</Button>
</NodeToolbar>
<ContextMenu>
<ContextMenuTrigger>
<div class="w-auto h-auto p-3 rounded-md space-y-3">
<div class="w-full flex space-x-2 items-center text-xs">
<div class="w-6 h-6">
<Button class="w-6 h-6 text-xs" size="icon">
<PlayIcon class="w-3 h-3" />
</Button>
</div>
<div class="w-[120px] line-clamp-1">{{props.data.label}}</div>
</div>
<div class="relative min-h-6 flex items-center">
<div class="w-full"></div>
<Handle id="a" type="target" :position="Position.Right"></Handle>
</div>
</div>
</ContextMenuTrigger>
<ContextMenuContent class="hidden"></ContextMenuContent>
</ContextMenu>
</div>
</template>
<script setup lang="ts">
import {onBeforeMount, onMounted, onBeforeUnmount, onUnmounted, nextTick} from "vue";
import {Handle, Position} from "@vue-flow/core";
import {NodeToolbar} from "@vue-flow/node-toolbar";
import {Button} from "@/lib/button";
import {DotsVerticalIcon, PlayIcon} from "@radix-icons/vue";
import {ContextMenu, ContextMenuContent, ContextMenuTrigger} from "@/lib/context-menu";
const props = defineProps({
id: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
}
});
onBeforeMount(() => {});
onMounted(() => {
nextTick(() => {
});
});
onBeforeUnmount(() => {});
onUnmounted(() => {});
</script>

View File

@@ -0,0 +1,9 @@
export {useRoute, useRouter} from "vue-router";
export {useI18n} from "vue-i18n";
export {useDark} from "@vueuse/core";
export {useStore} from "./store";
export {Request} from "./request";
export {useForm} from "vee-validate";
export {toTypedSchema} from "@vee-validate/zod";
export {object, string} from "zod";
export {ThemeColors} from "./config";

View File

@@ -0,0 +1,82 @@
export default {
header: {
search: {
tooltip: "Search"
},
tab: {
dashboard: "Dashboard",
account: "Account",
assets: "Assets",
operate: "Operate",
tools: "Tools"
},
theme: {
tooltip: "Theme Customizing",
title: "Customize",
describe: "Pick a style and color for your components",
colour: "Colour",
fillet: "Fillet",
style: "Style"
},
user: {
profile: "Your Profile",
member: "Member Management",
settings: "Settings",
upgrade: "Upgrade Subscribe",
logout: "Log out"
}
},
page: {
login: {
title: "Connect to Server",
describe: "Enter your server information below and connect",
email: "Email",
password: "Password",
forgot_password: "Forgot your password?",
button: "Sign in",
account: "Don't have an account?",
register: {
email: "Email",
code: {
title: "Captcha",
button: "Get Captcha",
loading: "Retry in {count} second"
},
password: "Password",
button: "Sign up",
account: "Already have an account?"
},
},
},
common: {
tooltips: "ToolTips",
nodata: "No relevant data",
abnormal: "Service request exception",
incomplete_information: "Incomplete information",
search: {
keyword: "Search keyword..."
},
select: {
placeholder: "Select"
},
alert: {
delete: "Delete this data?",
cancel: "Cancel",
confirm: "Confirm"
},
status: {
success: "Request successful",
error: "Request failed, please try again",
20000: "Request failed, please try again",
20001: "Please fill in the correct email address",
20002: "Please fill in the login password",
20003: "Please confirm the login password",
20004: "Inconsistent password input",
20005: "Please fill in the correct verification code",
20006: "Login email or password error",
20007: "Email verification code error",
20008: "The email has been registered",
20009: "Please provide the correct server address",
}
}
}

View File

@@ -0,0 +1,28 @@
import {createI18n} from "vue-i18n";
import en from "./en";
import zh from './zh';
const messages = {
en: en,
zh: zh
};
function init(){
let lang: string = "zh";
if(localStorage.getItem("mir2:language")){
lang = localStorage.getItem("mir2:language") + ""
}else{
localStorage.setItem("mir2:language", "zh")
}
return lang
}
export const Language = createI18n({
legacy: false,
locale: init(),
fallbackLocale: "zh",
messages,
warnHtmlMessage: false
});

View File

@@ -0,0 +1,82 @@
export default {
header: {
search: {
tooltip: "搜索"
},
tab: {
dashboard: "仪表盘",
account: "用户管理",
assets: "游戏资产",
operate: "运营维护",
tools: "工具箱"
},
theme: {
tooltip: "主题定制",
title: "自定义",
describe: "为零部件选择样式和颜色",
colour: "颜色",
fillet: "圆角",
style: "样式"
},
user: {
profile: "个人资料",
member: "学科&学生管理",
settings: "设置",
upgrade: "升级套餐",
logout: "退出登录"
}
},
page: {
login: {
title: "连接服务器",
describe: "在下面输入您的服务器信息并连接",
email: "邮箱",
password: "密码",
forgot_password: "忘记密码?",
button: "登录",
account: "没有账号?",
register: {
email: "邮箱",
code: {
title: "验证码",
button: "获取验证码",
loading: "{count}秒后重试"
},
password: "密码",
button: "注册",
account: "已有账号?"
},
},
},
common: {
tooltips: "提示",
nodata: "没有相关数据",
abnormal: "服务请求异常",
incomplete_information: "信息不完整",
search: {
keyword: "搜索关键词..."
},
select: {
placeholder: "选择"
},
alert: {
delete: "是否删除这条数据",
cancel: "取消",
confirm: "确定"
},
status: {
success: "请求成功",
error: "请求失败,请重新尝试",
20000: "请求失败,请重新尝试",
20001: "请填写正确的邮箱",
20002: "请填写登录密码",
20003: "请确认登录密码",
20004: "两次密码不一致",
20005: "请填写正确的验证码",
20006: "登录邮箱或密码错误",
20007: "邮箱验证码错误",
20008: "邮箱已经被注册",
20009: "请填写正确的服务器地址",
}
}
}

View File

@@ -0,0 +1,62 @@
import Axios from "axios";
import FingerprintJS from "@fingerprintjs/fingerprintjs";
const request = Axios.create({
baseURL: "",
timeout: 15000,
});
request.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
error => {
if(error.response){
if (error.response.status) {
return false;
}
}
return false;
}
);
export async function Request(path: string, method: string, params: object, data: object) {
const baseURL: string = "https://api.mir2.geekros.com";
return FingerprintJS.load().then((fp: any) => {
return fp.get().then((result: any) => {
return request({
baseURL: baseURL,
headers: {
"Content-Type": "application/json",
"Accept-Fetch-Id": "desktop",
"Accept-Fetch-Referer": "makeryang.com",
"Accept-Fetch-Visitor": result.visitorId,
"Accept-Fetch-Auth": localStorage.getItem("mir2:login:token") ? localStorage.getItem("mir2:login:token") : ""
},
url: path,
method: method,
params: params ? params : {},
data: data ? data : {}
});
});
}).catch(()=>{
return request({
baseURL: baseURL,
headers: {
"Content-Type": "application/json",
"Accept-Fetch-Id": "desktop",
"Accept-Fetch-Referer": "makeryang.com",
"Accept-Fetch-Visitor": "",
"Accept-Fetch-Auth": ""
},
url: path,
method: method,
params: params ? params : {},
data: data ? data : {}
});
});
}

View File

@@ -0,0 +1,15 @@
import {createRouter, createWebHashHistory } from "vue-router";
import Start from "../../pages/start.vue";
const routes: any = [
{
path: "/",
name: "Start",
component: Start
}
]
export const Router = createRouter({
history: createWebHashHistory(),
routes: routes
});

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import {
AccordionRoot,
type AccordionRootEmits,
type AccordionRootProps,
useForwardPropsEmits,
} from 'radix-vue'
const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AccordionRoot v-bind="forwarded">
<slot />
</AccordionRoot>
</template>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AccordionContent, type AccordionContentProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionContentProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AccordionContent
v-bind="delegatedProps"
class="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
>
<div :class="cn('pb-4 pt-0', props.class)">
<slot />
</div>
</AccordionContent>
</template>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AccordionItem, type AccordionItemProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<AccordionItem
v-bind="forwardedProps"
:class="cn('border-b', props.class)"
>
<slot />
</AccordionItem>
</template>

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import {
AccordionHeader,
AccordionTrigger,
type AccordionTriggerProps,
} from 'radix-vue'
import { ChevronDownIcon } from '@radix-icons/vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AccordionHeader class="flex">
<AccordionTrigger
v-bind="delegatedProps"
:class="
cn(
'flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
props.class,
)
"
>
<slot />
<slot name="icon">
<ChevronDownIcon
class="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200"
/>
</slot>
</AccordionTrigger>
</AccordionHeader>
</template>

View File

@@ -0,0 +1,4 @@
export { default as Accordion } from './Accordion.vue'
export { default as AccordionContent } from './AccordionContent.vue'
export { default as AccordionItem } from './AccordionItem.vue'
export { default as AccordionTrigger } from './AccordionTrigger.vue'

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import { type AlertDialogEmits, type AlertDialogProps, AlertDialogRoot, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<AlertDialogProps>()
const emits = defineEmits<AlertDialogEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AlertDialogRoot v-bind="forwarded">
<slot />
</AlertDialogRoot>
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/lib/button'
const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
<slot />
</AlertDialogAction>
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue'
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/lib/button'
const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AlertDialogCancel v-bind="delegatedProps" :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)">
<slot />
</AlertDialogCancel>
</template>

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import {
AlertDialogContent,
type AlertDialogContentEmits,
type AlertDialogContentProps,
AlertDialogOverlay,
AlertDialogPortal,
useForwardPropsEmits,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<AlertDialogContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<AlertDialogPortal>
<AlertDialogOverlay
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<AlertDialogContent
v-bind="forwarded"
:class="
cn(
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background 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',
props.class,
)
"
>
<slot />
</AlertDialogContent>
</AlertDialogPortal>
</template>

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import {
AlertDialogDescription,
type AlertDialogDescriptionProps,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AlertDialogDescription
v-bind="delegatedProps"
:class="cn('text-sm text-muted-foreground', props.class)"
>
<slot />
</AlertDialogDescription>
</template>

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
:class="
cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
props.class,
)
"
>
<slot />
</div>
</template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
:class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
>
<slot />
</div>
</template>

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AlertDialogTitle
v-bind="delegatedProps"
:class="cn('text-lg font-semibold', props.class)"
>
<slot />
</AlertDialogTitle>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import { AlertDialogTrigger, type AlertDialogTriggerProps } from 'radix-vue'
const props = defineProps<AlertDialogTriggerProps>()
</script>
<template>
<AlertDialogTrigger v-bind="props">
<slot />
</AlertDialogTrigger>
</template>

View File

@@ -0,0 +1,9 @@
export { default as AlertDialog } from './AlertDialog.vue'
export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue'
export { default as AlertDialogContent } from './AlertDialogContent.vue'
export { default as AlertDialogHeader } from './AlertDialogHeader.vue'
export { default as AlertDialogTitle } from './AlertDialogTitle.vue'
export { default as AlertDialogDescription } from './AlertDialogDescription.vue'
export { default as AlertDialogFooter } from './AlertDialogFooter.vue'
export { default as AlertDialogAction } from './AlertDialogAction.vue'
export { default as AlertDialogCancel } from './AlertDialogCancel.vue'

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { type AlertVariants, alertVariants } from '.'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
variant?: AlertVariants['variant']
}>()
</script>
<template>
<div :class="cn(alertVariants({ variant }), props.class)" role="alert">
<slot />
</div>
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div :class="cn('text-sm [&_p]:leading-relaxed', props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
<slot />
</h5>
</template>

View File

@@ -0,0 +1,23 @@
import { type VariantProps, cva } from 'class-variance-authority'
export { default as Alert } from './Alert.vue'
export { default as AlertTitle } from './AlertTitle.vue'
export { default as AlertDescription } from './AlertDescription.vue'
export const alertVariants = cva(
'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
{
variants: {
variant: {
default: 'bg-background text-foreground',
destructive:
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
},
},
defaultVariants: {
variant: 'default',
},
},
)
export type AlertVariants = VariantProps<typeof alertVariants>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import { AspectRatio, type AspectRatioProps } from 'radix-vue'
const props = defineProps<AspectRatioProps>()
</script>
<template>
<AspectRatio v-bind="props">
<slot />
</AspectRatio>
</template>

View File

@@ -0,0 +1 @@
export { default as AspectRatio } from './AspectRatio.vue'

View File

@@ -0,0 +1,105 @@
<script setup lang="ts" generic="T extends ZodObjectOrWrapped">
import { computed, toRefs } from 'vue'
import type { ZodAny, z } from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import type { FormContext, GenericObject } from 'vee-validate'
import { type ZodObjectOrWrapped, getBaseSchema, getBaseType, getDefaultValueInZodStack, getObjectFormSchema } from './utils'
import type { Config, ConfigItem, Dependency, Shape } from './interface'
import AutoFormField from './AutoFormField.vue'
import { provideDependencies } from './dependencies'
import { Form } from '@/lib/registry/new-york/ui/form'
const props = defineProps<{
schema: T
form?: FormContext<GenericObject>
fieldConfig?: Config<z.infer<T>>
dependencies?: Dependency<z.infer<T>>[]
}>()
const emits = defineEmits<{
submit: [event: GenericObject]
}>()
const { dependencies } = toRefs(props)
provideDependencies(dependencies)
const shapes = computed(() => {
// @ts-expect-error ignore {} not assignable to object
const val: { [key in keyof T]: Shape } = {}
const baseSchema = getObjectFormSchema(props.schema)
const shape = baseSchema.shape
Object.keys(shape).forEach((name) => {
const item = shape[name] as ZodAny
const baseItem = getBaseSchema(item) as ZodAny
let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined
if (!Array.isArray(options) && typeof options === 'object')
options = Object.values(options)
val[name as keyof T] = {
type: getBaseType(item),
default: getDefaultValueInZodStack(item),
options,
required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),
schema: baseItem,
}
})
return val
})
const fields = computed(() => {
// @ts-expect-error ignore {} not assignable to object
const val: { [key in keyof z.infer<T>]: { shape: Shape, fieldName: string, config: ConfigItem } } = {}
for (const key in shapes.value) {
const shape = shapes.value[key]
val[key as keyof z.infer<T>] = {
shape,
config: props.fieldConfig?.[key] as ConfigItem,
fieldName: key,
}
}
return val
})
const formComponent = computed(() => props.form ? 'form' : Form)
const formComponentProps = computed(() => {
if (props.form) {
return {
onSubmit: props.form.handleSubmit(val => emits('submit', val)),
}
}
else {
const formSchema = toTypedSchema(props.schema)
return {
keepValues: true,
validationSchema: formSchema,
onSubmit: (val: GenericObject) => emits('submit', val),
}
}
})
</script>
<template>
<component
:is="formComponent"
v-bind="formComponentProps"
>
<slot name="customAutoForm" :fields="fields">
<template v-for="(shape, key) of shapes" :key="key">
<slot
:shape="shape"
:name="key.toString() as keyof z.infer<T>"
:field-name="key.toString()"
:config="fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem"
>
<AutoFormField
:config="fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem"
:field-name="key.toString()"
:shape="shape"
/>
</slot>
</template>
</slot>
<slot :shapes="shapes" />
</component>
</template>

View File

@@ -0,0 +1,45 @@
<script setup lang="ts" generic="U extends ZodAny">
import type { ZodAny } from 'zod'
import { computed } from 'vue'
import type { Config, ConfigItem, Shape } from './interface'
import { DEFAULT_ZOD_HANDLERS, INPUT_COMPONENTS } from './constant'
import useDependencies from './dependencies'
const props = defineProps<{
fieldName: string
shape: Shape
config?: ConfigItem | Config<U>
}>()
function isValidConfig(config: any): config is ConfigItem {
return !!config?.component
}
const delegatedProps = computed(() => {
if (['ZodObject', 'ZodArray'].includes(props.shape?.type))
return { schema: props.shape?.schema }
return undefined
})
const { isDisabled, isHidden, isRequired, overrideOptions } = useDependencies(props.fieldName)
</script>
<template>
<component
:is="isValidConfig(config)
? typeof config.component === 'string'
? INPUT_COMPONENTS[config.component!]
: config.component
: INPUT_COMPONENTS[DEFAULT_ZOD_HANDLERS[shape.type]] "
v-if="!isHidden"
:field-name="fieldName"
:label="shape.schema?.description"
:required="isRequired || shape.required"
:options="overrideOptions || shape.options"
:disabled="isDisabled"
:config="config"
v-bind="delegatedProps"
>
<slot />
</component>
</template>

View File

@@ -0,0 +1,110 @@
<script setup lang="ts" generic="T extends z.ZodAny">
import * as z from 'zod'
import { computed, provide } from 'vue'
import { PlusIcon, TrashIcon } from 'lucide-vue-next'
import { FieldArray, FieldContextKey, useField } from 'vee-validate'
import type { Config, ConfigItem } from './interface'
import { beautifyObjectName, getBaseType } from './utils'
import AutoFormField from './AutoFormField.vue'
import AutoFormLabel from './AutoFormLabel.vue'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Separator } from '@/lib/registry/new-york/ui/separator'
import { FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
const props = defineProps<{
fieldName: string
required?: boolean
config?: Config<T>
schema?: z.ZodArray<T>
disabled?: boolean
}>()
function isZodArray(
item: z.ZodArray<any> | z.ZodDefault<any>,
): item is z.ZodArray<any> {
return item instanceof z.ZodArray
}
function isZodDefault(
item: z.ZodArray<any> | z.ZodDefault<any>,
): item is z.ZodDefault<any> {
return item instanceof z.ZodDefault
}
const itemShape = computed(() => {
if (!props.schema)
return
const schema: z.ZodAny = isZodArray(props.schema)
? props.schema._def.type
: isZodDefault(props.schema)
// @ts-expect-error missing schema
? props.schema._def.innerType._def.type
: null
return {
type: getBaseType(schema),
schema,
}
})
const fieldContext = useField(props.fieldName)
// @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext)
</script>
<template>
<FieldArray v-slot="{ fields, remove, push }" as="section" :name="fieldName">
<slot v-bind="props">
<Accordion type="multiple" class="w-full" collapsible :disabled="disabled" as-child>
<FormItem>
<AccordionItem :value="fieldName" class="border-none">
<AccordionTrigger>
<AutoFormLabel class="text-base" :required="required">
{{ schema?.description || beautifyObjectName(fieldName) }}
</AutoFormLabel>
</AccordionTrigger>
<AccordionContent>
<template v-for="(field, index) of fields" :key="field.key">
<div class="mb-4 p-1">
<AutoFormField
:field-name="`${fieldName}[${index}]`"
:label="fieldName"
:shape="itemShape!"
:config="config as ConfigItem"
/>
<div class="!my-4 flex justify-end">
<Button
type="button"
size="icon"
variant="secondary"
@click="remove(index)"
>
<TrashIcon :size="16" />
</Button>
</div>
<Separator v-if="!field.isLast" />
</div>
</template>
<Button
type="button"
variant="secondary"
class="mt-4 flex items-center"
@click="push(null)"
>
<PlusIcon class="mr-2" :size="16" />
Add
</Button>
</AccordionContent>
<FormMessage />
</AccordionItem>
</FormItem>
</Accordion>
</slot>
</FieldArray>
</template>

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { computed } from 'vue'
import { beautifyObjectName } from './utils'
import type { FieldProps } from './interface'
import AutoFormLabel from './AutoFormLabel.vue'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Switch } from '@/lib/registry/new-york/ui/switch'
import { Checkbox } from '@/lib/registry/new-york/ui/checkbox'
const props = defineProps<FieldProps>()
const booleanComponent = computed(() => props.config?.component === 'switch' ? Switch : Checkbox)
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem>
<div class="space-y-0 mb-3 flex items-center gap-3">
<FormControl>
<slot v-bind="slotProps">
<component
:is="booleanComponent"
v-bind="{ ...slotProps.componentField }"
:disabled="disabled"
:checked="slotProps.componentField.modelValue"
@update:checked="slotProps.componentField['onUpdate:modelValue']"
/>
</slot>
</FormControl>
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label || beautifyObjectName(label ?? fieldName) }}
</AutoFormLabel>
</div>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>

View File

@@ -0,0 +1,57 @@
<script setup lang="ts">
import { DateFormatter, getLocalTimeZone } from '@internationalized/date'
import { CalendarIcon } from '@radix-icons/vue'
import { beautifyObjectName } from './utils'
import AutoFormLabel from './AutoFormLabel.vue'
import type { FieldProps } from './interface'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Calendar } from '@/lib/registry/new-york/ui/calendar'
import { Button } from '@/lib/registry/new-york/ui/button'
import { Popover, PopoverContent, PopoverTrigger } from '@/lib/registry/new-york/ui/popover'
import { cn } from '@/lib/utils'
defineProps<FieldProps>()
const df = new DateFormatter('en-US', {
dateStyle: 'long',
})
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem>
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label || beautifyObjectName(label ?? fieldName) }}
</AutoFormLabel>
<FormControl>
<slot v-bind="slotProps">
<div>
<Popover>
<PopoverTrigger as-child :disabled="disabled">
<Button
variant="outline"
:class="cn(
'w-full justify-start text-left font-normal',
!slotProps.componentField.modelValue && 'text-muted-foreground',
)"
>
<CalendarIcon class="mr-2 h-4 w-4" />
{{ slotProps.componentField.modelValue ? df.format(slotProps.componentField.modelValue.toDate(getLocalTimeZone())) : "Pick a date" }}
</Button>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<Calendar initial-focus v-bind="slotProps.componentField" />
</PopoverContent>
</Popover>
</div>
</slot>
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>

View File

@@ -0,0 +1,49 @@
<script setup lang="ts">
import AutoFormLabel from './AutoFormLabel.vue'
import { beautifyObjectName } from './utils'
import type { FieldProps } from './interface'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/lib/registry/new-york/ui/select'
import { Label } from '@/lib/registry/new-york/ui/label'
import { RadioGroup, RadioGroupItem } from '@/lib/registry/new-york/ui/radio-group'
defineProps<FieldProps & {
options?: string[]
}>()
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem>
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label || beautifyObjectName(label ?? fieldName) }}
</AutoFormLabel>
<FormControl>
<slot v-bind="slotProps">
<RadioGroup v-if="config?.component === 'radio'" :disabled="disabled" :orientation="'vertical'" v-bind="{ ...slotProps.componentField }">
<div v-for="(option, index) in options" :key="option" class="mb-2 flex items-center gap-3 space-y-0">
<RadioGroupItem :id="`${option}-${index}`" :value="option" />
<Label :for="`${option}-${index}`">{{ beautifyObjectName(option) }}</Label>
</div>
</RadioGroup>
<Select v-else :disabled="disabled" v-bind="{ ...slotProps.componentField }">
<SelectTrigger class="w-full">
<SelectValue :placeholder="config?.inputProps?.placeholder" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="option in options" :key="option" :value="option">
{{ beautifyObjectName(option) }}
</SelectItem>
</SelectContent>
</Select>
</slot>
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>

View File

@@ -0,0 +1,74 @@
<script setup lang="ts">
import { ref } from 'vue'
import { TrashIcon } from '@radix-icons/vue'
import { beautifyObjectName } from './utils'
import type { FieldProps } from './interface'
import AutoFormLabel from './AutoFormLabel.vue'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Button } from '@/lib/registry/new-york/ui/button'
defineProps<FieldProps>()
const inputFile = ref<File>()
async function parseFileAsString(file: File | undefined): Promise<string> {
return new Promise((resolve, reject) => {
if (file) {
const reader = new FileReader()
reader.onloadend = () => {
resolve(reader.result as string)
}
reader.onerror = (err) => {
reject(err)
}
reader.readAsDataURL(file)
}
})
}
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem v-bind="$attrs">
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label || beautifyObjectName(label ?? fieldName) }}
</AutoFormLabel>
<FormControl>
<slot v-bind="slotProps">
<Input
v-if="!inputFile"
type="file"
v-bind="{ ...config?.inputProps }"
:disabled="disabled"
@change="async (ev: InputEvent) => {
const file = (ev.target as HTMLInputElement).files?.[0]
inputFile = file
const parsed = await parseFileAsString(file)
slotProps.componentField.onInput(parsed)
}"
/>
<div v-else class="flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent pl-3 pr-1 py-1 text-sm shadow-sm transition-colors">
<p>{{ inputFile?.name }}</p>
<Button
:size="'icon'"
:variant="'ghost'"
class="h-[26px] w-[26px]"
aria-label="Remove file"
type="button"
@click="() => {
inputFile = undefined
slotProps.componentField.onInput(undefined)
}"
>
<TrashIcon />
</Button>
</div>
</slot>
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>

View File

@@ -0,0 +1,36 @@
<script setup lang="ts">
import { computed } from 'vue'
import AutoFormLabel from './AutoFormLabel.vue'
import { beautifyObjectName } from './utils'
import type { FieldProps } from './interface'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Input } from '@/lib/registry/new-york/ui/input'
import { Textarea } from '@/lib/registry/new-york/ui/textarea'
const props = defineProps<FieldProps>()
const inputComponent = computed(() => props.config?.component === 'textarea' ? Textarea : Input)
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem v-bind="$attrs">
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label || beautifyObjectName(label ?? fieldName) }}
</AutoFormLabel>
<FormControl>
<slot v-bind="slotProps">
<component
:is="inputComponent"
type="text"
v-bind="{ ...slotProps.componentField, ...config?.inputProps }"
:disabled="disabled"
/>
</slot>
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import AutoFormLabel from './AutoFormLabel.vue'
import { beautifyObjectName } from './utils'
import type { FieldProps } from './interface'
import { FormControl, FormDescription, FormField, FormItem, FormMessage } from '@/lib/registry/new-york/ui/form'
import { Input } from '@/lib/registry/new-york/ui/input'
defineOptions({
inheritAttrs: false,
})
defineProps<FieldProps>()
</script>
<template>
<FormField v-slot="slotProps" :name="fieldName">
<FormItem>
<AutoFormLabel v-if="!config?.hideLabel" :required="required">
{{ config?.label || beautifyObjectName(label ?? fieldName) }}
</AutoFormLabel>
<FormControl>
<slot v-bind="slotProps">
<Input type="number" v-bind="{ ...slotProps.componentField, ...config?.inputProps }" :disabled="disabled" />
</slot>
</FormControl>
<FormDescription v-if="config?.description">
{{ config.description }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
</template>

View File

@@ -0,0 +1,78 @@
<script setup lang="ts" generic="T extends ZodRawShape">
import type { ZodAny, ZodObject, ZodRawShape } from 'zod'
import { computed, provide } from 'vue'
import { FieldContextKey, useField } from 'vee-validate'
import AutoFormField from './AutoFormField.vue'
import type { Config, ConfigItem, Shape } from './interface'
import { beautifyObjectName, getBaseSchema, getBaseType, getDefaultValueInZodStack } from './utils'
import AutoFormLabel from './AutoFormLabel.vue'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/lib/registry/new-york/ui/accordion'
import { FormItem } from '@/lib/registry/new-york/ui/form'
const props = defineProps<{
fieldName: string
required?: boolean
config?: Config<T>
schema?: ZodObject<T>
disabled?: boolean
}>()
const shapes = computed(() => {
// @ts-expect-error ignore {} not assignable to object
const val: { [key in keyof T]: Shape } = {}
if (!props.schema)
return
const shape = getBaseSchema(props.schema)?.shape
if (!shape)
return
Object.keys(shape).forEach((name) => {
const item = shape[name] as ZodAny
const baseItem = getBaseSchema(item) as ZodAny
let options = (baseItem && 'values' in baseItem._def) ? baseItem._def.values as string[] : undefined
if (!Array.isArray(options) && typeof options === 'object')
options = Object.values(options)
val[name as keyof T] = {
type: getBaseType(item),
default: getDefaultValueInZodStack(item),
options,
required: !['ZodOptional', 'ZodNullable'].includes(item._def.typeName),
schema: item,
}
})
return val
})
const fieldContext = useField(props.fieldName)
// @ts-expect-error ignore missing `id`
provide(FieldContextKey, fieldContext)
</script>
<template>
<section>
<slot v-bind="props">
<Accordion type="single" as-child class="w-full" collapsible :disabled="disabled">
<FormItem>
<AccordionItem :value="fieldName" class="border-none">
<AccordionTrigger>
<AutoFormLabel class="text-base" :required="required">
{{ schema?.description || beautifyObjectName(fieldName) }}
</AutoFormLabel>
</AccordionTrigger>
<AccordionContent class="p-1 space-y-5">
<template v-for="(shape, key) in shapes" :key="key">
<AutoFormField
:config="config?.[key as keyof typeof config] as ConfigItem"
:field-name="`${fieldName}.${key.toString()}`"
:label="key.toString()"
:shape="shape"
/>
</template>
</AccordionContent>
</AccordionItem>
</FormItem>
</Accordion>
</slot>
</section>
</template>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import { FormLabel } from '@/lib/registry/new-york/ui/form'
defineProps<{
required?: boolean
}>()
</script>
<template>
<FormLabel>
<slot />
<span v-if="required" class="text-destructive"> *</span>
</FormLabel>
</template>

View File

@@ -0,0 +1,39 @@
import AutoFormFieldArray from './AutoFormFieldArray.vue'
import AutoFormFieldBoolean from './AutoFormFieldBoolean.vue'
import AutoFormFieldDate from './AutoFormFieldDate.vue'
import AutoFormFieldEnum from './AutoFormFieldEnum.vue'
import AutoFormFieldFile from './AutoFormFieldFile.vue'
import AutoFormFieldInput from './AutoFormFieldInput.vue'
import AutoFormFieldNumber from './AutoFormFieldNumber.vue'
import AutoFormFieldObject from './AutoFormFieldObject.vue'
export const INPUT_COMPONENTS = {
date: AutoFormFieldDate,
select: AutoFormFieldEnum,
radio: AutoFormFieldEnum,
checkbox: AutoFormFieldBoolean,
switch: AutoFormFieldBoolean,
textarea: AutoFormFieldInput,
number: AutoFormFieldNumber,
string: AutoFormFieldInput,
file: AutoFormFieldFile,
array: AutoFormFieldArray,
object: AutoFormFieldObject,
}
/**
* Define handlers for specific Zod types.
* You can expand this object to support more types.
*/
export const DEFAULT_ZOD_HANDLERS: {
[key: string]: keyof typeof INPUT_COMPONENTS
} = {
ZodString: 'string',
ZodBoolean: 'checkbox',
ZodDate: 'date',
ZodEnum: 'select',
ZodNativeEnum: 'select',
ZodNumber: 'number',
ZodArray: 'array',
ZodObject: 'object',
}

View File

@@ -0,0 +1,92 @@
import type * as z from 'zod'
import type { Ref } from 'vue'
import { computed, ref, watch } from 'vue'
import { useFieldValue, useFormValues } from 'vee-validate'
import { createContext } from 'radix-vue'
import { type Dependency, DependencyType, type EnumValues } from './interface'
import { getFromPath, getIndexIfArray } from './utils'
export const [injectDependencies, provideDependencies] = createContext<Ref<Dependency<z.infer<z.ZodObject<any>>>[] | undefined>>('AutoFormDependencies')
export default function useDependencies(
fieldName: string,
) {
const form = useFormValues()
// parsed test[0].age => test.age
const currentFieldName = fieldName.replace(/\[\d+\]/g, '')
const currentFieldValue = useFieldValue<any>(fieldName)
if (!form)
throw new Error('useDependencies should be used within <AutoForm>')
const dependencies = injectDependencies()
const isDisabled = ref(false)
const isHidden = ref(false)
const isRequired = ref(false)
const overrideOptions = ref<EnumValues | undefined>()
const currentFieldDependencies = computed(() => dependencies.value?.filter(
dependency => dependency.targetField === currentFieldName,
))
function getSourceValue(dep: Dependency<any>) {
const source = dep.sourceField as string
const index = getIndexIfArray(fieldName) ?? -1
const [sourceLast, ...sourceInitial] = source.split('.').toReversed()
const [_targetLast, ...targetInitial] = (dep.targetField as string).split('.').toReversed()
if (index >= 0 && sourceInitial.join(',') === targetInitial.join(',')) {
const [_currentLast, ...currentInitial] = fieldName.split('.').toReversed()
return getFromPath(form.value, currentInitial.join('.') + sourceLast)
}
return getFromPath(form.value, source)
}
const sourceFieldValues = computed(() => currentFieldDependencies.value?.map(dep => getSourceValue(dep)))
const resetConditionState = () => {
isDisabled.value = false
isHidden.value = false
isRequired.value = false
overrideOptions.value = undefined
}
watch([sourceFieldValues, dependencies], () => {
resetConditionState()
currentFieldDependencies.value?.forEach((dep) => {
const sourceValue = getSourceValue(dep)
const conditionMet = dep.when(sourceValue, currentFieldValue.value)
switch (dep.type) {
case DependencyType.DISABLES:
if (conditionMet)
isDisabled.value = true
break
case DependencyType.REQUIRES:
if (conditionMet)
isRequired.value = true
break
case DependencyType.HIDES:
if (conditionMet)
isHidden.value = true
break
case DependencyType.SETS_OPTIONS:
if (conditionMet)
overrideOptions.value = dep.options
break
}
})
}, { immediate: true, deep: true })
return {
isDisabled,
isHidden,
isRequired,
overrideOptions,
}
}

View File

@@ -0,0 +1,15 @@
export { getObjectFormSchema, getBaseSchema, getBaseType } from './utils'
export type { Config, ConfigItem, FieldProps } from './interface'
export { default as AutoForm } from './AutoForm.vue'
export { default as AutoFormField } from './AutoFormField.vue'
export { default as AutoFormLabel } from './AutoFormLabel.vue'
export { default as AutoFormFieldArray } from './AutoFormFieldArray.vue'
export { default as AutoFormFieldBoolean } from './AutoFormFieldBoolean.vue'
export { default as AutoFormFieldDate } from './AutoFormFieldDate.vue'
export { default as AutoFormFieldEnum } from './AutoFormFieldEnum.vue'
export { default as AutoFormFieldFile } from './AutoFormFieldFile.vue'
export { default as AutoFormFieldInput } from './AutoFormFieldInput.vue'
export { default as AutoFormFieldNumber } from './AutoFormFieldNumber.vue'
export { default as AutoFormFieldObject } from './AutoFormFieldObject.vue'

View File

@@ -0,0 +1,81 @@
import type { Component, InputHTMLAttributes } from 'vue'
import type { ZodAny, z } from 'zod'
import type { INPUT_COMPONENTS } from './constant'
export interface FieldProps {
fieldName: string
label?: string
required?: boolean
config?: ConfigItem
disabled?: boolean
}
export interface Shape {
type: string
default?: any
required?: boolean
options?: string[]
schema?: ZodAny
}
export interface ConfigItem {
/** Value for the `FormLabel` */
label?: string
/** Value for the `FormDescription` */
description?: string
/** Pick which component to be rendered. */
component?: keyof typeof INPUT_COMPONENTS | Component
/** Hide `FormLabel`. */
hideLabel?: boolean
inputProps?: InputHTMLAttributes
}
// Define a type to unwrap an array
type UnwrapArray<T> = T extends (infer U)[] ? U : never
export type Config<SchemaType extends object> = {
// If SchemaType.key is an object, create a nested Config, otherwise ConfigItem
[Key in keyof SchemaType]?:
SchemaType[Key] extends any[]
? UnwrapArray<Config<SchemaType[Key]>>
: SchemaType[Key] extends object
? Config<SchemaType[Key]>
: ConfigItem;
}
export enum DependencyType {
DISABLES,
REQUIRES,
HIDES,
SETS_OPTIONS,
}
interface BaseDependency<SchemaType extends z.infer<z.ZodObject<any, any>>> {
sourceField: keyof SchemaType
type: DependencyType
targetField: keyof SchemaType
when: (sourceFieldValue: any, targetFieldValue: any) => boolean
}
export type ValueDependency<SchemaType extends z.infer<z.ZodObject<any, any>>> =
BaseDependency<SchemaType> & {
type:
| DependencyType.DISABLES
| DependencyType.REQUIRES
| DependencyType.HIDES
}
export type EnumValues = readonly [string, ...string[]]
export type OptionsDependency<
SchemaType extends z.infer<z.ZodObject<any, any>>,
> = BaseDependency<SchemaType> & {
type: DependencyType.SETS_OPTIONS
// Partial array of values from sourceField that will trigger the dependency
options: EnumValues
}
export type Dependency<SchemaType extends z.infer<z.ZodObject<any, any>>> =
| ValueDependency<SchemaType>
| OptionsDependency<SchemaType>

View File

@@ -0,0 +1,171 @@
import type { z } from 'zod'
// TODO: This should support recursive ZodEffects but TypeScript doesn't allow circular type definitions.
export type ZodObjectOrWrapped =
| z.ZodObject<any, any>
| z.ZodEffects<z.ZodObject<any, any>>
/**
* Beautify a camelCase string.
* e.g. "myString" -> "My String"
*/
export function beautifyObjectName(string: string) {
// Remove bracketed indices
// if numbers only return the string
let output = string.replace(/\[\d+\]/g, '').replace(/([A-Z])/g, ' $1')
output = output.charAt(0).toUpperCase() + output.slice(1)
return output
}
/**
* Parse string and extract the index
* @param string
* @returns index or undefined
*/
export function getIndexIfArray(string: string) {
const indexRegex = /\[(\d+)\]/
// Match the index
const match = string.match(indexRegex)
// Extract the index (number)
const index = match ? Number.parseInt(match[1]) : undefined
return index
}
/**
* Get the lowest level Zod type.
* This will unpack optionals, refinements, etc.
*/
export function getBaseSchema<
ChildType extends z.ZodAny | z.AnyZodObject = z.ZodAny,
>(schema: ChildType | z.ZodEffects<ChildType>): ChildType | null {
if (!schema)
return null
if ('innerType' in schema._def)
return getBaseSchema(schema._def.innerType as ChildType)
if ('schema' in schema._def)
return getBaseSchema(schema._def.schema as ChildType)
return schema as ChildType
}
/**
* Get the type name of the lowest level Zod type.
* This will unpack optionals, refinements, etc.
*/
export function getBaseType(schema: z.ZodAny) {
const baseSchema = getBaseSchema(schema)
return baseSchema ? baseSchema._def.typeName : ''
}
/**
* Search for a "ZodDefault" in the Zod stack and return its value.
*/
export function getDefaultValueInZodStack(schema: z.ZodAny): any {
const typedSchema = schema as unknown as z.ZodDefault<
z.ZodNumber | z.ZodString
>
if (typedSchema._def.typeName === 'ZodDefault')
return typedSchema._def.defaultValue()
if ('innerType' in typedSchema._def) {
return getDefaultValueInZodStack(
typedSchema._def.innerType as unknown as z.ZodAny,
)
}
if ('schema' in typedSchema._def) {
return getDefaultValueInZodStack(
(typedSchema._def as any).schema as z.ZodAny,
)
}
return undefined
}
export function getObjectFormSchema(
schema: ZodObjectOrWrapped,
): z.ZodObject<any, any> {
if (schema?._def.typeName === 'ZodEffects') {
const typedSchema = schema as z.ZodEffects<z.ZodObject<any, any>>
return getObjectFormSchema(typedSchema._def.schema)
}
return schema as z.ZodObject<any, any>
}
function isIndex(value: unknown): value is number {
return Number(value) >= 0
}
/**
* Constructs a path with dot paths for arrays to use brackets to be compatible with vee-validate path syntax
*/
export function normalizeFormPath(path: string): string {
const pathArr = path.split('.')
if (!pathArr.length)
return ''
let fullPath = String(pathArr[0])
for (let i = 1; i < pathArr.length; i++) {
if (isIndex(pathArr[i])) {
fullPath += `[${pathArr[i]}]`
continue
}
fullPath += `.${pathArr[i]}`
}
return fullPath
}
type NestedRecord = Record<string, unknown> | { [k: string]: NestedRecord }
/**
* Checks if the path opted out of nested fields using `[fieldName]` syntax
*/
export function isNotNestedPath(path: string) {
return /^\[.+\]$/i.test(path)
}
function isObject(obj: unknown): obj is Record<string, unknown> {
return obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj)
}
function isContainerValue(value: unknown): value is Record<string, unknown> {
return isObject(value) || Array.isArray(value)
}
function cleanupNonNestedPath(path: string) {
if (isNotNestedPath(path))
return path.replace(/\[|\]/gi, '')
return path
}
/**
* Gets a nested property value from an object
*/
export function getFromPath<TValue = unknown>(object: NestedRecord | undefined, path: string): TValue | undefined
export function getFromPath<TValue = unknown, TFallback = TValue>(
object: NestedRecord | undefined,
path: string,
fallback?: TFallback,
): TValue | TFallback
export function getFromPath<TValue = unknown, TFallback = TValue>(
object: NestedRecord | undefined,
path: string,
fallback?: TFallback,
): TValue | TFallback | undefined {
if (!object)
return fallback
if (isNotNestedPath(path))
return object[cleanupNonNestedPath(path)] as TValue | undefined
const resolvedValue = (path || '')
.split(/\.|\[(\d+)\]/)
.filter(Boolean)
.reduce((acc, propKey) => {
if (isContainerValue(acc) && propKey in acc)
return acc[propKey]
return fallback
}, object as unknown)
return resolvedValue as TValue | undefined
}

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { AvatarRoot } from 'radix-vue'
import { type AvatarVariants, avatarVariant } from '.'
import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<{
class?: HTMLAttributes['class']
size?: AvatarVariants['size']
shape?: AvatarVariants['shape']
}>(), {
size: 'sm',
shape: 'circle',
})
</script>
<template>
<AvatarRoot :class="cn(avatarVariant({ size, shape }), props.class)">
<slot />
</AvatarRoot>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import { AvatarFallback, type AvatarFallbackProps } from 'radix-vue'
const props = defineProps<AvatarFallbackProps>()
</script>
<template>
<AvatarFallback v-bind="props">
<slot />
</AvatarFallback>
</template>

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
import { AvatarImage, type AvatarImageProps } from 'radix-vue'
const props = defineProps<AvatarImageProps>()
</script>
<template>
<AvatarImage v-bind="props" class="h-full w-full object-cover" />
</template>

View File

@@ -0,0 +1,24 @@
import { type VariantProps, cva } from 'class-variance-authority'
export { default as Avatar } from './Avatar.vue'
export { default as AvatarImage } from './AvatarImage.vue'
export { default as AvatarFallback } from './AvatarFallback.vue'
export const avatarVariant = cva(
'inline-flex items-center justify-center font-normal text-foreground select-none shrink-0 bg-secondary overflow-hidden',
{
variants: {
size: {
sm: 'h-10 w-10 text-xs',
base: 'h-16 w-16 text-2xl',
lg: 'h-32 w-32 text-5xl',
},
shape: {
circle: 'rounded-full',
square: 'rounded-md',
},
},
},
)
export type AvatarVariants = VariantProps<typeof avatarVariant>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { type BadgeVariants, badgeVariants } from '.'
import { cn } from '@/lib/utils'
const props = defineProps<{
variant?: BadgeVariants['variant']
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div :class="cn(badgeVariants({ variant }), props.class)">
<slot />
</div>
</template>

View File

@@ -0,0 +1,25 @@
import { type VariantProps, cva } from 'class-variance-authority'
export { default as Badge } from './Badge.vue'
export const badgeVariants = cva(
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
default:
'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
secondary:
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
destructive:
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
},
)
export type BadgeVariants = VariantProps<typeof badgeVariants>

View File

@@ -0,0 +1,13 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<nav aria-label="breadcrumb" :class="props.class">
<slot />
</nav>
</template>

View File

@@ -0,0 +1,22 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { DotsHorizontalIcon } from '@radix-icons/vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<span
role="presentation"
aria-hidden="true"
:class="cn('flex h-9 w-9 items-center justify-center', props.class)"
>
<slot>
<DotsHorizontalIcon class="h-4 w-4" />
</slot>
<span class="sr-only">More</span>
</span>
</template>

View File

@@ -0,0 +1,16 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<li
:class="cn('inline-flex items-center gap-1.5', props.class)"
>
<slot />
</li>
</template>

View File

@@ -0,0 +1,19 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = withDefaults(defineProps<PrimitiveProps & { class?: HTMLAttributes['class'] }>(), {
as: 'a',
})
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn('transition-colors hover:text-foreground', props.class)"
>
<slot />
</Primitive>
</template>

View File

@@ -0,0 +1,16 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<ol
:class="cn('flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5', props.class)"
>
<slot />
</ol>
</template>

View File

@@ -0,0 +1,19 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<span
role="link"
aria-disabled="true"
aria-current="page"
:class="cn('font-normal text-foreground', props.class)"
>
<slot />
</span>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { ChevronRightIcon } from '@radix-icons/vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<li
role="presentation"
aria-hidden="true"
:class="cn('[&>svg]:size-3.5', props.class)"
>
<slot>
<ChevronRightIcon />
</slot>
</li>
</template>

View File

@@ -0,0 +1,7 @@
export { default as Breadcrumb } from './Breadcrumb.vue'
export { default as BreadcrumbEllipsis } from './BreadcrumbEllipsis.vue'
export { default as BreadcrumbItem } from './BreadcrumbItem.vue'
export { default as BreadcrumbLink } from './BreadcrumbLink.vue'
export { default as BreadcrumbList } from './BreadcrumbList.vue'
export { default as BreadcrumbPage } from './BreadcrumbPage.vue'
export { default as BreadcrumbSeparator } from './BreadcrumbSeparator.vue'

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'radix-vue'
import { type ButtonVariants, buttonVariants } from '.'
import { cn } from '@/lib/utils'
interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
class?: HTMLAttributes['class']
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot />
</Primitive>
</template>

View File

@@ -0,0 +1,35 @@
import { type VariantProps, cva } from 'class-variance-authority'
export { default as Button } from './Button.vue'
export const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline:
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-9 px-4 py-2',
xs: 'h-7 rounded px-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'h-9 w-9',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)
export type ButtonVariants = VariantProps<typeof buttonVariants>

View File

@@ -0,0 +1,60 @@
<script lang="ts" setup>
import { type HTMLAttributes, computed } from 'vue'
import { CalendarRoot, type CalendarRootEmits, type CalendarRootProps, useForwardPropsEmits } from 'radix-vue'
import { CalendarCell, CalendarCellTrigger, CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNextButton, CalendarPrevButton } from '.'
import { cn } from '@/lib/utils'
const props = defineProps<CalendarRootProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<CalendarRootEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<CalendarRoot
v-slot="{ grid, weekDays }"
:class="cn('p-3', props.class)"
v-bind="forwarded"
>
<CalendarHeader>
<CalendarPrevButton />
<CalendarHeading />
<CalendarNextButton />
</CalendarHeader>
<div class="flex flex-col gap-y-4 mt-4 sm:flex-row sm:gap-x-4 sm:gap-y-0">
<CalendarGrid v-for="month in grid" :key="month.value.toString()">
<CalendarGridHead>
<CalendarGridRow>
<CalendarHeadCell
v-for="day in weekDays" :key="day"
>
{{ day }}
</CalendarHeadCell>
</CalendarGridRow>
</CalendarGridHead>
<CalendarGridBody>
<CalendarGridRow v-for="(weekDates, index) in month.rows" :key="`weekDate-${index}`" class="mt-2 w-full">
<CalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
>
<CalendarCellTrigger
:day="weekDate"
:month="month.value"
/>
</CalendarCell>
</CalendarGridRow>
</CalendarGridBody>
</CalendarGrid>
</div>
</CalendarRoot>
</template>

View File

@@ -0,0 +1,24 @@
<script lang="ts" setup>
import { type HTMLAttributes, computed } from 'vue'
import { CalendarCell, type CalendarCellProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<CalendarCellProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<CalendarCell
:class="cn('relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50', props.class)"
v-bind="forwardedProps"
>
<slot />
</CalendarCell>
</template>

View File

@@ -0,0 +1,38 @@
<script lang="ts" setup>
import { type HTMLAttributes, computed } from 'vue'
import { CalendarCellTrigger, type CalendarCellTriggerProps, useForwardProps } from 'radix-vue'
import { buttonVariants } from '@/lib/registry/default/ui/button'
import { cn } from '@/lib/utils'
const props = defineProps<CalendarCellTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<CalendarCellTrigger
:class="cn(
buttonVariants({ variant: 'ghost' }),
'h-8 w-8 p-0 font-normal',
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
// Selected
'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground',
// Disabled
'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
// Unavailable
'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
// Outside months
'data-[outside-month]:pointer-events-none data-[outside-month]:text-muted-foreground data-[outside-month]:opacity-50 [&[data-outside-month][data-selected]]:bg-accent/50 [&[data-outside-month][data-selected]]:text-muted-foreground [&[data-outside-month][data-selected]]:opacity-30',
props.class,
)"
v-bind="forwardedProps"
>
<slot />
</CalendarCellTrigger>
</template>

View File

@@ -0,0 +1,24 @@
<script lang="ts" setup>
import { type HTMLAttributes, computed } from 'vue'
import { CalendarGrid, type CalendarGridProps, useForwardProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<CalendarGridProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<CalendarGrid
:class="cn('w-full border-collapse space-y-1', props.class)"
v-bind="forwardedProps"
>
<slot />
</CalendarGrid>
</template>

View File

@@ -0,0 +1,11 @@
<script lang="ts" setup>
import { CalendarGridBody, type CalendarGridBodyProps } from 'radix-vue'
const props = defineProps<CalendarGridBodyProps>()
</script>
<template>
<CalendarGridBody v-bind="props">
<slot />
</CalendarGridBody>
</template>

View File

@@ -0,0 +1,12 @@
<script lang="ts" setup>
import type { HTMLAttributes } from 'vue'
import { CalendarGridHead, type CalendarGridHeadProps } from 'radix-vue'
const props = defineProps<CalendarGridHeadProps & { class?: HTMLAttributes['class'] }>()
</script>
<template>
<CalendarGridHead v-bind="props">
<slot />
</CalendarGridHead>
</template>

Some files were not shown because too many files have changed in this diff Show More