feature/implement vault allocation (#11)

* feat: add authentication middleware

* feat: add REST API endpoints for database interactions

* refactor: move DiscoveryDocument Pkl schema to oidc module

* fix: replace sonrd with test_node.sh

* feat: use NFT keeper to mint DID namespace NFT

* refactor: move NFT class configuration to types

* feat: add GlobalIntegrity genesis state

* fix: ensure GlobalIntegrity is initialized in genesis

* refactor: update all references to transactions module

* refactor: improve genesis state struct

* chore(did): update discovery endpoint to reflect base url

* feat: remove unused context cache and client code

* refactor: remove middleware dependency from keeper

* feat: Add new query handlers for DID module

* feat: Implement unimplemented params queries

* feat: add support for first-party caveats

* refactor: move motr command to cmd directory

* feat: add support for GitHub releases

* fix(motr): build app.wasm for motr package

* feat: add card component

* feat: add IndexedDB support for persistent storage

* feat: Add Row and Column components

* feat: add  and  components

* refactor: improve button component

* refactor: remove unnecessary button parameter in renderButton

* feat: add vault service endpoint

* feat: add input component
This commit is contained in:
Prad Nukala 2024-09-14 12:47:25 -04:00 committed by GitHub
parent bbfe2a2329
commit b593245fe6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
133 changed files with 13352 additions and 4247 deletions

View File

@ -4,6 +4,8 @@ name: "Release Binary"
on:
push:
branches:
- master
tags:
- v*

3
.gitignore vendored
View File

@ -76,3 +76,6 @@ sonr
deploy/**/data
x/.DS_Store
.aider*
!motr/build

View File

@ -2,7 +2,7 @@ project_name: core
release:
github:
owner: di-dao
owner: onsonr
name: core
name_template: "{{.Tag}}"
@ -13,44 +13,22 @@ builds:
goos:
- linux
- darwin
# - windows
goarch:
- amd64
# - arm64
# - "386"
goarm:
- "6"
gomips:
- hardfloat
goamd64:
- v1
targets:
- linux_amd64_v1
# - darwin_amd64_v1
# - linux_arm64
# - linux_386
# - darwin_arm64
# - windows_amd64_v1
# - windows_arm64
# - windows_386
dir: .
main: ./cmd/sonrd
binary: sonrd
builder: go
gobinary: go
command: build
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
archives:
- id: default
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
format: tar.gz
files:
- src: license*
- src: LICENSE*
- src: readme*
- src: README*
- src: changelog*
- src: CHANGELOG*
snapshot:
name_template: "{{ .Version }}-SNAPSHOT-{{ .ShortCommit }}"
@ -76,28 +54,11 @@ announce:
reddit:
title_template: "{{ .ProjectName }} {{ .Tag }} is out!"
url_template: "{{ .ReleaseURL }}"
slack:
message_template: "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"
username: GoReleaser
discord:
message_template: "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"
author: GoReleaser
color: "3888754"
icon_url: https://goreleaser.com/static/avatar.png
teams:
title_template: "{{ .ProjectName }} {{ .Tag }} is out!"
message_template: "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"
color: "#2D313E"
icon_url: https://goreleaser.com/static/avatar.png
smtp:
subject_template: "{{ .ProjectName }} {{ .Tag }} is out!"
body_template: "You can view details from: {{ .ReleaseURL }}"
mattermost:
message_template: "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"
title_template: "{{ .ProjectName }} {{ .Tag }} is out!"
username: GoReleaser
linkedin:
message_template: "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"
telegram:
message_template: "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"
webhook:

View File

@ -86,4 +86,4 @@ WORKDIR /opt
# rest server, tendermint p2p, tendermint rpc
EXPOSE 1317 26656 26657
ENTRYPOINT ["/usr/bin/sonrd"]
CMD ["test_node.sh"]

View File

@ -307,7 +307,7 @@ dwn:
motr:
@echo "(motr) Building app.wasm -> Deploy to Cloudflare Workers"
GOOS=js GOARCH=wasm go build -o ./motr/build/app.wasm ./motr
GOOS=js GOARCH=wasm go build -o ./cmd/motr/build/app.wasm ./cmd/motr/main.go
###############################################################################
### help ###

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -19,9 +19,14 @@ import (
const _ = grpc.SupportPackageIsVersion7
const (
Query_Params_FullMethodName = "/did.v1.Query/Params"
Query_Resolve_FullMethodName = "/did.v1.Query/Resolve"
Query_Service_FullMethodName = "/did.v1.Query/Service"
Query_Params_FullMethodName = "/did.v1.Query/Params"
Query_ParamsAssets_FullMethodName = "/did.v1.Query/ParamsAssets"
Query_ParamsByAsset_FullMethodName = "/did.v1.Query/ParamsByAsset"
Query_ParamsKeys_FullMethodName = "/did.v1.Query/ParamsKeys"
Query_ParamsByKey_FullMethodName = "/did.v1.Query/ParamsByKey"
Query_RegistrationOptionsByKey_FullMethodName = "/did.v1.Query/RegistrationOptionsByKey"
Query_Resolve_FullMethodName = "/did.v1.Query/Resolve"
Query_Service_FullMethodName = "/did.v1.Query/Service"
)
// QueryClient is the client API for Query service.
@ -29,7 +34,17 @@ const (
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type QueryClient interface {
// Params queries all parameters of the module.
Params(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
Params(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
// ParamsAssets queries all parameters of the module.
ParamsAssets(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
// Params queries all parameters of the module.
ParamsByAsset(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
// ParamsKeys queries all parameters of the module.
ParamsKeys(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
// Params queries all parameters of the module.
ParamsByKey(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
// Params queries all parameters of the module.
RegistrationOptionsByKey(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
// Resolve queries the DID document by its id.
Resolve(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error)
// Service returns associated ServiceInfo for a given Origin
@ -46,8 +61,8 @@ func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
return &queryClient{cc}
}
func (c *queryClient) Params(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
func (c *queryClient) Params(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
out := new(QueryParamsResponse)
err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
@ -55,6 +70,51 @@ func (c *queryClient) Params(ctx context.Context, in *QueryRequest, opts ...grpc
return out, nil
}
func (c *queryClient) ParamsAssets(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, Query_ParamsAssets_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryClient) ParamsByAsset(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, Query_ParamsByAsset_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryClient) ParamsKeys(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, Query_ParamsKeys_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryClient) ParamsByKey(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, Query_ParamsByKey_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryClient) RegistrationOptionsByKey(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, Query_RegistrationOptionsByKey_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryClient) Resolve(ctx context.Context, in *QueryRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, Query_Resolve_FullMethodName, in, out, opts...)
@ -78,7 +138,17 @@ func (c *queryClient) Service(ctx context.Context, in *QueryRequest, opts ...grp
// for forward compatibility
type QueryServer interface {
// Params queries all parameters of the module.
Params(context.Context, *QueryRequest) (*QueryResponse, error)
Params(context.Context, *QueryRequest) (*QueryParamsResponse, error)
// ParamsAssets queries all parameters of the module.
ParamsAssets(context.Context, *QueryRequest) (*QueryResponse, error)
// Params queries all parameters of the module.
ParamsByAsset(context.Context, *QueryRequest) (*QueryResponse, error)
// ParamsKeys queries all parameters of the module.
ParamsKeys(context.Context, *QueryRequest) (*QueryResponse, error)
// Params queries all parameters of the module.
ParamsByKey(context.Context, *QueryRequest) (*QueryResponse, error)
// Params queries all parameters of the module.
RegistrationOptionsByKey(context.Context, *QueryRequest) (*QueryResponse, error)
// Resolve queries the DID document by its id.
Resolve(context.Context, *QueryRequest) (*QueryResponse, error)
// Service returns associated ServiceInfo for a given Origin
@ -92,9 +162,24 @@ type QueryServer interface {
type UnimplementedQueryServer struct {
}
func (UnimplementedQueryServer) Params(context.Context, *QueryRequest) (*QueryResponse, error) {
func (UnimplementedQueryServer) Params(context.Context, *QueryRequest) (*QueryParamsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
}
func (UnimplementedQueryServer) ParamsAssets(context.Context, *QueryRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ParamsAssets not implemented")
}
func (UnimplementedQueryServer) ParamsByAsset(context.Context, *QueryRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ParamsByAsset not implemented")
}
func (UnimplementedQueryServer) ParamsKeys(context.Context, *QueryRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ParamsKeys not implemented")
}
func (UnimplementedQueryServer) ParamsByKey(context.Context, *QueryRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ParamsByKey not implemented")
}
func (UnimplementedQueryServer) RegistrationOptionsByKey(context.Context, *QueryRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RegistrationOptionsByKey not implemented")
}
func (UnimplementedQueryServer) Resolve(context.Context, *QueryRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Resolve not implemented")
}
@ -132,6 +217,96 @@ func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interf
return interceptor(ctx, in, info, handler)
}
func _Query_ParamsAssets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).ParamsAssets(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Query_ParamsAssets_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).ParamsAssets(ctx, req.(*QueryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Query_ParamsByAsset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).ParamsByAsset(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Query_ParamsByAsset_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).ParamsByAsset(ctx, req.(*QueryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Query_ParamsKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).ParamsKeys(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Query_ParamsKeys_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).ParamsKeys(ctx, req.(*QueryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Query_ParamsByKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).ParamsByKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Query_ParamsByKey_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).ParamsByKey(ctx, req.(*QueryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Query_RegistrationOptionsByKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryServer).RegistrationOptionsByKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Query_RegistrationOptionsByKey_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryServer).RegistrationOptionsByKey(ctx, req.(*QueryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Query_Resolve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryRequest)
if err := dec(in); err != nil {
@ -179,6 +354,26 @@ var Query_ServiceDesc = grpc.ServiceDesc{
MethodName: "Params",
Handler: _Query_Params_Handler,
},
{
MethodName: "ParamsAssets",
Handler: _Query_ParamsAssets_Handler,
},
{
MethodName: "ParamsByAsset",
Handler: _Query_ParamsByAsset_Handler,
},
{
MethodName: "ParamsKeys",
Handler: _Query_ParamsKeys_Handler,
},
{
MethodName: "ParamsByKey",
Handler: _Query_ParamsByKey_Handler,
},
{
MethodName: "RegistrationOptionsByKey",
Handler: _Query_RegistrationOptionsByKey_Handler,
},
{
MethodName: "Resolve",
Handler: _Query_Resolve_Handler,

File diff suppressed because it is too large Load Diff

View File

@ -611,6 +611,7 @@ func NewChainApp(
appCodec,
sdkruntime.NewKVStoreService(keys[didtypes.StoreKey]),
app.AccountKeeper,
app.NFTKeeper,
app.StakingKeeper,
logger,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
@ -872,7 +873,7 @@ func NewChainApp(
app.GetSubspace(packetforwardtypes.ModuleName),
),
did.NewAppModule(appCodec, app.DidKeeper),
did.NewAppModule(appCodec, app.DidKeeper, app.NFTKeeper),
)
// BasicModuleManager defines the module BasicManager is in charge of setting up basic,

View File

@ -4,12 +4,9 @@
package main
import (
"errors"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/vfs/wasm"
"github.com/donseba/go-htmx"
"github.com/onsonr/sonr/internal/db"
)
@ -17,102 +14,27 @@ var dwn *DWN
type DWN struct {
*echo.Echo
DB *db.DB
Htmx *htmx.HTMX
DB *db.DB
}
func main() {
dwn = initRails()
dwn.GET("/", htmxHandler)
dwn.POST("/accounts", accountsHandler)
wasm.Serve(dwn.Echo)
}
// initRails initializes the Rails application
func initRails() *DWN {
// Open the database
cnfg := db.New()
db, err := cnfg.Open()
e := echo.New()
db, err := db.New()
if err != nil {
panic(err.Error())
}
db.ServeEcho(e.Group("/dwn"))
// Initialize the htmx handler
return &DWN{
Echo: echo.New(),
Echo: e,
DB: db,
Htmx: htmx.New(),
}
}
func accountsHandler(e echo.Context) error {
switch e.Request().Method {
case "GET":
case "PUT":
// dwn.DB.AddAccount(e.Param("account"), e.Param("address"))
default:
e.Error(errors.New("Method not allowed"))
}
return e.JSON(200, "OK")
}
func htmxHandler(e echo.Context) error {
// initiate a new htmx handler
h := dwn.Htmx.NewHandler(e.Response(), e.Request())
// check if the request is a htmx request
if h.IsHxRequest() {
// do something
}
// check if the request is boosted
if h.IsHxBoosted() {
// do something
}
// check if the request is a history restore request
if h.IsHxHistoryRestoreRequest() {
// do something
}
// check if the request is a prompt request
if h.RenderPartial() {
// do something
}
// set the headers for the response, see docs for more options
h.PushURL("http://push.url")
h.ReTarget("#ReTarged")
// write the output like you normally do.
// check the inspector tool in the browser to see that the headers are set.
_, err := h.Write([]byte("OK"))
return err
}
//
// func credentialsHandler(e echo.Context) error {
// switch e.Request().Method {
// case "GET":
// dwn.DB.GetCredentials(e.Param("account"))
// case "PUT":
// dwn.DB.AddCredentials(e.Param("account"), e.Param("address"))
// default:
// e.Error(errors.New("Method not allowed"))
// }
// return e.JSON(200, "OK")
// }
//
// func keysharesHandler(e echo.Context) error {
// switch e.Request().Method {
// case "GET":
// dwn.DB.GetKeyshares(e.Param("account"))
// case "PUT":
// dwn.DB.AddKeyshares(e.Param("account"), e.Param("address"))
// default:
// e.Error(errors.New("Method not allowed"))
// }
// return e.JSON(200, "OK")
// }

View File

@ -6,6 +6,8 @@ import (
"github.com/labstack/echo/v4"
"github.com/syumai/workers"
"github.com/onsonr/sonr/internal/db"
"github.com/onsonr/sonr/internal/gui/views"
"github.com/onsonr/sonr/internal/mdw"
"github.com/onsonr/sonr/internal/svc"
@ -19,13 +21,15 @@ func main() {
e.Use(mdw.UseSession)
// Setup routes
registerRoutes(e)
registerFrontend(e)
registerOpenID(e.Group("/authorize"))
registerVault(e.Group("/vault"))
// Serve Worker
workers.Serve(e)
}
func registerRoutes(e *echo.Echo) {
func registerFrontend(e *echo.Echo) {
// Add Public Pages
e.GET("/", views.HomeView)
e.GET("/login", views.LoginView)
@ -33,11 +37,25 @@ func registerRoutes(e *echo.Echo) {
e.GET("/register", views.RegisterView)
e.POST("/register/:subject", svc.HandleCredentialCreation)
e.POST("/register/:subject/check", svc.CheckSubjectIsValid)
// Add Authenticated Pages
e.GET("/authorize", views.AuthorizeView)
e.GET("/authorize/discovery", svc.GetDiscovery)
e.GET("/authorize/jwks", svc.GetJWKS)
e.GET("/authorize/token", svc.GetToken)
e.POST("/authorize/:origin/grant/:subject", svc.GrantAuthorization)
e.GET("/profile", views.ProfileView)
}
func registerOpenID(g *echo.Group) {
// Add Authenticated Pages
g.Use(mdw.MacaroonMiddleware("test", "test"))
g.GET("/", views.AuthorizeView)
g.GET("/discovery", svc.GetDiscovery)
g.GET("/jwks", svc.GetJWKS)
g.GET("/token", svc.GetToken)
g.POST("/:origin/grant/:subject", svc.GrantAuthorization)
}
func registerVault(g *echo.Group) {
// Add Authenticated Pages
g.Use(mdw.MacaroonMiddleware("test", "test"))
vault, err := db.New(db.WitDir("vault"))
if err != nil {
// panic(err)
}
vault.ServeEcho(g)
}

View File

@ -13,8 +13,7 @@ import (
func main() {
rootCmd := NewRootCmd()
rootCmd.AddCommand(tui.NewTUIDashboardCmd())
// rootCmd.AddCommand(tui.NewExplorerCmd())
tui.AddTUICmds(rootCmd)
if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
log.NewLogger(rootCmd.OutOrStderr()).Error("failure when running app", "err", err)

View File

@ -15,8 +15,7 @@
"CHAIN_ID": "sonr-testnet-1",
"DENOM": "usnr",
"KEYRING": "test",
"MONIKER": "florence",
"MIN_GAS_PRICES": "0.0001usnr"
"MONIKER": "florence"
},
"shell": {
"scripts": {
@ -39,6 +38,7 @@
"make proto-gen"
],
"gen:pkl": [
"go run github.com/apple/pkl-go/cmd/pkl-gen-go ./pkl/oidc.pkl",
"go run github.com/apple/pkl-go/cmd/pkl-gen-go ./pkl/orm.pkl",
"go run github.com/apple/pkl-go/cmd/pkl-gen-go ./pkl/txns.pkl"
],

15
go.mod
View File

@ -2,8 +2,6 @@ module github.com/onsonr/sonr
go 1.22.5
toolchain go1.23.0
// overrides
replace (
cosmossdk.io/core => cosmossdk.io/core v0.11.0
@ -65,20 +63,20 @@ require (
github.com/cosmos/ibc-go/modules/capability v1.0.0
github.com/cosmos/ibc-go/v8 v8.2.0
github.com/donseba/go-htmx v1.10.0
github.com/ethereum/go-ethereum v1.14.6
github.com/go-webauthn/webauthn v0.10.2
github.com/golang/protobuf v1.5.4
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/hack-pad/go-indexeddb v0.3.2
github.com/ipfs/boxo v0.21.0
github.com/ipfs/kubo v0.29.0
github.com/joho/godotenv v1.5.1
github.com/labstack/echo-jwt/v4 v4.2.0
github.com/labstack/echo/v4 v4.10.2
github.com/mr-tron/base58 v1.2.0
github.com/ncruces/go-sqlite3 v0.18.2
github.com/ncruces/go-sqlite3/gormlite v0.18.0
github.com/nlepage/go-js-promise v1.0.0
github.com/onsonr/crypto v1.28.0
github.com/onsonr/crypto v1.29.0
github.com/segmentio/ksuid v1.0.4
github.com/spf13/cast v1.6.0
github.com/spf13/cobra v1.8.0
@ -97,6 +95,7 @@ require (
gopkg.in/macaroon.v2 v2.1.0
gorm.io/gorm v1.25.11
lukechampine.com/adiantum v1.1.1
lukechampine.com/blake3 v1.3.0
)
require (
@ -110,7 +109,6 @@ require (
github.com/99designs/keyring v1.2.1 // indirect
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go v1.44.224 // indirect
@ -176,7 +174,6 @@ require (
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@ -192,10 +189,12 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.2 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/hack-pad/safejs v0.1.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-getter v1.7.5 // indirect
@ -211,6 +210,7 @@ require (
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/huandu/skiplist v1.2.0 // indirect
github.com/iancoleman/orderedmap v0.3.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
@ -362,7 +362,6 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
nhooyr.io/websocket v1.8.10 // indirect
pgregory.net/rapid v1.1.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect

16
go.sum
View File

@ -1129,6 +1129,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87K
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/ethereum/go-ethereum v1.14.6 h1:ZTxnErSopkDyxdvB8zW/KcK+/AVrdil/TzoWXVKaaC8=
github.com/ethereum/go-ethereum v1.14.6/go.mod h1:hglUZo/5pVIYXNyYjWzsAUDpT/zI+WbWo/Nih7ot+G0=
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A=
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@ -1228,8 +1230,6 @@ github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
@ -1403,6 +1403,10 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g=
github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -1459,6 +1463,8 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU=
github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c=
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
@ -1645,8 +1651,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c=
github.com/labstack/echo-jwt/v4 v4.2.0/go.mod h1:MA2RqdXdEn4/uEglx0HcUOgQSyBaTh5JcaHIan3biwU=
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=
@ -1857,8 +1861,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/onsonr/crypto v1.28.0 h1:IZnWipcqtehohvau065NJSEgS2a2iMeTv2S0KlQ22ns=
github.com/onsonr/crypto v1.28.0/go.mod h1:2Ew7gRDlysE9+UXN904nUaJ9bqn6OlpKFMtk/LuTYoc=
github.com/onsonr/crypto v1.29.0 h1:ontCN/XNNmpWv23N8VB6vsirLXcjxZaA67lLX9RNj0c=
github.com/onsonr/crypto v1.29.0/go.mod h1:NSfeCO6XoyQeSDEp6Jy42UGG5047GvzG6lW9lRnjrR0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=

View File

@ -12,11 +12,11 @@ func createInitialTables(db *gorm.DB) (*DB, error) {
err := db.AutoMigrate(
&orm.Account{},
&orm.Asset{},
&orm.Keyshare{},
&orm.Credential{},
&orm.Keyshare{},
&orm.Permission{},
&orm.Profile{},
&orm.Property{},
&orm.Permission{},
)
if err != nil {
return nil, fmt.Errorf("failed to create table: %w", err)
@ -35,12 +35,75 @@ func (db *DB) AddAccount(account *orm.Account) error {
return nil
}
// AddKeyshare adds a new keyshare to the database
func (db *DB) AddKeyshare(keyshare *orm.Keyshare) error {
tx := db.Create(keyshare)
// GetAccount gets an account from the database
func (db *DB) GetAccount(account *orm.Account) error {
tx := db.First(account)
if tx.Error != nil {
return fmt.Errorf("failed to get account: %w", tx.Error)
}
return nil
}
// UpdateAccount updates an existing account in the database
func (db *DB) UpdateAccount(account *orm.Account) error {
tx := db.Save(account)
if tx.Error != nil {
return fmt.Errorf("failed to update account: %w", tx.Error)
}
return nil
}
// DeleteAccount deletes an existing account from the database
func (db *DB) DeleteAccount(account *orm.Account) error {
tx := db.Delete(account)
if tx.Error != nil {
return fmt.Errorf("failed to delete account: %w", tx.Error)
}
return nil
}
// AddAsset adds a new asset to the database
func (db *DB) AddAsset(asset *orm.Asset) error {
tx := db.Create(asset)
if tx.Error != nil {
return fmt.Errorf("failed to add keyshare: %w", tx.Error)
return fmt.Errorf("failed to add asset: %w", tx.Error)
}
return nil
}
// GetAsset gets an asset from the database
func (db *DB) GetAsset(asset *orm.Asset) error {
tx := db.First(asset)
if tx.Error != nil {
return fmt.Errorf("failed to get asset: %w", tx.Error)
}
return nil
}
// UpdateAsset updates an existing asset in the database
func (db *DB) UpdateAsset(asset *orm.Asset) error {
tx := db.Save(asset)
if tx.Error != nil {
return fmt.Errorf("failed to update asset: %w", tx.Error)
}
return nil
}
// DeleteAsset deletes an existing asset from the database
func (db *DB) DeleteAsset(asset *orm.Asset) error {
tx := db.Delete(asset)
if tx.Error != nil {
return fmt.Errorf("failed to delete asset: %w", tx.Error)
}
return nil
@ -57,6 +120,127 @@ func (db *DB) AddCredential(credential *orm.Credential) error {
return nil
}
// GetCredential gets an credential from the database
func (db *DB) GetCredential(credential *orm.Credential) error {
tx := db.First(credential)
if tx.Error != nil {
return fmt.Errorf("failed to get credential: %w", tx.Error)
}
return nil
}
// UpdateCredential updates an existing credential in the database
func (db *DB) UpdateCredential(credential *orm.Credential) error {
tx := db.Save(credential)
if tx.Error != nil {
return fmt.Errorf("failed to update credential: %w", tx.Error)
}
return nil
}
// DeleteCredential deletes an existing credential from the database
func (db *DB) DeleteCredential(credential *orm.Credential) error {
tx := db.Delete(credential)
if tx.Error != nil {
return fmt.Errorf("failed to delete credential: %w", tx.Error)
}
return nil
}
// AddKeyshare adds a new keyshare to the database
func (db *DB) AddKeyshare(keyshare *orm.Keyshare) error {
tx := db.Create(keyshare)
if tx.Error != nil {
return fmt.Errorf("failed to add keyshare: %w", tx.Error)
}
return nil
}
// GetKeyshare gets an keyshare from the database
func (db *DB) GetKeyshare(keyshare *orm.Keyshare) error {
tx := db.First(keyshare)
if tx.Error != nil {
return fmt.Errorf("failed to get keyshare: %w", tx.Error)
}
return nil
}
// UpdateKeyshare updates an existing keyshare in the database
func (db *DB) UpdateKeyshare(keyshare *orm.Keyshare) error {
tx := db.Save(keyshare)
if tx.Error != nil {
return fmt.Errorf("failed to update keyshare: %w", tx.Error)
}
return nil
}
// DeleteKeyshare deletes an existing keyshare from the database
func (db *DB) DeleteKeyshare(keyshare *orm.Keyshare) error {
tx := db.Delete(keyshare)
if tx.Error != nil {
return fmt.Errorf("failed to delete keyshare: %w", tx.Error)
}
return nil
}
// AddPermission adds a new permission to the database
func (db *DB) AddPermission(permission *orm.Permission) error {
tx := db.Create(permission)
if tx.Error != nil {
return fmt.Errorf("failed to add permission: %w", tx.Error)
}
return nil
}
// GetPermission gets an permission from the database
func (db *DB) GetPermission(permission *orm.Permission) error {
tx := db.First(permission)
if tx.Error != nil {
return fmt.Errorf("failed to get permission: %w", tx.Error)
}
return nil
}
// UpdatePermission updates an existing permission in the database
func (db *DB) UpdatePermission(permission *orm.Permission) error {
tx := db.Save(permission)
if tx.Error != nil {
return fmt.Errorf("failed to update permission: %w", tx.Error)
}
return nil
}
// DeletePermission deletes an existing permission from the database
func (db *DB) DeletePermission(permission *orm.Permission) error {
tx := db.Delete(permission)
if tx.Error != nil {
return fmt.Errorf("failed to delete permission: %w", tx.Error)
}
return nil
}
// AddProfile adds a new profile to the database
func (db *DB) AddProfile(profile *orm.Profile) error {
tx := db.Create(profile)
@ -68,6 +252,39 @@ func (db *DB) AddProfile(profile *orm.Profile) error {
return nil
}
// GetProfile gets an profile from the database
func (db *DB) GetProfile(profile *orm.Profile) error {
tx := db.First(profile)
if tx.Error != nil {
return fmt.Errorf("failed to get profile: %w", tx.Error)
}
return nil
}
// UpdateProfile updates an existing profile in the database
func (db *DB) UpdateProfile(profile *orm.Profile) error {
tx := db.Save(profile)
if tx.Error != nil {
return fmt.Errorf("failed to update profile: %w", tx.Error)
}
return nil
}
// DeleteProfile deletes an existing profile from the database
func (db *DB) DeleteProfile(profile *orm.Profile) error {
tx := db.Delete(profile)
if tx.Error != nil {
return fmt.Errorf("failed to delete profile: %w", tx.Error)
}
return nil
}
// AddProperty adds a new property to the database
func (db *DB) AddProperty(property *orm.Property) error {
tx := db.Create(property)
@ -79,12 +296,34 @@ func (db *DB) AddProperty(property *orm.Property) error {
return nil
}
// AddPermission adds a new permission to the database
func (db *DB) AddPermission(permission *orm.Permission) error {
tx := db.Create(permission)
// GetProperty gets an property from the database
func (db *DB) GetProperty(property *orm.Property) error {
tx := db.First(property)
if tx.Error != nil {
return fmt.Errorf("failed to add permission: %w", tx.Error)
return fmt.Errorf("failed to get property: %w", tx.Error)
}
return nil
}
// UpdateProperty updates an existing property in the database
func (db *DB) UpdateProperty(property *orm.Property) error {
tx := db.Save(property)
if tx.Error != nil {
return fmt.Errorf("failed to update property: %w", tx.Error)
}
return nil
}
// DeleteProperty deletes an existing property from the database
func (db *DB) DeleteProperty(property *orm.Property) error {
tx := db.Delete(property)
if tx.Error != nil {
return fmt.Errorf("failed to delete property: %w", tx.Error)
}
return nil

View File

@ -1,17 +1,54 @@
package db
import "gorm.io/gorm"
import (
"crypto/rand"
"github.com/ncruces/go-sqlite3/gormlite"
"golang.org/x/crypto/argon2"
"gorm.io/gorm"
"lukechampine.com/adiantum/hbsh"
"lukechampine.com/adiantum/hpolyc"
)
type DB struct {
*gorm.DB
}
func New(opts ...DBOption) *DBConfig {
func New(opts ...DBOption) (*DB, error) {
config := &DBConfig{
fileName: "vault.db",
}
for _, opt := range opts {
opt(config)
}
return config
gormdb, err := gorm.Open(gormlite.Open(config.ConnectionString()))
if err != nil {
return nil, err
}
db, err := createInitialTables(gormdb)
if err != nil {
return nil, err
}
return db, nil
}
// HBSH creates an HBSH cipher given a key.
func (c *DB) HBSH(key []byte) *hbsh.HBSH {
if len(key) != 32 {
// Key is not appropriate, return nil.
return nil
}
return hpolyc.New(key)
}
// KDF gets a key from a secret.
func (c *DB) KDF(secret string) []byte {
if secret == "" {
// No secret is given, generate a random key.
key := make([]byte, 32)
n, _ := rand.Read(key)
return key[:n]
}
// Hash the secret with a KDF.
return argon2.IDKey([]byte(secret), []byte("hpolyc"), 3, 64*1024, 4, 32)
}

195
internal/db/handlers.go Normal file
View File

@ -0,0 +1,195 @@
package db
import (
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/db/orm"
)
func (db *DB) ServeEcho(e *echo.Group) {
e.GET("/accounts", db.HandleAccount)
e.GET("/assets", db.HandleAsset)
e.GET("/credentials", db.HandleCredential)
e.GET("/keyshares", db.HandleKeyshare)
e.GET("/permissions", db.HandlePermission)
e.GET("/profiles", db.HandleProfile)
e.GET("/properties", db.HandleProperty)
}
func (db *DB) HandleAccount(c echo.Context) error {
data := new(orm.Account)
if err := c.Bind(data); err != nil {
return err
}
// Check the method for GET, POST, PUT, DELETE
switch c.Request().Method {
case echo.POST:
if err := db.AddAccount(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdateAccount(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeleteAccount(data); err != nil {
return err
}
return c.JSON(200, nil)
}
return c.JSON(200, data)
}
func (db *DB) HandleAsset(c echo.Context) error {
data := new(orm.Asset)
if err := c.Bind(data); err != nil {
return err
}
switch c.Request().Method {
case echo.POST:
if err := db.AddAsset(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdateAsset(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeleteAsset(data); err != nil {
return err
}
return c.JSON(200, "OK")
}
return c.JSON(200, data)
}
func (db *DB) HandleCredential(c echo.Context) error {
data := new(orm.Credential)
if err := c.Bind(data); err != nil {
return err
}
switch c.Request().Method {
case echo.POST:
if err := db.AddCredential(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdateCredential(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeleteCredential(data); err != nil {
return err
}
}
return c.JSON(200, data)
}
func (db *DB) HandleKeyshare(c echo.Context) error {
data := new(orm.Keyshare)
if err := c.Bind(data); err != nil {
return err
}
switch c.Request().Method {
case echo.POST:
if err := db.AddKeyshare(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdateKeyshare(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeleteKeyshare(data); err != nil {
return err
}
}
return c.JSON(200, data)
}
func (db *DB) HandlePermission(c echo.Context) error {
data := new(orm.Permission)
if err := c.Bind(data); err != nil {
return err
}
switch c.Request().Method {
case echo.POST:
if err := db.AddPermission(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdatePermission(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeletePermission(data); err != nil {
return err
}
}
return c.JSON(200, data)
}
func (db *DB) HandleProfile(c echo.Context) error {
data := new(orm.Profile)
if err := c.Bind(data); err != nil {
return err
}
switch c.Request().Method {
case echo.POST:
if err := db.AddProfile(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdateProfile(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeleteProfile(data); err != nil {
return err
}
}
return c.JSON(200, data)
}
func (db *DB) HandleProperty(c echo.Context) error {
data := new(orm.Property)
if err := c.Bind(data); err != nil {
return err
}
switch c.Request().Method {
case echo.POST:
if err := db.AddProperty(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.PUT:
if err := db.UpdateProperty(data); err != nil {
return err
}
return c.JSON(200, "OK")
case echo.DELETE:
if err := db.DeleteProperty(data); err != nil {
return err
}
}
return c.JSON(200, data)
}

131
internal/db/idb/idb.go Normal file
View File

@ -0,0 +1,131 @@
//go:build js && wasm
// +build js,wasm
package idb
import (
"context"
"encoding/json"
"errors"
"syscall/js"
"github.com/hack-pad/go-indexeddb/idb"
)
// Model is an interface that must be implemented by types used with Table
type Model interface {
Table() string
}
// Table is a generic wrapper around IDB for easier database operations on a specific table
type Table[T Model] struct {
db *idb.Database
dbName string
keyPath string
}
// NewTable creates a new Table instance
func NewTable[T Model](dbName string, version uint, keyPath string) (*Table[T], error) {
ctx := context.Background()
factory := idb.Global()
var model T
tableName := model.Table()
openRequest, err := factory.Open(ctx, dbName, version, func(db *idb.Database, oldVersion, newVersion uint) error {
_, err := db.CreateObjectStore(tableName, idb.ObjectStoreOptions{
KeyPath: js.ValueOf(keyPath),
})
return err
})
if err != nil {
return nil, err
}
db, err := openRequest.Await(ctx)
if err != nil {
return nil, err
}
return &Table[T]{
db: db,
dbName: dbName,
keyPath: keyPath,
}, nil
}
// Insert adds a new record to the table
func (t *Table[T]) Insert(data T) error {
tx, err := t.db.Transaction(idb.TransactionReadWrite, data.Table())
if err != nil {
return err
}
defer tx.Commit()
objectStore, err := tx.ObjectStore(data.Table())
if err != nil {
return err
}
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
_, err = objectStore.Add(js.ValueOf(string(jsonData)))
return err
}
// Query retrieves a record from the table based on a key
func (t *Table[T]) Query(key interface{}) (T, error) {
var result T
tx, err := t.db.Transaction(idb.TransactionReadOnly, result.Table())
if err != nil {
return result, err
}
defer tx.Commit()
objectStore, err := tx.ObjectStore(result.Table())
if err != nil {
return result, err
}
request, err := objectStore.Get(js.ValueOf(key))
if err != nil {
return result, err
}
value, err := request.Await(context.Background())
if err != nil {
return result, err
}
if value.IsUndefined() || value.IsNull() {
return result, errors.New("record not found")
}
err = json.Unmarshal([]byte(value.String()), &result)
return result, err
}
// Delete removes a record from the table based on a key
func (t *Table[T]) Delete(key interface{}) error {
var model T
tx, err := t.db.Transaction(idb.TransactionReadWrite, model.Table())
if err != nil {
return err
}
defer tx.Commit()
objectStore, err := tx.ObjectStore(model.Table())
if err != nil {
return err
}
_, err = objectStore.Delete(js.ValueOf(key))
return err
}
// Close closes the database connection
func (t *Table[T]) Close() error {
return t.db.Close()
}

View File

@ -1,20 +1,13 @@
package db
import (
"crypto/rand"
"github.com/ncruces/go-sqlite3/gormlite"
"golang.org/x/crypto/argon2"
"gorm.io/gorm"
"lukechampine.com/adiantum/hbsh"
"lukechampine.com/adiantum/hpolyc"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/onsonr/sonr/internal/db/orm"
)
type DBOption func(config *DBConfig)
func WithDir(dir string) DBOption {
func WitDir(dir string) DBOption {
return func(config *DBConfig) {
config.Dir = dir
}
@ -30,41 +23,18 @@ type DBConfig struct {
Dir string
SecretKey string
fileName string
fileName string
initialAccounts []*orm.Account
initialAssets []*orm.Asset
initialCredentials []*orm.Credential
initialKeyshares []*orm.Keyshare
initialPermissions []*orm.Permission
initialProfiles []*orm.Profile
initialProperties []*orm.Property
}
func (config *DBConfig) ConnectionString() string {
connStr := "file:"
connStr += config.Dir + "/" + config.fileName
connStr += config.fileName
return connStr
}
// GormDialector creates a gorm dialector for the database.
func (config *DBConfig) Open() (*DB, error) {
db, err := gorm.Open(gormlite.Open(config.ConnectionString()))
if err != nil {
return nil, err
}
return createInitialTables(db)
}
// HBSH creates an HBSH cipher given a key.
func (c *DBConfig) HBSH(key []byte) *hbsh.HBSH {
if len(key) != 32 {
// Key is not appropriate, return nil.
return nil
}
return hpolyc.New(key)
}
// KDF gets a key from a secret.
func (c *DBConfig) KDF(secret string) []byte {
if secret == "" {
// No secret is given, generate a random key.
key := make([]byte, 32)
n, _ := rand.Read(key)
return key[:n]
}
// Hash the secret with a KDF.
return argon2.IDKey([]byte(secret), []byte("hpolyc"), 3, 64*1024, 4, 32)
}

View File

@ -4,17 +4,9 @@ package orm
type Keyshare struct {
Id uint `pkl:"id" gorm:"primaryKey,autoIncrement" json:"id,omitempty" query:"id"`
Metadata string `pkl:"metadata" json:"metadata,omitempty" param:"metadata"`
Payloads string `pkl:"payloads" json:"payloads,omitempty" param:"payloads"`
Protocol string `pkl:"protocol" json:"protocol,omitempty" param:"protocol"`
PublicKey string `pkl:"publicKey" json:"publicKey,omitempty" param:"publicKey"`
Data string `pkl:"data" json:"data,omitempty" param:"data"`
Role int `pkl:"role" json:"role,omitempty" param:"role"`
Version int `pkl:"version" json:"version,omitempty" param:"version"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty" param:"createdAt"`
}

View File

@ -1,16 +0,0 @@
// Code generated from Pkl module `orm`. DO NOT EDIT.
package orm
type PublicKey struct {
Id uint `pkl:"id" gorm:"primaryKey,autoIncrement" json:"id,omitempty" query:"id"`
Role int `pkl:"role" json:"role,omitempty" param:"role"`
Algorithm int `pkl:"algorithm" json:"algorithm,omitempty" param:"algorithm"`
Encoding int `pkl:"encoding" json:"encoding,omitempty" param:"encoding"`
Jwk string `pkl:"jwk" json:"jwk,omitempty" param:"jwk"`
CreatedAt *string `pkl:"createdAt" json:"createdAt,omitempty" param:"createdAt"`
}

View File

@ -12,7 +12,5 @@ func init() {
pkl.RegisterMapping("orm#Profile", Profile{})
pkl.RegisterMapping("orm#Property", Property{})
pkl.RegisterMapping("orm#Keyshare", Keyshare{})
pkl.RegisterMapping("orm#PublicKey", PublicKey{})
pkl.RegisterMapping("orm#Permission", Permission{})
pkl.RegisterMapping("orm#DiscoveryDocument", DiscoveryDocument{})
}

29
internal/db/orm/orm.go Normal file
View File

@ -0,0 +1,29 @@
package orm
func (a *Account) Table() string {
return "accounts"
}
func (a *Asset) Table() string {
return "assets"
}
func (a *Credential) Table() string {
return "credentials"
}
func (a *Keyshare) Table() string {
return "keyshares"
}
func (a *Permission) Table() string {
return "permissions"
}
func (a *Profile) Table() string {
return "profiles"
}
func (a *Property) Table() string {
return "properties"
}

View File

@ -0,0 +1,59 @@
package elements
templ PoweredBySonr() {
<div class="mx-auto w-fit pt-8">
<div class="pt-2 pb-3 pl-4 pr-4 gap-x-3 text-sm text-gray-500 border-t border-neutral-200/70">
<div
x-data="{
hoverCardHovered: false,
hoverCardDelay: 600,
hoverCardLeaveDelay: 500,
hoverCardTimout: null,
hoverCardLeaveTimeout: null,
hoverCardEnter () {
clearTimeout(this.hoverCardLeaveTimeout);
if(this.hoverCardHovered) return;
clearTimeout(this.hoverCardTimout);
this.hoverCardTimout = setTimeout(() => {
this.hoverCardHovered = true;
}, this.hoverCardDelay);
},
hoverCardLeave () {
clearTimeout(this.hoverCardTimout);
if(!this.hoverCardHovered) return;
clearTimeout(this.hoverCardLeaveTimeout);
this.hoverCardLeaveTimeout = setTimeout(() => {
this.hoverCardHovered = false;
}, this.hoverCardLeaveDelay);
}
}"
class="relative"
@mouseover="hoverCardEnter()"
@mouseleave="hoverCardLeave()"
>
<span class="bg-transparent text-gray-500 border border-neutral-300 flex items-center text-xs font-semibold px-2.5 py-0.5 rounded-full">
<span class="mr-1">Powered by </span>
<svg class="w-3 h-3" width="164" height="164" viewBox="0 0 164 164" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M71.8077 133.231C74.5054 135.928 78.1636 137.443 81.978 137.443C85.7924 137.443 89.4506 135.928 92.1483 133.231L133.219 92.1638C135.909 89.4654 137.42 85.8102 137.42 81.9998C137.42 78.1895 135.909 74.5345 133.219 71.8361L112.886 51.5272L131.665 32.7499L152.031 53.1143C159.696 60.7963 164 71.2046 164 82.0559C164 92.9072 159.696 103.315 152.031 110.997L110.95 152.065C107.154 155.869 102.642 158.883 97.6739 160.931C92.7059 162.98 87.3809 164.023 82.0071 164L82.0052 164C76.622 164.019 71.2886 162.969 66.3145 160.91C61.3405 158.852 56.8247 155.826 53.0294 152.009L53.0289 152.008L48.7187 147.699L67.4974 128.921L71.8077 133.231Z" fill="currentColor"></path>
<path d="M110.95 11.9912L115.26 16.3011L96.481 35.0785L92.1707 30.7685C89.4731 28.072 85.8148 26.5572 82.0004 26.5572C78.186 26.5572 74.5277 28.072 71.8301 30.7685L30.7597 71.8359C29.4247 73.1706 28.3658 74.7552 27.6433 76.4991C26.9208 78.2431 26.549 80.1122 26.549 81.9999C26.549 83.8876 26.9208 85.7567 27.6433 87.5007C28.3658 89.2446 29.4247 90.8292 30.7597 92.1639L51.1256 112.528L32.3138 131.306L11.9923 110.941C8.19043 107.141 5.17433 102.629 3.1167 97.6635C1.05907 92.6976 0 87.3751 0 81.9999C0 76.6247 1.05907 71.3022 3.1167 66.3363C5.17433 61.3705 8.19021 56.8587 11.9921 53.0586L53.0625 11.9912C56.8629 8.18964 61.3751 5.17395 66.3413 3.11647C71.3075 1.05899 76.6304 0 82.006 0C87.3816 0 92.7045 1.05899 97.6707 3.11647C102.637 5.17395 107.149 8.18964 110.95 11.9912Z" fill="currentColor"></path>
<path d="M55.603 76.6744L76.6993 55.5798C79.6327 52.6465 84.3888 52.6465 87.3223 55.5797L108.419 76.6744C111.352 79.6077 111.352 84.3634 108.419 87.2966L87.3223 108.391C84.3888 111.325 79.6327 111.325 76.6993 108.391L55.603 87.2966C52.6696 84.3634 52.6696 79.6077 55.603 76.6744Z" fill="currentColor"></path>
</svg>
</span>
<div x-show="hoverCardHovered" class="absolute top-0 w-[365px] max-w-lg mt-5 z-30 -translate-x-1/2 translate-y-3 left-1/2" x-cloak>
<div x-show="hoverCardHovered" class="w-[full] h-auto bg-white space-x-3 p-5 flex items-start rounded-md shadow-sm border border-neutral-200/70" x-transition>
<img src="https://cdn.sonr.io/logo.svg" alt="sonr image" class="rounded-full w-14 h-14"/>
<div class="relative">
<p class="mb-1 text-sm text-gray-600">The creative platform for developers. Community, tools, products, and more</p>
<p class="flex items-center space-x-1 text-xs text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z"></path>
</svg>
<span>Joined June 2020</span>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
}

View File

@ -0,0 +1,37 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package elements
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func PoweredBySonr() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"mx-auto w-fit pt-8\"><div class=\"pt-2 pb-3 pl-4 pr-4 gap-x-3 text-sm text-gray-500 border-t border-neutral-200/70\"><div x-data=\"{ \n hoverCardHovered: false,\n hoverCardDelay: 600,\n hoverCardLeaveDelay: 500,\n hoverCardTimout: null,\n hoverCardLeaveTimeout: null,\n hoverCardEnter () {\n clearTimeout(this.hoverCardLeaveTimeout);\n if(this.hoverCardHovered) return;\n clearTimeout(this.hoverCardTimout);\n this.hoverCardTimout = setTimeout(() =&gt; {\n this.hoverCardHovered = true;\n }, this.hoverCardDelay);\n },\n hoverCardLeave () {\n clearTimeout(this.hoverCardTimout);\n if(!this.hoverCardHovered) return;\n clearTimeout(this.hoverCardLeaveTimeout);\n this.hoverCardLeaveTimeout = setTimeout(() =&gt; {\n this.hoverCardHovered = false;\n }, this.hoverCardLeaveDelay);\n }\n }\" class=\"relative\" @mouseover=\"hoverCardEnter()\" @mouseleave=\"hoverCardLeave()\"><span class=\"bg-transparent text-gray-500 border border-neutral-300 flex items-center text-xs font-semibold px-2.5 py-0.5 rounded-full\"><span class=\"mr-1\">Powered by </span> <svg class=\"w-3 h-3\" width=\"164\" height=\"164\" viewBox=\"0 0 164 164\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M71.8077 133.231C74.5054 135.928 78.1636 137.443 81.978 137.443C85.7924 137.443 89.4506 135.928 92.1483 133.231L133.219 92.1638C135.909 89.4654 137.42 85.8102 137.42 81.9998C137.42 78.1895 135.909 74.5345 133.219 71.8361L112.886 51.5272L131.665 32.7499L152.031 53.1143C159.696 60.7963 164 71.2046 164 82.0559C164 92.9072 159.696 103.315 152.031 110.997L110.95 152.065C107.154 155.869 102.642 158.883 97.6739 160.931C92.7059 162.98 87.3809 164.023 82.0071 164L82.0052 164C76.622 164.019 71.2886 162.969 66.3145 160.91C61.3405 158.852 56.8247 155.826 53.0294 152.009L53.0289 152.008L48.7187 147.699L67.4974 128.921L71.8077 133.231Z\" fill=\"currentColor\"></path> <path d=\"M110.95 11.9912L115.26 16.3011L96.481 35.0785L92.1707 30.7685C89.4731 28.072 85.8148 26.5572 82.0004 26.5572C78.186 26.5572 74.5277 28.072 71.8301 30.7685L30.7597 71.8359C29.4247 73.1706 28.3658 74.7552 27.6433 76.4991C26.9208 78.2431 26.549 80.1122 26.549 81.9999C26.549 83.8876 26.9208 85.7567 27.6433 87.5007C28.3658 89.2446 29.4247 90.8292 30.7597 92.1639L51.1256 112.528L32.3138 131.306L11.9923 110.941C8.19043 107.141 5.17433 102.629 3.1167 97.6635C1.05907 92.6976 0 87.3751 0 81.9999C0 76.6247 1.05907 71.3022 3.1167 66.3363C5.17433 61.3705 8.19021 56.8587 11.9921 53.0586L53.0625 11.9912C56.8629 8.18964 61.3751 5.17395 66.3413 3.11647C71.3075 1.05899 76.6304 0 82.006 0C87.3816 0 92.7045 1.05899 97.6707 3.11647C102.637 5.17395 107.149 8.18964 110.95 11.9912Z\" fill=\"currentColor\"></path> <path d=\"M55.603 76.6744L76.6993 55.5798C79.6327 52.6465 84.3888 52.6465 87.3223 55.5797L108.419 76.6744C111.352 79.6077 111.352 84.3634 108.419 87.2966L87.3223 108.391C84.3888 111.325 79.6327 111.325 76.6993 108.391L55.603 87.2966C52.6696 84.3634 52.6696 79.6077 55.603 76.6744Z\" fill=\"currentColor\"></path></svg></span><div x-show=\"hoverCardHovered\" class=\"absolute top-0 w-[365px] max-w-lg mt-5 z-30 -translate-x-1/2 translate-y-3 left-1/2\" x-cloak><div x-show=\"hoverCardHovered\" class=\"w-[full] h-auto bg-white space-x-3 p-5 flex items-start rounded-md shadow-sm border border-neutral-200/70\" x-transition><img src=\"https://cdn.sonr.io/logo.svg\" alt=\"sonr image\" class=\"rounded-full w-14 h-14\"><div class=\"relative\"><p class=\"mb-1 text-sm text-gray-600\">The creative platform for developers. Community, tools, products, and more</p><p class=\"flex items-center space-x-1 text-xs text-gray-400\"><svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z\"></path></svg> <span>Joined June 2020</span></p></div></div></div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -0,0 +1,35 @@
package elements
templ Breadcrumbs() {
<nav class="flex justify-between px-3.5 py-1 rounded-md pb-3">
<ol class="inline-flex items-center mb-3 space-x-1 text-xs text-neutral-500 [&_.active-breadcrumb]:text-neutral-600 [&_.active-breadcrumb]:font-medium sm:mb-0">
<li class="flex items-center h-full">
<a href="#_" class="py-1 hover:text-neutral-900">
@breadcrumbIcon()
</a>
</li>
<svg class="w-5 h-5 text-gray-400/70" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="none"><path d="M10 8.013l4 4-4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>
@breadcrumbItem("Policy", false)
<svg class="w-5 h-5 text-gray-400/70" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="none"><path d="M10 8.013l4 4-4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>
@breadcrumbItem("About You", true)
<svg class="w-5 h-5 text-gray-400/70" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="none"><path d="M10 8.013l4 4-4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>
@breadcrumbItem("Generate", false)
</ol>
</nav>
}
templ breadcrumbItem(title string, active bool) {
if (active) {
<li><a class="inline-flex items-center py-1 font-normal rounded cursor-default active-breadcrumb focus:outline-none">{ title }</a></li>
} else {
<li><a href="#_" class="inline-flex items-center py-1 font-normal hover:text-neutral-900 focus:outline-none">{ title }</a></li>
}
}
templ breadcrumbIcon() {
<svg class="w-4 w-4" viewBox="0 0 164 164" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M71.8077 133.231C74.5054 135.928 78.1636 137.443 81.978 137.443C85.7924 137.443 89.4506 135.928 92.1483 133.231L133.219 92.1638C135.909 89.4654 137.42 85.8102 137.42 81.9998C137.42 78.1895 135.909 74.5345 133.219 71.8361L112.886 51.5272L131.665 32.7499L152.031 53.1143C159.696 60.7963 164 71.2046 164 82.0559C164 92.9072 159.696 103.315 152.031 110.997L110.95 152.065C107.154 155.869 102.642 158.883 97.6739 160.931C92.7059 162.98 87.3809 164.023 82.0071 164L82.0052 164C76.622 164.019 71.2886 162.969 66.3145 160.91C61.3405 158.852 56.8247 155.826 53.0294 152.009L53.0289 152.008L48.7187 147.699L67.4974 128.921L71.8077 133.231Z" fill="currentColor"></path>
<path d="M110.95 11.9912L115.26 16.3011L96.481 35.0785L92.1707 30.7685C89.4731 28.072 85.8148 26.5572 82.0004 26.5572C78.186 26.5572 74.5277 28.072 71.8301 30.7685L30.7597 71.8359C29.4247 73.1706 28.3658 74.7552 27.6433 76.4991C26.9208 78.2431 26.549 80.1122 26.549 81.9999C26.549 83.8876 26.9208 85.7567 27.6433 87.5007C28.3658 89.2446 29.4247 90.8292 30.7597 92.1639L51.1256 112.528L32.3138 131.306L11.9923 110.941C8.19043 107.141 5.17433 102.629 3.1167 97.6635C1.05907 92.6976 0 87.3751 0 81.9999C0 76.6247 1.05907 71.3022 3.1167 66.3363C5.17433 61.3705 8.19021 56.8587 11.9921 53.0586L53.0625 11.9912C56.8629 8.18964 61.3751 5.17395 66.3413 3.11647C71.3075 1.05899 76.6304 0 82.006 0C87.3816 0 92.7045 1.05899 97.6707 3.11647C102.637 5.17395 107.149 8.18964 110.95 11.9912Z" fill="currentColor"></path>
<path d="M55.603 76.6744L76.6993 55.5798C79.6327 52.6465 84.3888 52.6465 87.3223 55.5797L108.419 76.6744C111.352 79.6077 111.352 84.3634 108.419 87.2966L87.3223 108.391C84.3888 111.325 79.6327 111.325 76.6993 108.391L55.603 87.2966C52.6696 84.3634 52.6696 79.6077 55.603 76.6744Z" fill="currentColor"></path>
</svg>
}

View File

@ -0,0 +1,154 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package elements
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func Breadcrumbs() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav class=\"flex justify-between px-3.5 py-1 rounded-md pb-3\"><ol class=\"inline-flex items-center mb-3 space-x-1 text-xs text-neutral-500 [&amp;_.active-breadcrumb]:text-neutral-600 [&amp;_.active-breadcrumb]:font-medium sm:mb-0\"><li class=\"flex items-center h-full\"><a href=\"#_\" class=\"py-1 hover:text-neutral-900\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = breadcrumbIcon().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li><svg class=\"w-5 h-5 text-gray-400/70\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><g fill=\"none\" stroke=\"none\"><path d=\"M10 8.013l4 4-4 4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></g></svg>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = breadcrumbItem("Policy", false).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<svg class=\"w-5 h-5 text-gray-400/70\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><g fill=\"none\" stroke=\"none\"><path d=\"M10 8.013l4 4-4 4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></g></svg>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = breadcrumbItem("About You", true).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<svg class=\"w-5 h-5 text-gray-400/70\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><g fill=\"none\" stroke=\"none\"><path d=\"M10 8.013l4 4-4 4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></g></svg>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = breadcrumbItem("Generate", false).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ol></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
func breadcrumbItem(title string, active bool) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if active {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a class=\"inline-flex items-center py-1 font-normal rounded cursor-default active-breadcrumb focus:outline-none\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/breadcrumbs.templ`, Line: 23, Col: 126}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"#_\" class=\"inline-flex items-center py-1 font-normal hover:text-neutral-900 focus:outline-none\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/breadcrumbs.templ`, Line: 25, Col: 118}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return templ_7745c5c3_Err
})
}
func breadcrumbIcon() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<svg class=\"w-4 w-4\" viewBox=\"0 0 164 164\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M71.8077 133.231C74.5054 135.928 78.1636 137.443 81.978 137.443C85.7924 137.443 89.4506 135.928 92.1483 133.231L133.219 92.1638C135.909 89.4654 137.42 85.8102 137.42 81.9998C137.42 78.1895 135.909 74.5345 133.219 71.8361L112.886 51.5272L131.665 32.7499L152.031 53.1143C159.696 60.7963 164 71.2046 164 82.0559C164 92.9072 159.696 103.315 152.031 110.997L110.95 152.065C107.154 155.869 102.642 158.883 97.6739 160.931C92.7059 162.98 87.3809 164.023 82.0071 164L82.0052 164C76.622 164.019 71.2886 162.969 66.3145 160.91C61.3405 158.852 56.8247 155.826 53.0294 152.009L53.0289 152.008L48.7187 147.699L67.4974 128.921L71.8077 133.231Z\" fill=\"currentColor\"></path> <path d=\"M110.95 11.9912L115.26 16.3011L96.481 35.0785L92.1707 30.7685C89.4731 28.072 85.8148 26.5572 82.0004 26.5572C78.186 26.5572 74.5277 28.072 71.8301 30.7685L30.7597 71.8359C29.4247 73.1706 28.3658 74.7552 27.6433 76.4991C26.9208 78.2431 26.549 80.1122 26.549 81.9999C26.549 83.8876 26.9208 85.7567 27.6433 87.5007C28.3658 89.2446 29.4247 90.8292 30.7597 92.1639L51.1256 112.528L32.3138 131.306L11.9923 110.941C8.19043 107.141 5.17433 102.629 3.1167 97.6635C1.05907 92.6976 0 87.3751 0 81.9999C0 76.6247 1.05907 71.3022 3.1167 66.3363C5.17433 61.3705 8.19021 56.8587 11.9921 53.0586L53.0625 11.9912C56.8629 8.18964 61.3751 5.17395 66.3413 3.11647C71.3075 1.05899 76.6304 0 82.006 0C87.3816 0 92.7045 1.05899 97.6707 3.11647C102.637 5.17395 107.149 8.18964 110.95 11.9912Z\" fill=\"currentColor\"></path> <path d=\"M55.603 76.6744L76.6993 55.5798C79.6327 52.6465 84.3888 52.6465 87.3223 55.5797L108.419 76.6744C111.352 79.6077 111.352 84.3634 108.419 87.2966L87.3223 108.391C84.3888 111.325 79.6327 111.325 76.6993 108.391L55.603 87.2966C52.6696 84.3634 52.6696 79.6077 55.603 76.6744Z\" fill=\"currentColor\"></path></svg>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -1,7 +1,79 @@
package elements
func Button(variant Variant) templ.Component {
return renderButton(variant.Attributes())
type button struct {
variant Variant
hxGet string
hxPost string
hxTarget string
hxTrigger string
hxSwap string
}
type ButtonOpt func(button *button)
func PrimaryButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantPrimary
}
}
func InfoButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantInfo
}
}
func ErrorButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantError
}
}
func SuccessButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantSuccess
}
}
func WarningButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantWarning
}
}
func GET(action string, target string) ButtonOpt {
return func(button *button) {
button.hxGet = action
button.hxTarget = target
button.hxTrigger = "click"
button.hxSwap = "outerHTML"
}
}
func POST(action string, target string) ButtonOpt {
return func(button *button) {
button.hxPost = action
button.hxTarget = target
button.hxTrigger = "click"
button.hxSwap = "outerHTML"
}
}
func Button(opts ...ButtonOpt) templ.Component {
button := button{
variant: ButtonVariantDefault,
}
for _, opt := range opts {
opt(&button)
}
if button.hxGet != "" {
return renderHxGetButton(&button, button.variant.Attributes())
}
if button.hxPost != "" {
return renderHxPostButton(&button, button.variant.Attributes())
}
return renderButton(button.variant.Attributes())
}
templ renderButton(attrs templ.Attributes) {
@ -10,6 +82,18 @@ templ renderButton(attrs templ.Attributes) {
</button>
}
templ renderHxGetButton(c *button, attrs templ.Attributes) {
<button hx-get={ c.hxGet } hx-push-url="true" hx-target={ c.hxTarget } hx-trigger={ c.hxTrigger } hx-swap={ c.hxSwap } { attrs... }>
{ children... }
</button>
}
templ renderHxPostButton(c *button, attrs templ.Attributes) {
<button hx-post={ c.hxPost } hx-target={ c.hxTarget } hx-trigger={ c.hxTrigger } hx-swap={ c.hxSwap } { attrs... }>
{ children... }
</button>
}
type ButtonVariant int
const (

View File

@ -8,8 +8,80 @@ package elements
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func Button(variant Variant) templ.Component {
return renderButton(variant.Attributes())
type button struct {
variant Variant
hxGet string
hxPost string
hxTarget string
hxTrigger string
hxSwap string
}
type ButtonOpt func(button *button)
func PrimaryButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantPrimary
}
}
func InfoButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantInfo
}
}
func ErrorButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantError
}
}
func SuccessButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantSuccess
}
}
func WarningButtonStyle() ButtonOpt {
return func(button *button) {
button.variant = ButtonVariantWarning
}
}
func GET(action string, target string) ButtonOpt {
return func(button *button) {
button.hxGet = action
button.hxTarget = target
button.hxTrigger = "click"
button.hxSwap = "outerHTML"
}
}
func POST(action string, target string) ButtonOpt {
return func(button *button) {
button.hxPost = action
button.hxTarget = target
button.hxTrigger = "click"
button.hxSwap = "outerHTML"
}
}
func Button(opts ...ButtonOpt) templ.Component {
button := button{
variant: ButtonVariantDefault,
}
for _, opt := range opts {
opt(&button)
}
if button.hxGet != "" {
return renderHxGetButton(&button, button.variant.Attributes())
}
if button.hxPost != "" {
return renderHxPostButton(&button, button.variant.Attributes())
}
return renderButton(button.variant.Attributes())
}
func renderButton(attrs templ.Attributes) templ.Component {
@ -54,6 +126,194 @@ func renderButton(attrs templ.Attributes) templ.Component {
})
}
func renderHxGetButton(c *button, attrs templ.Attributes) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button hx-get=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxGet)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 86, Col: 25}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-push-url=\"true\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxTarget)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 86, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-trigger=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxTrigger)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 86, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxSwap)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 86, Col: 117}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, attrs)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var2.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
func renderHxPostButton(c *button, attrs templ.Attributes) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
if templ_7745c5c3_Var7 == nil {
templ_7745c5c3_Var7 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxPost)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 92, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-target=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxTarget)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 92, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-trigger=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxTrigger)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 92, Col: 79}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" hx-swap=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(c.hxSwap)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/button.templ`, Line: 92, Col: 100}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, attrs)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var7.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
type ButtonVariant int
const (

View File

@ -1,12 +1,12 @@
package elements
func Card(size Size) templ.Component {
return renderCard(size.CardAttributes())
func Card(id string, size Size) templ.Component {
return renderCard(id, size.CardAttributes())
}
templ renderCard(attrs templ.Attributes) {
<div { attrs... }>
<div class="container">
templ renderCard(id string, attrs templ.Attributes) {
<div id={ id } { attrs... }>
<div class="w-full h-full">
<div class="row">
<div class="col-md-12 space-3">
{ children... }
@ -15,3 +15,33 @@ templ renderCard(attrs templ.Attributes) {
</div>
</div>
}
templ ProfileCard() {
<div class="relative max-w-sm overflow-hidden bg-white border rounded-lg shadow-sm border-neutral-200/60">
<img src="https://cdn.devdojo.com/images/august2023/wallpaper.jpeg" class="relative z-20 object-cover w-full h-32"/>
<div class="absolute top-0 z-50 flex items-center w-full mt-2 translate-y-24 px-7 -translate-x-0">
<div class="w-20 h-20 p-1 bg-white rounded-full">
<img src="https://cdn.devdojo.com/images/august2023/adam.jpeg" class="w-full h-full rounded-full"/>
</div>
<a href="https://twitter.com/adamwathan" target="_blank" class="block mt-6 ml-2">
<h5 class="text-lg font-bold leading-none tracking-tight text-neutral-900">Adam Wathan</h5>
<small class="block mt-1 text-sm font-medium leading-none text-neutral-500">adamwathan</small>
</a>
<button class="absolute right-0 inline-flex items-center justify-center w-auto px-5 mt-6 text-sm font-medium transition-colors duration-100 rounded-full h-9 mr-7 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 bg-neutral-900 disabled:pointer-events-none hover:bg-neutral-800 text-neutral-100">
<span>Follow</span>
</button>
</div>
<div class="relative pb-6 p-7">
<p class="mt-12 mb-6 text-neutral-500 text-">Creator of @tailwindcss. Listener of Slayer. Austin 3:16. BTW, Pines UI is super cool!</p>
<div class="flex items-center justify-between pr-2 text-neutral-500">
<div class="relative flex w-16">
<img src="https://cdn.devdojo.com/images/august2023/caleb.jpeg" class="relative z-30 w-8 h-8 border-2 border-white rounded-full"/>
<img src="https://cdn.devdojo.com/images/august2023/taylor.jpeg" class="z-20 w-8 h-8 -translate-x-4 border-2 border-white rounded-full"/>
<img src="https://cdn.devdojo.com/images/august2023/adam.jpeg" class="z-10 w-8 h-8 border-2 border-white rounded-full -translate-x-7"/>
</div>
<a href="https://twitter.com/adamwathan/following" target="_blank" class="text-sm hover:underline"><strong class="text-neutral-800">673</strong> Following</a>
<a href="https://twitter.com/adamwathan/followers" target="_blank" class="text-sm hover:underline"><strong class="text-neutral-800">168.6K</strong> Followers</a>
</div>
</div>
</div>
}

View File

@ -8,11 +8,11 @@ package elements
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func Card(size Size) templ.Component {
return renderCard(size.CardAttributes())
func Card(id string, size Size) templ.Component {
return renderCard(id, size.CardAttributes())
}
func renderCard(attrs templ.Attributes) templ.Component {
func renderCard(id string, attrs templ.Attributes) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
@ -30,7 +30,20 @@ func renderCard(attrs templ.Attributes) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(id)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/card.templ`, Line: 8, Col: 13}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -38,7 +51,7 @@ func renderCard(attrs templ.Attributes) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("><div class=\"container\"><div class=\"row\"><div class=\"col-md-12 space-3\">")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("><div class=\"w-full h-full\"><div class=\"row\"><div class=\"col-md-12 space-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -54,4 +67,30 @@ func renderCard(attrs templ.Attributes) templ.Component {
})
}
func ProfileCard() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"relative max-w-sm overflow-hidden bg-white border rounded-lg shadow-sm border-neutral-200/60\"><img src=\"https://cdn.devdojo.com/images/august2023/wallpaper.jpeg\" class=\"relative z-20 object-cover w-full h-32\"><div class=\"absolute top-0 z-50 flex items-center w-full mt-2 translate-y-24 px-7 -translate-x-0\"><div class=\"w-20 h-20 p-1 bg-white rounded-full\"><img src=\"https://cdn.devdojo.com/images/august2023/adam.jpeg\" class=\"w-full h-full rounded-full\"></div><a href=\"https://twitter.com/adamwathan\" target=\"_blank\" class=\"block mt-6 ml-2\"><h5 class=\"text-lg font-bold leading-none tracking-tight text-neutral-900\">Adam Wathan</h5><small class=\"block mt-1 text-sm font-medium leading-none text-neutral-500\">adamwathan</small></a> <button class=\"absolute right-0 inline-flex items-center justify-center w-auto px-5 mt-6 text-sm font-medium transition-colors duration-100 rounded-full h-9 mr-7 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 bg-neutral-900 disabled:pointer-events-none hover:bg-neutral-800 text-neutral-100\"><span>Follow</span></button></div><div class=\"relative pb-6 p-7\"><p class=\"mt-12 mb-6 text-neutral-500 text-\">Creator of @tailwindcss. Listener of Slayer. Austin 3:16. BTW, Pines UI is super cool!</p><div class=\"flex items-center justify-between pr-2 text-neutral-500\"><div class=\"relative flex w-16\"><img src=\"https://cdn.devdojo.com/images/august2023/caleb.jpeg\" class=\"relative z-30 w-8 h-8 border-2 border-white rounded-full\"> <img src=\"https://cdn.devdojo.com/images/august2023/taylor.jpeg\" class=\"z-20 w-8 h-8 -translate-x-4 border-2 border-white rounded-full\"> <img src=\"https://cdn.devdojo.com/images/august2023/adam.jpeg\" class=\"z-10 w-8 h-8 border-2 border-white rounded-full -translate-x-7\"></div><a href=\"https://twitter.com/adamwathan/following\" target=\"_blank\" class=\"text-sm hover:underline\"><strong class=\"text-neutral-800\">673</strong> Following</a> <a href=\"https://twitter.com/adamwathan/followers\" target=\"_blank\" class=\"text-sm hover:underline\"><strong class=\"text-neutral-800\">168.6K</strong> Followers</a></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -35,3 +35,13 @@ func clsxMerge(variants ...Variant) templ.Attributes {
}
return combinedAttrs
}
func clsxBuilder(classes ...string) templ.Attributes {
if len(classes) == 0 {
return templ.Attributes{}
}
class := strings.Join(classes, " ")
return templ.Attributes{
"class": class,
}
}

View File

@ -0,0 +1,62 @@
package elements
func H1(content string) templ.Component {
return renderText(1, content)
}
func H2(content string) templ.Component {
return renderText(2, content)
}
func H3(content string) templ.Component {
return renderText(3, content)
}
func Text(content string) templ.Component {
return renderText(0, content)
}
templ renderText(level int, text string) {
switch level {
case 1:
<h1 class="text-2xl lg:text-3xl font-bold">
{ text }
</h1>
case 2:
<h2 class="text-xl lg:text-2xl font-bold">
{ text }
</h2>
case 3:
<h3 class="text-md lg:text-xl font-semibold">
{ text }
</h3>
default:
<p class="text-base font-normal">
{ text }
</p>
}
}
templ renderLink(attrs templ.Attributes, text string) {
<a { attrs... }>
{ text }
</a>
}
templ renderStrong(attrs templ.Attributes, text string) {
<strong { attrs... }>
{ text }
</strong>
}
templ renderEmphasis(attrs templ.Attributes, text string) {
<em { attrs... }>
{ text }
</em>
}
templ renderCode(attrs templ.Attributes, text string) {
<code { attrs... }>
{ text }
</code>
}

View File

@ -8,28 +8,23 @@ package elements
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
func Text(content string, options ...Variant) templ.Component {
if err := ValidTextVariants(options...); err != nil {
return renderParagraph(clsxMerge(TextSizeDefault, TextStyleDefault), content)
}
return renderParagraph(clsxMerge(options...), content)
func H1(content string) templ.Component {
return renderText(1, content)
}
func ValidTextVariants(variants ...Variant) error {
for _, v := range variants {
switch v.(type) {
case TextSize:
case TextStyle:
default:
return fmt.Errorf("invalid text variant: %v", v)
}
}
return nil
func H2(content string) templ.Component {
return renderText(2, content)
}
func renderHeading(attrs templ.Attributes, text string) templ.Component {
func H3(content string) templ.Component {
return renderText(3, content)
}
func Text(content string) templ.Component {
return renderText(0, content)
}
func renderText(level int, text string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
@ -47,77 +42,79 @@ func renderHeading(attrs templ.Attributes, text string) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, attrs)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/text.templ`, Line: 26, Col: 8}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
func renderParagraph(attrs templ.Attributes, text string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, attrs)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/text.templ`, Line: 32, Col: 8}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
switch level {
case 1:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1 class=\"text-2xl lg:text-3xl font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 23, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case 2:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h2 class=\"text-xl lg:text-2xl font-bold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 27, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case 3:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h3 class=\"text-md lg:text-xl font-semibold\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 31, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h3>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
default:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p class=\"text-base font-normal\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 35, Col: 10}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return templ_7745c5c3_Err
})
@ -136,9 +133,9 @@ func renderLink(attrs templ.Attributes, text string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a")
@ -153,12 +150,12 @@ func renderLink(attrs templ.Attributes, text string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(text)
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/text.templ`, Line: 38, Col: 8}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 42, Col: 8}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -183,9 +180,9 @@ func renderStrong(attrs templ.Attributes, text string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
if templ_7745c5c3_Var7 == nil {
templ_7745c5c3_Var7 = templ.NopComponent
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<strong")
@ -200,12 +197,12 @@ func renderStrong(attrs templ.Attributes, text string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(text)
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/text.templ`, Line: 44, Col: 8}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 48, Col: 8}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -230,9 +227,9 @@ func renderEmphasis(attrs templ.Attributes, text string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var9 := templ.GetChildren(ctx)
if templ_7745c5c3_Var9 == nil {
templ_7745c5c3_Var9 = templ.NopComponent
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
if templ_7745c5c3_Var10 == nil {
templ_7745c5c3_Var10 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<em")
@ -247,12 +244,12 @@ func renderEmphasis(attrs templ.Attributes, text string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(text)
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/text.templ`, Line: 50, Col: 8}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 54, Col: 8}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -277,9 +274,9 @@ func renderCode(attrs templ.Attributes, text string) templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var11 := templ.GetChildren(ctx)
if templ_7745c5c3_Var11 == nil {
templ_7745c5c3_Var11 = templ.NopComponent
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
if templ_7745c5c3_Var12 == nil {
templ_7745c5c3_Var12 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<code")
@ -294,12 +291,12 @@ func renderCode(attrs templ.Attributes, text string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(text)
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(text)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/text.templ`, Line: 56, Col: 8}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/fonts.templ`, Line: 60, Col: 8}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -311,70 +308,4 @@ func renderCode(attrs templ.Attributes, text string) templ.Component {
})
}
type TextSize int
const (
TextSizeDefault TextSize = iota
TextSizeSmall
TextSizeMedium
TextSizeLarge
)
func (s TextSize) Attributes() templ.Attributes {
switch s {
case TextSizeSmall:
return templ.Attributes{
"class": "text-sm",
}
case TextSizeLarge:
return templ.Attributes{
"class": "text-xl",
}
}
return templ.Attributes{
"class": "text-md",
}
}
type TextStyle int
const (
TextStyleDefault TextStyle = iota
TextStyleHeading
TextStyleParagraph
TextStyleLink
TextStyleStrong
TextStyleEmphasis
TextStyleCode
)
func (s TextStyle) Attributes() templ.Attributes {
switch s {
case TextStyleHeading:
return templ.Attributes{
"class": "text-xl font-bold text-xl",
}
case TextStyleParagraph:
return templ.Attributes{
"class": "text-base font-normal",
}
case TextStyleLink:
return templ.Attributes{
"class": "text-blue-600 font-medium underline",
}
case TextStyleStrong:
return templ.Attributes{
"class": "font-semibold text-md",
}
case TextStyleCode:
return templ.Attributes{
"class": "text-stone-800 font-mono",
}
default:
return templ.Attributes{
"class": "text-base font-normal text-md",
}
}
}
var _ = templruntime.GeneratedTemplate

View File

@ -7,11 +7,9 @@ templ Layout(title string) {
@defaultStyles()
<title>{ title }</title>
</head>
<body class="flex items-center justify-center h-full bg-gray-50 lg:p-24">
<main class="flex items-center justify-center w-full max-w-full">
<div id="view">
{ children... }
</div>
<body class="flex items-center justify-center h-full bg-neutral-50 lg:p-24 md:16 p-4">
<main class="flex-row items-center justify-center mx-auto w-fit max-w-screen-sm gap-y-3">
{ children... }
</main>
</body>
</html>
@ -31,7 +29,26 @@ templ defaultStyles() {
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style>[x-cloak]{display:none}</style>
<script src="https://unpkg.com/alpinejs" defer></script>
<script src="https://cdn.sonr.io/js/htmx.min.js"></script>
<link href="https://cdn.sonr.io/stylesheet.css" rel="stylesheet"/>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
<style>[x-cloak]{display:none}</style>
<style>font-family: R-Flex, system-ui, Inter, Helvetica, Arial, sans-serif;</style>
}
templ Rows() {
<div class="flex flex-row w-full gap-2 md:gap-4">
{ children... }
</div>
}
templ Columns() {
<div class="flex flex-col h-full w-full gap-3 md:gap-6 md:flex-row">
{ children... }
</div>
}
css main() {
font-family: R-Flex, system-ui, Avenir, Helvetica, Arial, sans-serif;
}

View File

@ -47,7 +47,7 @@ func Layout(title string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title></head><body class=\"flex items-center justify-center h-full bg-gray-50 lg:p-24\"><main class=\"flex items-center justify-center w-full max-w-full\"><div id=\"view\">")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title></head><body class=\"flex items-center justify-center h-full bg-neutral-50 lg:p-24 md:16 p-4\"><main class=\"flex-row items-center justify-center mx-auto w-fit max-w-screen-sm gap-y-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -55,7 +55,7 @@ func Layout(title string) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></main></body></html>")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main></body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -133,7 +133,7 @@ func defaultStyles() templ.Component {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<meta charset=\"UTF-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><style>[x-cloak]{display:none}</style><script src=\"https://unpkg.com/alpinejs\" defer></script><script src=\"https://cdn.tailwindcss.com\"></script>")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<meta charset=\"UTF-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><script src=\"https://cdn.sonr.io/js/htmx.min.js\"></script><link href=\"https://cdn.sonr.io/stylesheet.css\" rel=\"stylesheet\"><script src=\"https://cdn.tailwindcss.com\"></script><script src=\"https://unpkg.com/alpinejs\" defer></script><style>[x-cloak]{display:none}</style><style>font-family: R-Flex, system-ui, Inter, Helvetica, Arial, sans-serif;</style>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -141,4 +141,82 @@ func defaultStyles() templ.Component {
})
}
func Rows() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex flex-row w-full gap-2 md:gap-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var6.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
func Columns() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var7 := templ.GetChildren(ctx)
if templ_7745c5c3_Var7 == nil {
templ_7745c5c3_Var7 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex flex-col h-full w-full gap-3 md:gap-6 md:flex-row\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ_7745c5c3_Var7.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
func main() templ.CSSClass {
templ_7745c5c3_CSSBuilder := templruntime.GetBuilder()
templ_7745c5c3_CSSBuilder.WriteString(`font-family:R-Flex, system-ui, Avenir, Helvetica, Arial, sans-serif;`)
templ_7745c5c3_CSSID := templ.CSSID(`main`, templ_7745c5c3_CSSBuilder.String())
return templ.ComponentCSSClass{
ID: templ_7745c5c3_CSSID,
Class: templ.SafeCSS(`.` + templ_7745c5c3_CSSID + `{` + templ_7745c5c3_CSSBuilder.String() + `}`),
}
}
var _ = templruntime.GeneratedTemplate

View File

@ -26,3 +26,6 @@ templ renderIconVariant(v Icons) {
}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
}
templ SonrIcon() {
}

View File

@ -69,4 +69,26 @@ func renderIconVariant(v Icons) templ.Component {
})
}
func SonrIcon() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -0,0 +1,29 @@
package elements
type InputState int
const (
InputStateDefault InputState = iota
InputStateError
InputStateSuccess
)
templ TextInput(state InputState, label string, placeholder string) {
switch (state) {
case InputStateDefault:
<div class="flex flex-col space-y-1.5 p-6">
<label class="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" for="name">{ label }</label>
<input type="text" placeholder="{label}" id="name" value="{value}" class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"/>
</div>
case InputStateError:
<div class="flex flex-col space-y-1.5 p-6">
<label class="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" for="name">{ label }</label>
<input type="text" placeholder="{label}" id="name" value="{value}" class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"/>
</div>
case InputStateSuccess:
<div class="flex flex-col space-y-1.5 p-6">
<label class="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" for="name">{ label }</label>
<input type="text" placeholder="{label}" id="name" value="{value}" class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"/>
</div>
}
}

View File

@ -0,0 +1,97 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package elements
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
type InputState int
const (
InputStateDefault InputState = iota
InputStateError
InputStateSuccess
)
func TextInput(state InputState, label string, placeholder string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
switch state {
case InputStateDefault:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex flex-col space-y-1.5 p-6\"><label class=\"text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\" for=\"name\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(label)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/inputs.templ`, Line: 15, Col: 128}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <input type=\"text\" placeholder=\"{label}\" id=\"name\" value=\"{value}\" class=\"flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case InputStateError:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex flex-col space-y-1.5 p-6\"><label class=\"text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\" for=\"name\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(label)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/inputs.templ`, Line: 20, Col: 128}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <input type=\"text\" placeholder=\"{label}\" id=\"name\" value=\"{value}\" class=\"flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
case InputStateSuccess:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex flex-col space-y-1.5 p-6\"><label class=\"text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\" for=\"name\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(label)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/gui/elements/inputs.templ`, Line: 25, Col: 128}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> <input type=\"text\" placeholder=\"{label}\" id=\"name\" value=\"{value}\" class=\"flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50\"></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -0,0 +1,37 @@
package elements
templ RadioGroup() {
<div
x-data="{
radioGroupSelectedValue: null,
radioGroupOptions: [
{
title: 'Email Address',
description: 'Get a login code to your email address.',
value: 'email'
},
{
title: 'Phone Number',
description: 'Get a login code to your phone number.',
value: 'phone'
},
{
title: 'Passkey',
description: 'Sign in with your passkey.',
value: 'passkey'
}
]
}"
class="space-y-3"
>
<template x-for="(option, index) in radioGroupOptions" :key="index">
<label @click="radioGroupSelectedValue=option.value" class="flex items-start p-5 space-x-3 bg-white border rounded-md shadow-sm hover:bg-gray-50 border-neutral-200/70">
<input type="radio" name="radio-group" :value="option.value" class="text-gray-900 translate-y-px focus:ring-gray-700"/>
<span class="relative flex flex-col text-left space-y-1.5 leading-none">
<span x-text="option.title" class="font-semibold"></span>
<span x-text="option.description" class="text-sm opacity-50"></span>
</span>
</label>
</template>
</div>
}

View File

@ -0,0 +1,37 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package elements
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func RadioGroup() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div x-data=\"{\n radioGroupSelectedValue: null,\n radioGroupOptions: [\n {\n title: &#39;Email Address&#39;,\n description: &#39;Get a login code to your email address.&#39;,\n value: &#39;email&#39;\n },\n {\n title: &#39;Phone Number&#39;,\n description: &#39;Get a login code to your phone number.&#39;,\n value: &#39;phone&#39;\n },\n {\n title: &#39;Passkey&#39;,\n description: &#39;Sign in with your passkey.&#39;,\n value: &#39;passkey&#39;\n }\n ]\n}\" class=\"space-y-3\"><template x-for=\"(option, index) in radioGroupOptions\" :key=\"index\"><label @click=\"radioGroupSelectedValue=option.value\" class=\"flex items-start p-5 space-x-3 bg-white border rounded-md shadow-sm hover:bg-gray-50 border-neutral-200/70\"><input type=\"radio\" name=\"radio-group\" :value=\"option.value\" class=\"text-gray-900 translate-y-px focus:ring-gray-700\"> <span class=\"relative flex flex-col text-left space-y-1.5 leading-none\"><span x-text=\"option.title\" class=\"font-semibold\"></span> <span x-text=\"option.description\" class=\"text-sm opacity-50\"></span></span></label></template></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -13,15 +13,15 @@ func (s Size) CardAttributes() templ.Attributes {
switch s {
case SizeSmall:
return templ.Attributes{
"class": "max-w-md bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
"class": "max-w-lg bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
}
case SizeLarge:
return templ.Attributes{
"class": "max-w-xl bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
"class": "max-w-2xl bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
}
}
return templ.Attributes{
"class": "max-w-lg bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
"class": "max-w-xl bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
}
}

View File

@ -21,15 +21,15 @@ func (s Size) CardAttributes() templ.Attributes {
switch s {
case SizeSmall:
return templ.Attributes{
"class": "max-w-md bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
"class": "max-w-lg bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
}
case SizeLarge:
return templ.Attributes{
"class": "max-w-xl bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
"class": "max-w-2xl bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
}
}
return templ.Attributes{
"class": "max-w-lg bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
"class": "max-w-xl bg-white border rounded-lg shadow-sm p-7 border-neutral-200/60",
}
}

View File

@ -0,0 +1,109 @@
package elements
templ Tabs() {
<div
x-data="{
tabSelected: 1,
tabId: $id('tabs'),
tabButtonClicked(tabButton){
this.tabSelected = tabButton.id.replace(this.tabId + '-', '');
this.tabRepositionMarker(tabButton);
},
tabRepositionMarker(tabButton){
this.$refs.tabMarker.style.width=tabButton.offsetWidth + 'px';
this.$refs.tabMarker.style.height=tabButton.offsetHeight + 'px';
this.$refs.tabMarker.style.left=tabButton.offsetLeft + 'px';
},
tabContentActive(tabContent){
return this.tabSelected == tabContent.id.replace(this.tabId + '-content-', '');
},
tabButtonActive(tabContent){
const tabId = tabContent.id.split('-').slice(-1);
return this.tabSelected == tabId;
}
}"
x-init="tabRepositionMarker($refs.tabButtons.firstElementChild);"
class="relative w-full max-w-sm"
>
<div x-ref="tabButtons" class="relative inline-grid items-center justify-center w-full h-10 grid-cols-3 p-1 text-gray-500 bg-white border border-gray-100 rounded-lg select-none">
<button :id="$id(tabId)" @click="tabButtonClicked($el);" type="button" :class="{ 'bg-gray-100 text-gray-700' : tabButtonActive($el) }" class="relative z-20 inline-flex items-center justify-center w-full h-8 px-3 text-sm font-medium transition-all rounded-md cursor-pointer whitespace-nowrap">Tab1</button>
<button :id="$id(tabId)" @click="tabButtonClicked($el);" type="button" :class="{ 'bg-gray-100 text-gray-700' : tabButtonActive($el) }" class="relative z-20 inline-flex items-center justify-center w-full h-8 px-3 text-sm font-medium transition-all rounded-md cursor-pointer whitespace-nowrap">Tab2</button>
<button :id="$id(tabId)" @click="tabButtonClicked($el);" type="button" :class="{ 'bg-gray-100 text-gray-700' : tabButtonActive($el) }" class="relative z-20 inline-flex items-center justify-center w-full h-8 px-3 text-sm font-medium transition-all rounded-md cursor-pointer whitespace-nowrap">Tab3</button>
<div x-ref="tabMarker" class="absolute left-0 z-10 w-1/2 h-full duration-300 ease-out" x-cloak><div class="w-full h-full bg-gray-100 rounded-md shadow-sm"></div></div>
</div>
<div class="relative flex items-center justify-center w-full p-5 mt-2 text-xs text-gray-400 border rounded-md content border-gray-200/70">
<div :id="$id(tabId + '-content')" x-show="tabContentActive($el)" class="relative">
@Table()
</div>
<div :id="$id(tabId + '-content')" x-show="tabContentActive($el)" class="relative" x-cloak>
And, this is the content for Tab2
</div>
<div :id="$id(tabId + '-content')" x-show="tabContentActive($el)" class="relative" x-cloak>
Finally, this is the content for Tab3
</div>
</div>
</div>
}
templ Table() {
<div class="flex flex-col">
<div class="overflow-x-auto">
<div class="inline-block min-w-full">
<div class="overflow-hidden">
<table class="min-w-full divide-y divide-neutral-200">
<thead>
<tr class="text-neutral-500">
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Name</th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Age</th>
<th class="px-5 py-3 text-xs font-medium text-left uppercase">Address</th>
<th class="px-5 py-3 text-xs font-medium text-right uppercase">Action</th>
</tr>
</thead>
<tbody class="divide-y divide-neutral-200">
<tr class="text-neutral-800">
<td class="px-5 py-4 text-sm font-medium whitespace-nowrap">Richard Hendricks</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">30</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">Pied Piper HQ, Palo Alto</td>
<td class="px-5 py-4 text-sm font-medium text-right whitespace-nowrap">
<a class="text-blue-600 hover:text-blue-700" href="#">Edit</a>
</td>
</tr>
<tr class="text-neutral-800">
<td class="px-5 py-4 text-sm font-medium whitespace-nowrap">Erlich Bachman</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">40</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">5230 Penfield Ave, Woodland Hills</td>
<td class="px-5 py-4 text-sm font-medium text-right whitespace-nowrap">
<a class="text-blue-600 hover:text-blue-700" href="#">Edit</a>
</td>
</tr>
<tr class="text-neutral-800">
<td class="px-5 py-4 text-sm font-medium whitespace-nowrap">Monica Hall</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">35</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">2030 Stewart Drive, Sunnyvale</td>
<td class="px-5 py-4 text-sm font-medium text-right whitespace-nowrap">
<a class="text-blue-600 hover:text-blue-700" href="#">Edit</a>
</td>
</tr>
<tr class="text-neutral-800">
<td class="px-5 py-4 text-sm font-medium whitespace-nowrap">Dinesh Chugtai</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">28</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">Pied Piper HQ, Palo Alto</td>
<td class="px-5 py-4 text-sm font-medium text-right whitespace-nowrap">
<a class="text-blue-600 hover:text-blue-700" href="#">Edit</a>
</td>
</tr>
<tr class="text-neutral-800">
<td class="px-5 py-4 text-sm font-medium whitespace-nowrap">Gilfoyle</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">32</td>
<td class="px-5 py-4 text-sm whitespace-nowrap">Pied Piper HQ, Palo Alto</td>
<td class="px-5 py-4 text-sm font-medium text-right whitespace-nowrap">
<a class="text-blue-600 hover:text-blue-700" href="#">Edit</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
}

View File

@ -0,0 +1,71 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package elements
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func Tabs() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div x-data=\"{\n tabSelected: 1,\n tabId: $id(&#39;tabs&#39;),\n tabButtonClicked(tabButton){\n this.tabSelected = tabButton.id.replace(this.tabId + &#39;-&#39;, &#39;&#39;);\n this.tabRepositionMarker(tabButton);\n },\n tabRepositionMarker(tabButton){\n this.$refs.tabMarker.style.width=tabButton.offsetWidth + &#39;px&#39;;\n this.$refs.tabMarker.style.height=tabButton.offsetHeight + &#39;px&#39;;\n this.$refs.tabMarker.style.left=tabButton.offsetLeft + &#39;px&#39;;\n },\n tabContentActive(tabContent){\n return this.tabSelected == tabContent.id.replace(this.tabId + &#39;-content-&#39;, &#39;&#39;);\n },\n tabButtonActive(tabContent){\n const tabId = tabContent.id.split(&#39;-&#39;).slice(-1);\n return this.tabSelected == tabId;\n }\n }\" x-init=\"tabRepositionMarker($refs.tabButtons.firstElementChild);\" class=\"relative w-full max-w-sm\"><div x-ref=\"tabButtons\" class=\"relative inline-grid items-center justify-center w-full h-10 grid-cols-3 p-1 text-gray-500 bg-white border border-gray-100 rounded-lg select-none\"><button :id=\"$id(tabId)\" @click=\"tabButtonClicked($el);\" type=\"button\" :class=\"{ &#39;bg-gray-100 text-gray-700&#39; : tabButtonActive($el) }\" class=\"relative z-20 inline-flex items-center justify-center w-full h-8 px-3 text-sm font-medium transition-all rounded-md cursor-pointer whitespace-nowrap\">Tab1</button> <button :id=\"$id(tabId)\" @click=\"tabButtonClicked($el);\" type=\"button\" :class=\"{ &#39;bg-gray-100 text-gray-700&#39; : tabButtonActive($el) }\" class=\"relative z-20 inline-flex items-center justify-center w-full h-8 px-3 text-sm font-medium transition-all rounded-md cursor-pointer whitespace-nowrap\">Tab2</button> <button :id=\"$id(tabId)\" @click=\"tabButtonClicked($el);\" type=\"button\" :class=\"{ &#39;bg-gray-100 text-gray-700&#39; : tabButtonActive($el) }\" class=\"relative z-20 inline-flex items-center justify-center w-full h-8 px-3 text-sm font-medium transition-all rounded-md cursor-pointer whitespace-nowrap\">Tab3</button><div x-ref=\"tabMarker\" class=\"absolute left-0 z-10 w-1/2 h-full duration-300 ease-out\" x-cloak><div class=\"w-full h-full bg-gray-100 rounded-md shadow-sm\"></div></div></div><div class=\"relative flex items-center justify-center w-full p-5 mt-2 text-xs text-gray-400 border rounded-md content border-gray-200/70\"><div :id=\"$id(tabId + &#39;-content&#39;)\" x-show=\"tabContentActive($el)\" class=\"relative\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Table().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div :id=\"$id(tabId + &#39;-content&#39;)\" x-show=\"tabContentActive($el)\" class=\"relative\" x-cloak>And, this is the content for Tab2</div><div :id=\"$id(tabId + &#39;-content&#39;)\" x-show=\"tabContentActive($el)\" class=\"relative\" x-cloak>Finally, this is the content for Tab3</div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
func Table() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"flex flex-col\"><div class=\"overflow-x-auto\"><div class=\"inline-block min-w-full\"><div class=\"overflow-hidden\"><table class=\"min-w-full divide-y divide-neutral-200\"><thead><tr class=\"text-neutral-500\"><th class=\"px-5 py-3 text-xs font-medium text-left uppercase\">Name</th><th class=\"px-5 py-3 text-xs font-medium text-left uppercase\">Age</th><th class=\"px-5 py-3 text-xs font-medium text-left uppercase\">Address</th><th class=\"px-5 py-3 text-xs font-medium text-right uppercase\">Action</th></tr></thead> <tbody class=\"divide-y divide-neutral-200\"><tr class=\"text-neutral-800\"><td class=\"px-5 py-4 text-sm font-medium whitespace-nowrap\">Richard Hendricks</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">30</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">Pied Piper HQ, Palo Alto</td><td class=\"px-5 py-4 text-sm font-medium text-right whitespace-nowrap\"><a class=\"text-blue-600 hover:text-blue-700\" href=\"#\">Edit</a></td></tr><tr class=\"text-neutral-800\"><td class=\"px-5 py-4 text-sm font-medium whitespace-nowrap\">Erlich Bachman</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">40</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">5230 Penfield Ave, Woodland Hills</td><td class=\"px-5 py-4 text-sm font-medium text-right whitespace-nowrap\"><a class=\"text-blue-600 hover:text-blue-700\" href=\"#\">Edit</a></td></tr><tr class=\"text-neutral-800\"><td class=\"px-5 py-4 text-sm font-medium whitespace-nowrap\">Monica Hall</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">35</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">2030 Stewart Drive, Sunnyvale</td><td class=\"px-5 py-4 text-sm font-medium text-right whitespace-nowrap\"><a class=\"text-blue-600 hover:text-blue-700\" href=\"#\">Edit</a></td></tr><tr class=\"text-neutral-800\"><td class=\"px-5 py-4 text-sm font-medium whitespace-nowrap\">Dinesh Chugtai</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">28</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">Pied Piper HQ, Palo Alto</td><td class=\"px-5 py-4 text-sm font-medium text-right whitespace-nowrap\"><a class=\"text-blue-600 hover:text-blue-700\" href=\"#\">Edit</a></td></tr><tr class=\"text-neutral-800\"><td class=\"px-5 py-4 text-sm font-medium whitespace-nowrap\">Gilfoyle</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">32</td><td class=\"px-5 py-4 text-sm whitespace-nowrap\">Pied Piper HQ, Palo Alto</td><td class=\"px-5 py-4 text-sm font-medium text-right whitespace-nowrap\"><a class=\"text-blue-600 hover:text-blue-700\" href=\"#\">Edit</a></td></tr></tbody></table></div></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -1,124 +0,0 @@
package elements
import "fmt"
func Text(content string, options ...Variant) templ.Component {
if err := ValidTextVariants(options...); err != nil {
return renderParagraph(clsxMerge(TextSizeDefault, TextStyleDefault), content)
}
return renderParagraph(clsxMerge(options...), content)
}
func ValidTextVariants(variants ...Variant) error {
for _, v := range variants {
switch v.(type) {
case TextSize:
case TextStyle:
default:
return fmt.Errorf("invalid text variant: %v", v)
}
}
return nil
}
templ renderHeading(attrs templ.Attributes, text string) {
<h1 { attrs... }>
{ text }
</h1>
}
templ renderParagraph(attrs templ.Attributes, text string) {
<p { attrs... }>
{ text }
</p>
}
templ renderLink(attrs templ.Attributes, text string) {
<a { attrs... }>
{ text }
</a>
}
templ renderStrong(attrs templ.Attributes, text string) {
<strong { attrs... }>
{ text }
</strong>
}
templ renderEmphasis(attrs templ.Attributes, text string) {
<em { attrs... }>
{ text }
</em>
}
templ renderCode(attrs templ.Attributes, text string) {
<code { attrs... }>
{ text }
</code>
}
type TextSize int
const (
TextSizeDefault TextSize = iota
TextSizeSmall
TextSizeMedium
TextSizeLarge
)
func (s TextSize) Attributes() templ.Attributes {
switch s {
case TextSizeSmall:
return templ.Attributes{
"class": "text-sm",
}
case TextSizeLarge:
return templ.Attributes{
"class": "text-xl",
}
}
return templ.Attributes{
"class": "text-md",
}
}
type TextStyle int
const (
TextStyleDefault TextStyle = iota
TextStyleHeading
TextStyleParagraph
TextStyleLink
TextStyleStrong
TextStyleEmphasis
TextStyleCode
)
func (s TextStyle) Attributes() templ.Attributes {
switch s {
case TextStyleHeading:
return templ.Attributes{
"class": "text-xl font-bold text-xl",
}
case TextStyleParagraph:
return templ.Attributes{
"class": "text-base font-normal",
}
case TextStyleLink:
return templ.Attributes{
"class": "text-blue-600 font-medium underline",
}
case TextStyleStrong:
return templ.Attributes{
"class": "font-semibold text-md",
}
case TextStyleCode:
return templ.Attributes{
"class": "text-stone-800 font-mono",
}
default:
return templ.Attributes{
"class": "text-base font-normal text-md",
}
}
}

View File

@ -0,0 +1,41 @@
package forms
import (
"bytes"
"github.com/a-h/templ"
"github.com/labstack/echo/v4"
)
func echoFormResponse(c echo.Context, cmp templ.Component, state FormState) error {
// Create a buffer to store the rendered HTML
buf := &bytes.Buffer{}
// Render the component to the buffer
err := cmp.Render(c.Request().Context(), buf)
if err != nil {
return err
}
// Set the content type
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML)
c.Response().Header().Set("X-Status", string(state))
// Write the buffered content to the response
_, err = c.Response().Write(buf.Bytes())
return err
}
type FormState string
const (
InitialForm FormState = "initial"
ErrorForm FormState = "error"
SuccessForm FormState = "success"
WarningForm FormState = "warning"
)
type Form struct {
State FormState
Title string
Description string
}

View File

@ -0,0 +1,25 @@
package forms
import "github.com/labstack/echo/v4"
templ BasicInfo(c echo.Context, state FormState) {
switch (state) {
default:
<div class="border rounded-lg shadow-sm bg-card text-neutral-900">
<div class="flex flex-col space-y-1.5 p-6">
<h3 class="text-lg font-semibold leading-none tracking-tight">Account</h3>
<p class="text-sm text-neutral-500">Make changes to your account here. Click save when you're done.</p>
</div>
<div class="p-6 pt-0 space-y-2">
<div class="space-y-1"><label class="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" for="name">Name</label><input type="text" id="name" placeholder="Adam Wathan" class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"/></div>
<div class="space-y-1"><label class="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" for="username">Handle</label><input type="text" id="handle" placeholder="angelo.snr" class="flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50"/></div>
</div>
</div>
}
}
templ CreateCredentials(state FormState) {
}
templ PrivacyTerms(state FormState) {
}

View File

@ -0,0 +1,86 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package forms
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "github.com/labstack/echo/v4"
func BasicInfo(c echo.Context, state FormState) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
switch state {
default:
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"border rounded-lg shadow-sm bg-card text-neutral-900\"><div class=\"flex flex-col space-y-1.5 p-6\"><h3 class=\"text-lg font-semibold leading-none tracking-tight\">Account</h3><p class=\"text-sm text-neutral-500\">Make changes to your account here. Click save when you're done.</p></div><div class=\"p-6 pt-0 space-y-2\"><div class=\"space-y-1\"><label class=\"text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\" for=\"name\">Name</label><input type=\"text\" id=\"name\" placeholder=\"Adam Wathan\" class=\"flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50\"></div><div class=\"space-y-1\"><label class=\"text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\" for=\"username\">Handle</label><input type=\"text\" id=\"handle\" placeholder=\"angelo.snr\" class=\"flex w-full h-10 px-3 py-2 text-sm bg-white border rounded-md peer border-neutral-300 ring-offset-background placeholder:text-neutral-400 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50\"></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return templ_7745c5c3_Err
})
}
func CreateCredentials(state FormState) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
return templ_7745c5c3_Err
})
}
func PrivacyTerms(state FormState) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -11,13 +11,19 @@ func HomeView(c echo.Context) error {
templ renderHomeView() {
@elements.Layout("Sonr.ID") {
@elements.Card(elements.SizeMedium) {
@elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading)
@elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph)
@elements.Card("home-view", elements.SizeLarge) {
@elements.H1("Sonr.ID")
@elements.Text("A Decentralized Web Node Client for the Sonr Network.")
@elements.Spacer()
@elements.Button(elements.ButtonVariantPrimary) {
@elements.Text("Get Started", elements.TextSizeMedium, elements.TextStyleParagraph)
}
<div class="flex flex-col gap-3">
@elements.Button(elements.GET("/register", "#home-view"), elements.PrimaryButtonStyle()) {
@elements.Text("Get Started")
}
@elements.Button(elements.GET("/login", "#home-view")) {
@elements.Text("Login")
}
</div>
@elements.PoweredBySonr()
}
}
}

View File

@ -59,7 +59,7 @@ func renderHomeView() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.H1("Sonr.ID").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -67,7 +67,7 @@ func renderHomeView() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Text("A Decentralized Web Node Client for the Sonr Network.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -79,7 +79,7 @@ func renderHomeView() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <div class=\"flex flex-col gap-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -95,19 +95,49 @@ func renderHomeView() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Get Started", elements.TextSizeMedium, elements.TextStyleParagraph).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Text("Get Started").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Button(elements.ButtonVariantPrimary).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Button(elements.GET("/register", "#home-view"), elements.PrimaryButtonStyle()).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Login").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Button(elements.GET("/login", "#home-view")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.PoweredBySonr().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card(elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Card("home-view", elements.SizeLarge).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -10,13 +10,15 @@ func LoginView(c echo.Context) error {
}
templ renderLoginView() {
@elements.Layout("Sonr.ID") {
@elements.Card(elements.SizeMedium) {
@elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading)
@elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph)
@elements.Layout("Login | Sonr.ID") {
@elements.Card("login-view", elements.SizeLarge) {
@elements.H1("Sonr.ID")
@elements.Text("Neo-tree is a file manager for NeoFS.")
@elements.Spacer()
@elements.Button(elements.ButtonVariantPrimary) {
@elements.Text("Login", elements.TextSizeMedium, elements.TextStyleParagraph)
@elements.RadioGroup()
@elements.Spacer()
@elements.Button(elements.GET("/", "#login-view")) {
@elements.Text("Cancel")
}
}
}

View File

@ -59,7 +59,7 @@ func renderLoginView() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.H1("Sonr.ID").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -67,7 +67,23 @@ func renderLoginView() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Text("Neo-tree is a file manager for NeoFS.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Spacer().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.RadioGroup().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -95,25 +111,25 @@ func renderLoginView() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Login", elements.TextSizeMedium, elements.TextStyleParagraph).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Text("Cancel").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Button(elements.ButtonVariantPrimary).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Button(elements.GET("/", "#login-view")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card(elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Card("login-view", elements.SizeLarge).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Layout("Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Layout("Login | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -11,9 +11,9 @@ func AuthorizeView(c echo.Context) error {
templ renderAuthorizeView() {
@elements.Layout("Login | Sonr.ID") {
@elements.Card(elements.SizeMedium) {
@elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading)
@elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph)
@elements.Card("authorize-view", elements.SizeMedium) {
@elements.H1("Sonr.ID")
@elements.Text("Neo-tree is a file manager for NeoFS.")
}
}
}

View File

@ -59,7 +59,7 @@ func renderAuthorizeView() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.H1("Sonr.ID").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -67,13 +67,13 @@ func renderAuthorizeView() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Text("Neo-tree is a file manager for NeoFS.").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card(elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Card("authorize-view", elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -0,0 +1,19 @@
package views
import (
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/gui/elements"
)
func ProfileView(c echo.Context) error {
return echoComponentResponse(c, renderProfileView())
}
templ renderProfileView() {
@elements.Layout("Profile | Sonr.ID") {
@elements.Card("profile-view", elements.SizeLarge) {
@elements.ProfileCard()
@elements.Tabs()
}
}
}

View File

@ -0,0 +1,90 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.771
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/gui/elements"
)
func ProfileView(c echo.Context) error {
return echoComponentResponse(c, renderProfileView())
}
func renderProfileView() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.ProfileCard().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Tabs().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card("profile-view", elements.SizeLarge).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Layout("Profile | Sonr.ID").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -3,31 +3,24 @@ package views
import (
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/gui/elements"
"github.com/onsonr/sonr/internal/gui/forms"
)
func RegisterView(c echo.Context) error {
return echoComponentResponse(c, renderRegisterView())
return echoComponentResponse(c, renderRegisterView(c))
}
templ renderRegisterView() {
templ renderRegisterView(c echo.Context) {
@elements.Layout("Register | Sonr.ID") {
@elements.Card(elements.SizeMedium) {
@elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading)
@elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph)
}
@elements.Card(elements.SizeMedium) {
<h1>Welcome to Neo-tree</h1>
<p>Neo-tree is a file manager for NeoFS.</p>
}
@elements.Card(elements.SizeMedium) {
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Welcome to Neo-tree</h1>
<p>Neo-tree is a file manager for NeoFS.</p>
</div>
</div>
</div>
@elements.Card("register-view", elements.SizeMedium) {
@elements.H2("Account Registration")
@elements.Spacer()
@elements.Breadcrumbs()
@forms.BasicInfo(c, forms.InitialForm)
@elements.Spacer()
@elements.Button(elements.GET("/", "#register-view")) {
@elements.Text("Cancel")
}
}
}
}

View File

@ -11,13 +11,14 @@ import templruntime "github.com/a-h/templ/runtime"
import (
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/gui/elements"
"github.com/onsonr/sonr/internal/gui/forms"
)
func RegisterView(c echo.Context) error {
return echoComponentResponse(c, renderRegisterView())
return echoComponentResponse(c, renderRegisterView(c))
}
func renderRegisterView() templ.Component {
func renderRegisterView(c echo.Context) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
@ -59,7 +60,7 @@ func renderRegisterView() templ.Component {
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Sonr.ID", elements.TextSizeLarge, elements.TextStyleHeading).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.H2("Account Registration").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@ -67,65 +68,63 @@ func renderRegisterView() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Text("Neo-tree is a file manager for NeoFS.", elements.TextSizeMedium, elements.TextStyleParagraph).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Spacer().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Breadcrumbs().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = forms.BasicInfo(c, forms.InitialForm).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = elements.Spacer().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = elements.Text("Cancel").Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Button(elements.GET("/", "#register-view")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card(elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>Welcome to Neo-tree</h1><p>Neo-tree is a file manager for NeoFS.</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card(elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Var5 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><div class=\"row\"><div class=\"col-md-12\"><h1>Welcome to Neo-tree</h1><p>Neo-tree is a file manager for NeoFS.</p></div></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = elements.Card(elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
templ_7745c5c3_Err = elements.Card("register-view", elements.SizeMedium).Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View File

@ -1,41 +1,61 @@
package mdw
import (
"fmt"
"net/http"
"time"
echojwt "github.com/labstack/echo-jwt/v4"
"github.com/labstack/echo/v4"
"gopkg.in/macaroon.v2"
)
type Authz struct {
echo.Context
echojwt.Config
const (
OriginMacroonCaveat MacroonCaveat = "origin"
ScopesMacroonCaveat MacroonCaveat = "scopes"
SubjectMacroonCaveat MacroonCaveat = "subject"
ExpMacroonCaveat MacroonCaveat = "exp"
TokenMacroonCaveat MacroonCaveat = "token"
)
signKey []byte
type MacroonCaveat string
func (c MacroonCaveat) Equal(other string) bool {
return string(c) == other
}
func newAuthz(c echo.Context, signKey []byte) *Authz {
return &Authz{Context: c, signKey: signKey}
func (c MacroonCaveat) String() string {
return string(c)
}
func (a *Authz) Accessible(route string, handler echo.HandlerFunc) echo.HandlerFunc {
// Verify the macaroon
// verified := a.Verify(a.signKey, func(caveat string) error {
// Implement your caveat verification logic here
// For example, you might check if the caveat is still valid (e.g., not expired)
// return nil // Return nil if the caveat is valid
// }, nil)
// if !verified {
// return func(c echo.Context) error {
// return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid macaroon"})
// }
// }
a.SetPath(route)
return handler
func (c MacroonCaveat) Verify(value string) error {
switch c {
case OriginMacroonCaveat:
return nil
case ScopesMacroonCaveat:
return nil
case SubjectMacroonCaveat:
return nil
case ExpMacroonCaveat:
// Check if the expiration time is still valid
exp, err := time.Parse(time.RFC3339, value)
if err != nil {
return err
}
if time.Now().After(exp) {
return fmt.Errorf("expired")
}
return nil
case TokenMacroonCaveat:
return nil
default:
return fmt.Errorf("unknown caveat: %s", c)
}
}
func ValidateMacaroonMiddleware(secretKey []byte, location string) echo.MiddlewareFunc {
var MacroonCaveats = []MacroonCaveat{OriginMacroonCaveat, ScopesMacroonCaveat, SubjectMacroonCaveat, ExpMacroonCaveat, TokenMacroonCaveat}
func MacaroonMiddleware(secretKeyStr string, location string) echo.MiddlewareFunc {
secretKey := []byte(secretKeyStr)
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Extract the macaroon from the Authorization header
@ -57,8 +77,11 @@ func ValidateMacaroonMiddleware(secretKey []byte, location string) echo.Middlewa
// Verify the macaroon
err = token.Verify(secretKey, func(caveat string) error {
// Implement your caveat verification logic here
// For example, you might check if the caveat is still valid (e.g., not expired)
for _, c := range MacroonCaveats {
if c.String() == caveat {
return nil
}
}
return nil // Return nil if the caveat is valid
}, nil)
if err != nil {

3
internal/mdw/client.go Normal file
View File

@ -0,0 +1,3 @@
package mdw
type AuthClient struct{}

View File

@ -1,7 +1,5 @@
package mdw
import "github.com/labstack/echo/v4"
type RequestHeaders struct {
Authorization *string `header:"Authorization"`
CacheControl *string `header:"Cache-Control"`
@ -55,8 +53,3 @@ type ResponseHeaders struct {
HXTriggerAfterSettle *string `header:"HX-Trigger-After-Settle"`
HXTriggerAfterSwap *string `header:"HX-Trigger-After-Swap"`
}
func bindRequestHeaders(c echo.Context) {
headers := new(RequestHeaders)
c.Bind(headers)
}

View File

@ -10,13 +10,10 @@ import (
"github.com/segmentio/ksuid"
)
// UseSession establishes a Session Cookie.
func UseSession(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
sc := newSession(c)
bindRequestHeaders(sc)
return next(sc)
}
type Session struct {
echo.Context
htmx *htmx.HTMX
dB *db.DB
}
// GetSession returns the current Session
@ -24,25 +21,29 @@ func GetSession(c echo.Context) *Session {
return c.(*Session)
}
type Session struct {
echo.Context
htmx *htmx.HTMX
dB *db.DB
}
func (c *Session) ID() string {
return readCookie(c, "session")
}
func (c *Session) Htmx() *htmx.HTMX {
return c.htmx
// UseSession establishes a Session Cookie.
func UseSession(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
sc := initSession(c)
headers := new(RequestHeaders)
sc.Bind(headers)
return next(sc)
}
}
func (c *Session) DB() *db.DB {
return c.dB
}
func newSession(c echo.Context) *Session {
func (c *Session) Htmx() *htmx.HTMX {
return c.htmx
}
func (c *Session) ID() string {
return readCookie(c, "session")
}
func initSession(c echo.Context) *Session {
s := &Session{Context: c}
if val := readCookie(c, "session"); val == "" {
id := ksuid.New().String()

View File

@ -1 +0,0 @@
package mdw

View File

@ -1,8 +1,10 @@
package svc
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/db/orm"
oidc "github.com/onsonr/sonr/x/did/types/oidc"
)
func GrantAuthorization(e echo.Context) error {
@ -25,13 +27,13 @@ func GetToken(e echo.Context) error {
func GetDiscovery(e echo.Context) error {
baseURL := "https://" + e.Request().Host // Ensure this is the correct base URL for your service
discoveryDoc := &orm.DiscoveryDocument{
Issuer: "https://sonr.id",
AuthorizationEndpoint: "https://api.sonr.id/auth",
TokenEndpoint: "https://api.sonr.id/token",
UserinfoEndpoint: "https://api.sonr.id/userinfo",
JwksUri: baseURL + "/jwks", // You'll need to implement this endpoint
RegistrationEndpoint: baseURL + "/register",
discoveryDoc := &oidc.DiscoveryDocument{
Issuer: baseURL,
AuthorizationEndpoint: fmt.Sprintf("%s/auth", baseURL),
TokenEndpoint: fmt.Sprintf("%s/token", baseURL),
UserinfoEndpoint: fmt.Sprintf("%s/userinfo", baseURL),
JwksUri: fmt.Sprintf("%s/jwks", baseURL),
RegistrationEndpoint: fmt.Sprintf("%s/register", baseURL),
ScopesSupported: []string{"openid", "profile", "email", "web3", "sonr"},
ResponseTypesSupported: []string{"code"},
ResponseModesSupported: []string{"query", "form_post"},

View File

@ -1,4 +1,4 @@
package tui
package txmodel
import (
"fmt"
@ -12,7 +12,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/spf13/cobra"
)
const maxWidth = 100
@ -321,28 +320,3 @@ func RunTUIForm() (*tx.TxBody, error) {
return finalM.message, nil
}
func NewTUIDashboardCmd() *cobra.Command {
return &cobra.Command{
Use: "dash",
Short: "TUI for managing the local Sonr validator node",
RunE: func(cmd *cobra.Command, args []string) error {
txBody, err := RunTUIForm()
if err != nil {
return err
}
interfaceRegistry := codectypes.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
jsonBytes, err := marshaler.MarshalJSON(txBody)
if err != nil {
return fmt.Errorf("failed to marshal tx body: %w", err)
}
fmt.Println("Generated Protobuf Message (JSON format):")
fmt.Println(string(jsonBytes))
return nil
},
}
}

View File

@ -1,4 +1,4 @@
package tui
package dexmodel
import (
"fmt"
@ -156,18 +156,10 @@ func tick() tea.Msg {
return tickMsg{}
}
func runExplorer(cmd *cobra.Command, args []string) error {
func RunExplorerTUI(cmd *cobra.Command, args []string) error {
p := tea.NewProgram(initialModel(), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
return fmt.Errorf("error running explorer: %v", err)
}
return nil
}
func NewExplorerCmd() *cobra.Command {
return &cobra.Command{
Use: "cosmos-explorer",
Short: "A terminal-based Cosmos blockchain explorer",
RunE: runExplorer,
}
}

49
internal/tui/tui.go Normal file
View File

@ -0,0 +1,49 @@
package tui
import (
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/onsonr/sonr/internal/tui/dexmodel"
"github.com/onsonr/sonr/internal/tui/txmodel"
"github.com/spf13/cobra"
)
func AddTUICmds(rootCmd *cobra.Command) {
rootCmd.AddCommand(newBuildTxnTUICmd())
rootCmd.AddCommand(newExplorerTUICmd())
}
func newBuildTxnTUICmd() *cobra.Command {
return &cobra.Command{
Use: "dash",
Short: "TUI for managing the local Sonr validator node",
RunE: func(cmd *cobra.Command, args []string) error {
txBody, err := txmodel.RunBuildTxnTUI()
if err != nil {
return err
}
interfaceRegistry := codectypes.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
jsonBytes, err := marshaler.MarshalJSON(txBody)
if err != nil {
return fmt.Errorf("failed to marshal tx body: %w", err)
}
fmt.Println("Generated Protobuf Message (JSON format):")
fmt.Println(string(jsonBytes))
return nil
},
}
}
func newExplorerTUICmd() *cobra.Command {
return &cobra.Command{
Use: "cosmos-explorer",
Short: "A terminal-based Cosmos blockchain explorer",
RunE: dexmodel.RunExplorerTUI,
}
}

View File

@ -0,0 +1,322 @@
package txmodel
import (
"fmt"
"strings"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
const maxWidth = 100
var (
red = lipgloss.AdaptiveColor{Light: "#FE5F86", Dark: "#FE5F86"}
indigo = lipgloss.AdaptiveColor{Light: "#5A56E0", Dark: "#7571F9"}
green = lipgloss.AdaptiveColor{Light: "#02BA84", Dark: "#02BF87"}
)
type Styles struct {
Base,
HeaderText,
Status,
StatusHeader,
Highlight,
ErrorHeaderText,
Help lipgloss.Style
}
func NewStyles(lg *lipgloss.Renderer) *Styles {
s := Styles{}
s.Base = lg.NewStyle().
Padding(1, 2, 0, 1)
s.HeaderText = lg.NewStyle().
Foreground(indigo).
Bold(true).
Padding(0, 1, 0, 1)
s.Status = lg.NewStyle().
Border(lipgloss.RoundedBorder()).
BorderForeground(indigo).
PaddingLeft(1).
MarginTop(1)
s.StatusHeader = lg.NewStyle().
Foreground(green).
Bold(true)
s.Highlight = lg.NewStyle().
Foreground(lipgloss.Color("212"))
s.ErrorHeaderText = s.HeaderText.
Foreground(red)
s.Help = lg.NewStyle().
Foreground(lipgloss.Color("240"))
return &s
}
type state int
const (
statusNormal state = iota
stateDone
)
type Model struct {
state state
lg *lipgloss.Renderer
styles *Styles
form *huh.Form
width int
message *tx.TxBody
}
func NewModel() Model {
m := Model{width: maxWidth}
m.lg = lipgloss.DefaultRenderer()
m.styles = NewStyles(m.lg)
m.form = huh.NewForm(
huh.NewGroup(
huh.NewInput().
Key("from").
Title("From Address").
Placeholder("cosmos1...").
Validate(func(s string) error {
if !strings.HasPrefix(s, "cosmos1") {
return fmt.Errorf("invalid address format")
}
return nil
}),
huh.NewInput().
Key("to").
Title("To Address").
Placeholder("cosmos1...").
Validate(func(s string) error {
if !strings.HasPrefix(s, "cosmos1") {
return fmt.Errorf("invalid address format")
}
return nil
}),
huh.NewInput().
Key("amount").
Title("Amount").
Placeholder("100").
Validate(func(s string) error {
if _, err := sdk.ParseCoinNormalized(s + "atom"); err != nil {
return fmt.Errorf("invalid coin amount")
}
return nil
}),
huh.NewSelect[string]().
Key("denom").
Title("Denom").
Options(huh.NewOptions("atom", "osmo", "usnr", "snr")...),
huh.NewInput().
Key("memo").
Title("Memo").
Placeholder("Optional"),
huh.NewConfirm().
Key("done").
Title("Ready to convert?").
Validate(func(v bool) error {
if !v {
return fmt.Errorf("Please confirm when you're ready to convert")
}
return nil
}).
Affirmative("Yes, convert!").
Negative("Not yet"),
),
).
WithWidth(60).
WithShowHelp(false).
WithShowErrors(false)
return m
}
func (m Model) Init() tea.Cmd {
return m.form.Init()
}
func min(x, y int) int {
if x > y {
return y
}
return x
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width = min(msg.Width, maxWidth) - m.styles.Base.GetHorizontalFrameSize()
case tea.KeyMsg:
switch msg.String() {
case "esc", "ctrl+c", "q":
return m, tea.Quit
}
}
var cmds []tea.Cmd
form, cmd := m.form.Update(msg)
if f, ok := form.(*huh.Form); ok {
m.form = f
cmds = append(cmds, cmd)
}
if m.form.State == huh.StateCompleted {
m.buildMessage()
cmds = append(cmds, tea.Quit)
}
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
s := m.styles
switch m.form.State {
case huh.StateCompleted:
pklCode := m.generatePkl()
messageView := m.getMessageView()
var b strings.Builder
fmt.Fprintf(&b, "Final Tx:\n\n%s\n\n%s", pklCode, messageView)
return s.Status.Margin(0, 1).Padding(1, 2).Width(80).Render(b.String()) + "\n\n"
default:
var schemaType string
if m.form.GetString("schemaType") != "" {
schemaType = "Schema Type: " + m.form.GetString("schemaType")
}
v := strings.TrimSuffix(m.form.View(), "\n\n")
form := m.lg.NewStyle().Margin(1, 0).Render(v)
var status string
{
preview := "(Preview will appear here)"
if m.form.GetString("schema") != "" {
preview = m.generatePkl()
}
const statusWidth = 40
statusMarginLeft := m.width - statusWidth - lipgloss.Width(form) - s.Status.GetMarginRight()
status = s.Status.
Height(lipgloss.Height(form)).
Width(statusWidth).
MarginLeft(statusMarginLeft).
Render(s.StatusHeader.Render("Pkl Preview") + "\n" +
schemaType + "\n\n" +
preview)
}
errors := m.form.Errors()
header := m.appBoundaryView("Sonr TX Builder")
if len(errors) > 0 {
header = m.appErrorBoundaryView(m.errorView())
}
body := lipgloss.JoinHorizontal(lipgloss.Top, form, status)
footer := m.appBoundaryView(m.form.Help().ShortHelpView(m.form.KeyBinds()))
if len(errors) > 0 {
footer = m.appErrorBoundaryView("")
}
return s.Base.Render(header + "\n" + body + "\n\n" + footer)
}
}
func (m Model) errorView() string {
var s string
for _, err := range m.form.Errors() {
s += err.Error()
}
return s
}
func (m Model) appBoundaryView(text string) string {
return lipgloss.PlaceHorizontal(
m.width,
lipgloss.Left,
m.styles.HeaderText.Render(text),
lipgloss.WithWhitespaceChars("="),
lipgloss.WithWhitespaceForeground(indigo),
)
}
func (m Model) appErrorBoundaryView(text string) string {
return lipgloss.PlaceHorizontal(
m.width,
lipgloss.Left,
m.styles.ErrorHeaderText.Render(text),
lipgloss.WithWhitespaceChars("="),
lipgloss.WithWhitespaceForeground(red),
)
}
func (m Model) generatePkl() string {
schemaType := m.form.GetString("schemaType")
schema := m.form.GetString("schema")
// This is a placeholder for the actual conversion logic
// In a real implementation, you would parse the schema and generate Pkl code
return fmt.Sprintf("// Converted from %s\n\nclass ConvertedSchema {\n // TODO: Implement conversion from %s\n // Original schema:\n /*\n%s\n */\n}", schemaType, schemaType, schema)
}
func (m *Model) buildMessage() {
from := m.form.GetString("from")
to := m.form.GetString("to")
amount := m.form.GetString("amount")
denom := m.form.GetString("denom")
memo := m.form.GetString("memo")
coin, _ := sdk.ParseCoinNormalized(fmt.Sprintf("%s%s", amount, denom))
sendMsg := &banktypes.MsgSend{
FromAddress: from,
ToAddress: to,
Amount: sdk.NewCoins(coin),
}
anyMsg, _ := codectypes.NewAnyWithValue(sendMsg)
m.message = &tx.TxBody{
Messages: []*codectypes.Any{anyMsg},
Memo: memo,
}
}
func (m Model) getMessageView() string {
if m.message == nil {
return "Current Message: None"
}
interfaceRegistry := codectypes.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
jsonBytes, _ := marshaler.MarshalJSON(m.message)
return fmt.Sprintf("Current Message:\n%s", string(jsonBytes))
}
func RunBuildTxnTUI() (*tx.TxBody, error) {
m := NewModel()
p := tea.NewProgram(m)
finalModel, err := p.Run()
if err != nil {
return nil, fmt.Errorf("failed to run program: %w", err)
}
finalM, ok := finalModel.(Model)
if !ok || finalM.message == nil {
return nil, fmt.Errorf("form not completed")
}
return finalM.message, nil
}

37
pkl/api.pkl Normal file
View File

@ -0,0 +1,37 @@
@go.Package { name = "github.com/onsonr/sonr/motr/api" }
module api
import "package://pkg.pkl-lang.org/pkl-go/pkl.golang@0.5.0#/go.pkl"
class JsonField extends go.Field {
structTags {
["json"] = "%{name},omitempty"
}
}
abstract class Request {
route: String
method: String
headers: Map<String, String>
origin: String?
userAgent: String?
body: Dynamic
}
abstract class Response {
statusCode: Int
headers: Map<String, String>
body: Dynamic
}
class GetAccountsRequest extends Request {
route = "/accounts"
method = "GET"
headers = {
"Content-Type" = "application/json"
}
origin = null
userAgent = null
body = null
}

30
pkl/bake.pkl Normal file
View File

@ -0,0 +1,30 @@
@go.Package { name = "github.com/onsonr/sonr/motr/api/bake" }
module bake
import "package://pkg.pkl-lang.org/pkl-go/pkl.golang@0.5.0#/go.pkl"
class JsonField extends go.Field {
structTags {
["json"] = "%{name},omitempty"
}
}
abstract class Macroon {
id: String
location: String
}
abstract class FirstPartyCaveats {
scope: String
exp: Int
cnf: String
aud: String
}
abstract class ThirdPartyCaveats {
scope: String
exp: Int
cnf: String
aud: String
}

59
pkl/oidc.pkl Normal file
View File

@ -0,0 +1,59 @@
@go.Package { name = "github.com/onsonr/sonr/x/did/types/oidc" }
module oidc
import "package://pkg.pkl-lang.org/pkl-go/pkl.golang@0.5.0#/go.pkl"
class JsonField extends go.Field {
structTags {
["json"] = "%{name},omitempty"
["param"] = "%{name}"
}
}
class DiscoveryDocument {
@JsonField
issuer: String
@JsonField
authorization_endpoint: String
@JsonField
token_endpoint: String
@JsonField
userinfo_endpoint: String
@JsonField
jwks_uri: String
@JsonField
registration_endpoint: String
@JsonField
scopes_supported: List<String>
@JsonField
response_types_supported: List<String>
@JsonField
response_modes_supported: List<String>
@JsonField
subject_types_supported: List<String>
@JsonField
id_token_signing_alg_values_supported: List<String>
@JsonField
claims_supported: List<String>
@JsonField
grant_types_supported: List<String>
@JsonField
acr_values_supported: List<String>
@JsonField
token_endpoint_auth_methods_supported: List<String>
}

View File

@ -196,43 +196,11 @@ class Keyshare {
id: UInt
@JsonField
metadata: String
@JsonField
payloads: String
@JsonField
protocol: String
@JsonField
publicKey: String
data: String
@JsonField
role: Int
@JsonField
version: Int
@JsonField
createdAt: String?
}
class PublicKey {
@PrimaryKey
id: UInt
@JsonField
role: Int
@JsonField
algorithm: Int
@JsonField
encoding: Int
@JsonField
jwk: String
@JsonField
createdAt: String?
}
@ -257,53 +225,3 @@ class Permission {
updatedAt: String?
}
//
// OpenID Connect
//
class DiscoveryDocument {
@JsonField
issuer: String
@JsonField
authorization_endpoint: String
@JsonField
token_endpoint: String
@JsonField
userinfo_endpoint: String
@JsonField
jwks_uri: String
@JsonField
registration_endpoint: String
@JsonField
scopes_supported: List<String>
@JsonField
response_types_supported: List<String>
@JsonField
response_modes_supported: List<String>
@JsonField
subject_types_supported: List<String>
@JsonField
id_token_signing_alg_values_supported: List<String>
@JsonField
claims_supported: List<String>
@JsonField
grant_types_supported: List<String>
@JsonField
acr_values_supported: List<String>
@JsonField
token_endpoint_auth_methods_supported: List<String>
}

View File

@ -1,6 +1,6 @@
@go.Package { name = "github.com/onsonr/sonr/txns" }
@go.Package { name = "github.com/onsonr/sonr/x/did/types/txns" }
module transactions
module txns
import "package://pkg.pkl-lang.org/pkl-go/pkl.golang@0.5.0#/go.pkl"

View File

@ -10,6 +10,17 @@ option go_package = "github.com/onsonr/sonr/x/did/types";
message GenesisState {
// Params defines all the parameters of the module.
Params params = 1 [(gogoproto.nullable) = false];
// GlobalIntegrity defines a zkp integrity proof for the entire DID namespace
GlobalIntegrity global_integrity = 2;
}
// GlobalIntegrity defines a zkp integrity proof for the entire DID namespace
message GlobalIntegrity {
string controller = 1;
string seed = 2;
bytes accumulator = 3;
uint64 count = 4;
}
// Params defines the set of module parameters.
@ -21,23 +32,20 @@ message Params {
// Whitelisted Assets
repeated AssetInfo whitelisted_assets = 1;
// Whitelisted Blockchains
repeated ChainInfo whitelisted_chains = 2;
// Whitelisted Key Types
repeated KeyInfo allowed_public_keys = 3;
map<string, KeyInfo> allowed_public_keys = 2;
// IpfsActive is a flag to enable/disable ipfs
bool ipfs_active = 4;
bool ipfs_active = 3;
// Localhost Registration Enabled
bool localhost_registration_enabled = 5;
bool localhost_registration_enabled = 4;
// ConveyancePreference defines the conveyance preference
string conveyance_preference = 6;
string conveyance_preference = 5;
// AttestationFormats defines the attestation formats
repeated string attestation_formats = 7;
repeated string attestation_formats = 6;
}
// AssetInfo defines the asset info
@ -57,20 +65,8 @@ message AssetInfo {
// The name of the asset
string name = 5;
// The Method of the did namespace
string method = 6;
// The icon url
string icon_url = 7;
}
// ChainInfo defines the chain info
message ChainInfo {
string id = 1;
string chain_id = 2;
string name = 3;
string symbol = 4;
repeated ValidatorInfo validators = 5;
string icon_url = 6;
}
// KeyInfo defines information for accepted PubKey types

View File

@ -97,9 +97,18 @@ message ServiceInfo {
Service service = 4;
}
// Token defines a macron token
message Token {
string id = 1;
string controller = 2;
bytes macron = 3;
// FirstPartyCaveat defines a first party caveat
message FirstPartyCaveat {
Permissions scope = 1;
int64 exp = 2;
string cnf = 3;
string aud = 4;
}
// ThirdPartyCaveat defines a third party caveat
message ThirdPartyCaveat {
Permissions scope = 1;
int64 exp = 2;
string cnf = 3;
string aud = 4;
}

View File

@ -10,8 +10,33 @@ option go_package = "github.com/onsonr/sonr/x/did/types";
// Query provides defines the gRPC querier service.
service Query {
// Params queries all parameters of the module.
rpc Params(QueryRequest) returns (QueryResponse) {
option (google.api.http).get = "/did/params";
rpc Params(QueryRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/params";
}
// ParamsAssets queries all parameters of the module.
rpc ParamsAssets(QueryRequest) returns (QueryResponse) {
option (google.api.http).get = "/params/assets";
}
// Params queries all parameters of the module.
rpc ParamsByAsset(QueryRequest) returns (QueryResponse) {
option (google.api.http).get = "/params/assets/{asset}";
}
// ParamsKeys queries all parameters of the module.
rpc ParamsKeys(QueryRequest) returns (QueryResponse) {
option (google.api.http).get = "/params/keys";
}
// Params queries all parameters of the module.
rpc ParamsByKey(QueryRequest) returns (QueryResponse) {
option (google.api.http).get = "/params/keys/{key}";
}
// Params queries all parameters of the module.
rpc RegistrationOptionsByKey(QueryRequest) returns (QueryResponse) {
option (google.api.http).get = "/params/keys/{key}/registration";
}
// Resolve queries the DID document by its id.
@ -31,6 +56,8 @@ service Query {
message QueryRequest {
string did = 1;
string origin = 2;
string key = 3;
string asset = 4;
}
// QueryResolveResponse is the response type for the Query/Resolve RPC method.
@ -41,3 +68,28 @@ message QueryResponse {
ServiceInfo service = 4;
Params params = 5;
}
// QueryParamsResponse is the response type for the Query/Params RPC method.
message QueryParamsResponse {
Params params = 1;
}
message QueryParamsAssetsResponse {
repeated AssetInfo assets = 1;
}
message QueryParamsKeysResponse {
map<string, KeyInfo> keys = 1;
}
message QueryParamsByKeyResponse {
KeyInfo key = 1;
}
message QueryParamsByAssetResponse {
AssetInfo asset = 1;
}
message QueryRegistrationOptionsByKeyResponse {
repeated string registration_options = 1;
}

View File

@ -48,7 +48,7 @@ message MsgUpdateParams {
Params params = 2 [(gogoproto.nullable) = false];
// token is the macron token to authenticate the operation.
Token token = 3;
string token = 3;
}
// MsgUpdateParamsResponse defines the response structure for executing a
@ -100,7 +100,7 @@ message MsgProveWitness {
bytes witness = 3;
// token is the macron token to authenticate the operation.
Token token = 4;
string token = 4;
}
// MsgProveWitnessResponse is the response type for the ProveWitness RPC.
@ -117,7 +117,7 @@ message MsgSyncController {
string controller = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// Token is the public token to authenticate the operation.
Token token = 3;
string token = 3;
}
// MsgSyncControllerResponse is the response type for the SyncController RPC.
@ -168,13 +168,13 @@ message MsgAuthorizeService {
Permissions scopes = 3;
// token is the macron token to authenticate the operation.
Token token = 4;
string token = 4;
}
// MsgAuthorizeServiceResponse is the response type for the AuthorizeService RPC.
message MsgAuthorizeServiceResponse {
bool success = 1;
Token token = 2;
string token = 2;
}
// MsgRegisterService is the message type for the RegisterService RPC.
@ -188,7 +188,7 @@ message MsgRegisterService {
Service service = 2;
// token is the macron token to authenticate the operation.
Token token = 3;
string token = 3;
}
// MsgRegisterServiceResponse is the response type for the RegisterService RPC.

View File

@ -1,6 +0,0 @@
// Code generated from Pkl module `transactions`. DO NOT EDIT.
package txns
type Msg interface {
GetTypeUrl() string
}

View File

@ -1,27 +0,0 @@
// Code generated from Pkl module `transactions`. DO NOT EDIT.
package txns
import "github.com/apple/pkl-go/pkl"
func init() {
pkl.RegisterMapping("transactions", Transactions{})
pkl.RegisterMapping("transactions#Proposal", Proposal{})
pkl.RegisterMapping("transactions#MsgGovSubmitProposal", MsgGovSubmitProposalImpl{})
pkl.RegisterMapping("transactions#MsgGovVote", MsgGovVoteImpl{})
pkl.RegisterMapping("transactions#MsgGovDeposit", MsgGovDepositImpl{})
pkl.RegisterMapping("transactions#MsgGroupCreateGroup", MsgGroupCreateGroupImpl{})
pkl.RegisterMapping("transactions#MsgGroupSubmitProposal", MsgGroupSubmitProposalImpl{})
pkl.RegisterMapping("transactions#MsgGroupVote", MsgGroupVoteImpl{})
pkl.RegisterMapping("transactions#MsgStakingCreateValidator", MsgStakingCreateValidatorImpl{})
pkl.RegisterMapping("transactions#MsgStakingDelegate", MsgStakingDelegateImpl{})
pkl.RegisterMapping("transactions#MsgStakingUndelegate", MsgStakingUndelegateImpl{})
pkl.RegisterMapping("transactions#MsgStakingBeginRedelegate", MsgStakingBeginRedelegateImpl{})
pkl.RegisterMapping("transactions#MsgDidUpdateParams", MsgDidUpdateParamsImpl{})
pkl.RegisterMapping("transactions#MsgDidAllocateVault", MsgDidAllocateVaultImpl{})
pkl.RegisterMapping("transactions#MsgDidProveWitness", MsgDidProveWitnessImpl{})
pkl.RegisterMapping("transactions#MsgDidSyncVault", MsgDidSyncVaultImpl{})
pkl.RegisterMapping("transactions#MsgDidRegisterController", MsgDidRegisterControllerImpl{})
pkl.RegisterMapping("transactions#MsgDidAuthorize", MsgDidAuthorizeImpl{})
pkl.RegisterMapping("transactions#MsgDidRegisterService", MsgDidRegisterServiceImpl{})
pkl.RegisterMapping("transactions#TxBody", TxBody{})
}

View File

@ -24,7 +24,7 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "UpdateParams",
Skip: false, // set to true if authority gated
Skip: true, // set to true if authority gated
},
},
},

27
x/did/builder/openid.go Normal file
View File

@ -0,0 +1,27 @@
package builder
import (
"fmt"
"github.com/onsonr/sonr/x/did/types/oidc"
)
func GetDiscovery(origin string) *oidc.DiscoveryDocument {
baseURL := "https://" + origin // Ensure this is the correct base URL for your service
discoveryDoc := &oidc.DiscoveryDocument{
Issuer: baseURL,
AuthorizationEndpoint: fmt.Sprintf("%s/auth", baseURL),
TokenEndpoint: fmt.Sprintf("%s/token", baseURL),
UserinfoEndpoint: fmt.Sprintf("%s/userinfo", baseURL),
JwksUri: fmt.Sprintf("%s/jwks", baseURL),
RegistrationEndpoint: fmt.Sprintf("%s/register", baseURL),
ScopesSupported: []string{"openid", "profile", "email", "web3", "sonr"},
ResponseTypesSupported: []string{"code"},
ResponseModesSupported: []string{"query", "form_post"},
GrantTypesSupported: []string{"authorization_code", "refresh_token"},
AcrValuesSupported: []string{"passkey"},
SubjectTypesSupported: []string{"public"},
ClaimsSupported: []string{"sub", "iss", "name", "email"},
}
return discoveryDoc
}

View File

@ -2,6 +2,7 @@ package builder
import (
"crypto/rand"
"strings"
"github.com/onsonr/sonr/x/did/types"
)
@ -53,8 +54,8 @@ func NewCredentialParameter(ki *types.KeyInfo) CredentialParameter {
func ExtractCredentialParameters(p *types.Params) []CredentialParameter {
var keys []*types.KeyInfo
for _, v := range p.AllowedPublicKeys {
if v.Role == types.KeyRole_KEY_ROLE_AUTHENTICATION {
for k, v := range p.AllowedPublicKeys {
if strings.Contains(k, "webauthn") {
keys = append(keys, v)
}
}

View File

@ -1 +0,0 @@
package context

View File

@ -1,55 +0,0 @@
package context
import (
"context"
"fmt"
"net"
"strings"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
)
type ClientInfo struct {
Authority string
ContentType string
UserAgent string
Hostname string
IPAddress string
}
func ExtractClientInfo(ctx context.Context) (*ClientInfo, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, fmt.Errorf("failed to get metadata from context")
}
info := &ClientInfo{}
// Extract authority, content-type, and user-agent
if authority := md.Get("authority"); len(authority) > 0 {
info.Authority = authority[0]
}
if contentType := md.Get("content-type"); len(contentType) > 0 {
info.ContentType = contentType[0]
}
if userAgent := md.Get("user-agent"); len(userAgent) > 0 {
info.UserAgent = userAgent[0]
}
// Extract hostname and IP address
p, ok := peer.FromContext(ctx)
if ok {
if tcpAddr, ok := p.Addr.(*net.TCPAddr); ok {
info.IPAddress = tcpAddr.IP.String()
// Try to get hostname
names, err := net.LookupAddr(info.IPAddress)
if err == nil && len(names) > 0 {
info.Hostname = strings.TrimSuffix(names[0], ".")
}
}
}
return info, nil
}

View File

@ -8,10 +8,12 @@ import (
"cosmossdk.io/core/store"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
nftkeeper "cosmossdk.io/x/nft/keeper"
"github.com/cosmos/cosmos-sdk/codec"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
@ -42,6 +44,7 @@ type ModuleInputs struct {
AddressCodec address.Codec
AccountKeeper authkeeper.AccountKeeper
NFTKeeper nftkeeper.Keeper
StakingKeeper stakingkeeper.Keeper
SlashingKeeper slashingkeeper.Keeper
}
@ -56,8 +59,8 @@ type ModuleOutputs struct {
func ProvideModule(in ModuleInputs) ModuleOutputs {
govAddr := authtypes.NewModuleAddress(govtypes.ModuleName).String()
k := keeper.NewKeeper(in.Cdc, in.StoreService, in.AccountKeeper, &in.StakingKeeper, log.NewLogger(os.Stderr), govAddr)
m := NewAppModule(in.Cdc, k)
k := keeper.NewKeeper(in.Cdc, in.StoreService, in.AccountKeeper, in.NFTKeeper, &in.StakingKeeper, log.NewLogger(os.Stderr), govAddr)
m := NewAppModule(in.Cdc, k, in.NFTKeeper)
return ModuleOutputs{Module: m, Keeper: k, Out: depinject.Out{}}
}

View File

@ -5,16 +5,17 @@ import (
storetypes "cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/orm/model/ormdb"
nftkeeper "cosmossdk.io/x/nft/keeper"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
stakkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/ipfs/kubo/client/rpc"
apiv1 "github.com/onsonr/sonr/api/did/v1"
middleware "github.com/onsonr/sonr/x/did/context"
"github.com/onsonr/sonr/x/did/types"
)
@ -30,6 +31,7 @@ type Keeper struct {
Schema collections.Schema
AccountKeeper authkeeper.AccountKeeper
NftKeeper nftkeeper.Keeper
StakingKeeper *stakkeeper.Keeper
authority string
@ -41,6 +43,7 @@ func NewKeeper(
cdc codec.BinaryCodec,
storeService storetypes.KVStoreService,
accKeeper authkeeper.AccountKeeper,
nftKeeper nftkeeper.Keeper,
stkKeeper *stakkeeper.Keeper,
logger log.Logger,
authority string,
@ -77,6 +80,7 @@ func NewKeeper(
authority: authority,
OrmDB: store,
AccountKeeper: accKeeper,
NftKeeper: nftKeeper,
StakingKeeper: stkKeeper,
}
schema, err := sb.Build()
@ -95,10 +99,7 @@ func (k Keeper) IsUnclaimedServiceOrigin(ctx sdk.Context, origin string) bool {
}
// IsValidServiceOrigin checks if a service origin is valid
func (k Keeper) IsValidServiceOrigin(ctx sdk.Context, origin string, clientInfo *middleware.ClientInfo) bool {
if origin != clientInfo.Hostname {
return false
}
func (k Keeper) IsValidServiceOrigin(ctx sdk.Context, origin string) bool {
rec, err := k.OrmDB.ServiceRecordTable().GetByOrigin(ctx, origin)
if err != nil {
return false

View File

@ -6,6 +6,7 @@ import (
"cosmossdk.io/core/store"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
nftkeeper "cosmossdk.io/x/nft/keeper"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
@ -48,6 +49,7 @@ type testFixture struct {
accountkeeper authkeeper.AccountKeeper
bankkeeper bankkeeper.BaseKeeper
nftKeeper nftkeeper.Keeper
stakingKeeper *stakingkeeper.Keeper
mintkeeper mintkeeper.Keeper
@ -77,10 +79,10 @@ func SetupTest(t *testing.T) *testFixture {
registerBaseSDKModules(f, encCfg, storeService, logger, require)
// Setup POA Keeper.
f.k = keeper.NewKeeper(encCfg.Codec, storeService, f.accountkeeper, f.stakingKeeper, logger, f.govModAddr)
f.k = keeper.NewKeeper(encCfg.Codec, storeService, f.accountkeeper, f.nftKeeper, f.stakingKeeper, logger, f.govModAddr)
f.msgServer = keeper.NewMsgServerImpl(f.k)
f.queryServer = keeper.NewQuerier(f.k)
f.appModule = module.NewAppModule(encCfg.Codec, f.k)
f.appModule = module.NewAppModule(encCfg.Codec, f.k, f.nftKeeper)
return f
}

View File

@ -20,9 +20,9 @@ func NewQuerier(keeper Keeper) Querier {
func (k Querier) Params(
goCtx context.Context,
req *types.QueryRequest,
) (*types.QueryResponse, error) {
) (*types.QueryParamsResponse, error) {
ctx := k.CurrentCtx(goCtx)
return &types.QueryResponse{Params: k.GetParams(ctx.SDK())}, nil
return &types.QueryParamsResponse{Params: k.GetParams(ctx.SDK())}, nil
}
// Resolve implements types.QueryServer.
@ -42,3 +42,38 @@ func (k Querier) Service(
ctx := k.CurrentCtx(goCtx)
return &types.QueryResponse{Service: ctx.GetServiceInfo(req.GetOrigin()), Params: ctx.Params()}, nil
}
// ParamsAssets implements types.QueryServer.
func (k Querier) ParamsAssets(goCtx context.Context, req *types.QueryRequest) (*types.QueryResponse, error) {
// ctx := sdk.UnwrapSDKContext(goCtx)
panic("ParamsAssets is unimplemented")
return &types.QueryResponse{}, nil
}
// ParamsByAsset implements types.QueryServer.
func (k Querier) ParamsByAsset(goCtx context.Context, req *types.QueryRequest) (*types.QueryResponse, error) {
// ctx := sdk.UnwrapSDKContext(goCtx)
panic("ParamsByAsset is unimplemented")
return &types.QueryResponse{}, nil
}
// ParamsKeys implements types.QueryServer.
func (k Querier) ParamsKeys(goCtx context.Context, req *types.QueryRequest) (*types.QueryResponse, error) {
// ctx := sdk.UnwrapSDKContext(goCtx)
panic("ParamsKeys is unimplemented")
return &types.QueryResponse{}, nil
}
// ParamsByKey implements types.QueryServer.
func (k Querier) ParamsByKey(goCtx context.Context, req *types.QueryRequest) (*types.QueryResponse, error) {
// ctx := sdk.UnwrapSDKContext(goCtx)
panic("ParamsByKey is unimplemented")
return &types.QueryResponse{}, nil
}
// RegistrationOptionsByKey implements types.QueryServer.
func (k Querier) RegistrationOptionsByKey(goCtx context.Context, req *types.QueryRequest) (*types.QueryResponse, error) {
// ctx := sdk.UnwrapSDKContext(goCtx)
panic("RegistrationOptionsByKey is unimplemented")
return &types.QueryResponse{}, nil
}

View File

@ -9,7 +9,6 @@ import (
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/onsonr/sonr/x/did/builder"
snrctx "github.com/onsonr/sonr/x/did/context"
"github.com/onsonr/sonr/x/did/types"
)
@ -24,6 +23,8 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{k: keeper}
}
// # AuthorizeService
//
// AuthorizeService implements types.MsgServer.
func (ms msgServer) AuthorizeService(goCtx context.Context, msg *types.MsgAuthorizeService) (*types.MsgAuthorizeServiceResponse, error) {
if ms.k.authority != msg.Controller {
@ -37,19 +38,16 @@ func (ms msgServer) AuthorizeService(goCtx context.Context, msg *types.MsgAuthor
return &types.MsgAuthorizeServiceResponse{}, nil
}
// # AllocateVault
//
// AllocateVault implements types.MsgServer.
func (ms msgServer) AllocateVault(
goCtx context.Context,
msg *types.MsgAllocateVault,
) (*types.MsgAllocateVaultResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
clientInfo, err := snrctx.ExtractClientInfo(goCtx)
if err != nil {
return nil, err
}
// 1.Check if the service origin is valid
if ms.k.IsValidServiceOrigin(ctx, msg.Origin, clientInfo) {
if ms.k.IsValidServiceOrigin(ctx, msg.Origin) {
return nil, types.ErrInvalidServiceOrigin
}
@ -76,6 +74,8 @@ func (ms msgServer) AllocateVault(
}, nil
}
// # RegisterController
//
// RegisterController implements types.MsgServer.
func (ms msgServer) RegisterController(
goCtx context.Context,
@ -85,6 +85,8 @@ func (ms msgServer) RegisterController(
return &types.MsgRegisterControllerResponse{}, nil
}
// # RegisterService
//
// RegisterService implements types.MsgServer.
func (ms msgServer) RegisterService(
goCtx context.Context,
@ -92,24 +94,23 @@ func (ms msgServer) RegisterService(
) (*types.MsgRegisterServiceResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
clientInfo, err := snrctx.ExtractClientInfo(goCtx)
if err != nil {
return nil, err
}
// 1.Check if the service origin is valid
if !ms.k.IsValidServiceOrigin(ctx, msg.Service.Origin, clientInfo) {
if !ms.k.IsValidServiceOrigin(ctx, msg.Service.Origin) {
return nil, types.ErrInvalidServiceOrigin
}
return ms.k.insertService(ctx, msg.Service)
}
// # SyncController
//
// SyncController implements types.MsgServer.
func (ms msgServer) SyncController(ctx context.Context, msg *types.MsgSyncController) (*types.MsgSyncControllerResponse, error) {
// ctx := sdk.UnwrapSDKContext(goCtx)
return &types.MsgSyncControllerResponse{}, nil
}
// # UpdateParams
//
// UpdateParams updates the x/did module parameters.
func (ms msgServer) UpdateParams(
ctx context.Context,

View File

@ -4,13 +4,14 @@ import (
"context"
"encoding/json"
"github.com/gorilla/mux"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
abci "github.com/cometbft/cometbft/abci/types"
"cosmossdk.io/client/v2/autocli"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/nft"
nftkeeper "cosmossdk.io/x/nft/keeper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
@ -24,17 +25,15 @@ import (
)
const (
// ConsensusVersion defines the current x/did module consensus version.
ConsensusVersion = 1
// this line is used by starport scaffolding # simapp/module/const
)
var (
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleGenesis = AppModule{}
_ module.AppModule = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
_ module.AppModuleGenesis = AppModule{}
_ module.AppModule = AppModule{}
_ autocli.HasAutoCLIConfig = AppModule{}
)
@ -46,17 +45,20 @@ type AppModuleBasic struct {
type AppModule struct {
AppModuleBasic
keeper keeper.Keeper
keeper keeper.Keeper
nftKeeper nftkeeper.Keeper
}
// NewAppModule constructor
func NewAppModule(
cdc codec.Codec,
keeper keeper.Keeper,
nftKeeper nftkeeper.Keeper,
) *AppModule {
return &AppModule{
AppModuleBasic: AppModuleBasic{cdc: cdc},
keeper: keeper,
nftKeeper: nftKeeper,
}
}
@ -66,7 +68,8 @@ func (a AppModuleBasic) Name() string {
func (a AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
return cdc.MustMarshalJSON(&types.GenesisState{
Params: types.DefaultParams(),
GlobalIntegrity: types.DefaultGlobalIntegrity(),
Params: types.DefaultParams(),
})
}
@ -82,28 +85,13 @@ func (a AppModuleBasic) ValidateGenesis(marshaler codec.JSONCodec, _ client.TxEn
return nil
}
func (a AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {
}
func (a AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx))
if err != nil {
// same behavior as in cosmos-sdk
panic(err)
}
}
// Disable in favor of autocli.go. If you wish to use these, it will override AutoCLI methods.
/*
func (a AppModuleBasic) GetTxCmd() *cobra.Command {
return cli.NewTxCmd()
}
func (a AppModuleBasic) GetQueryCmd() *cobra.Command {
return cli.GetQueryCmd()
}
*/
func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
types.RegisterLegacyAminoCodec(cdc)
}
@ -113,11 +101,15 @@ func (a AppModuleBasic) RegisterInterfaces(r codectypes.InterfaceRegistry) {
}
func (a AppModule) InitGenesis(ctx sdk.Context, marshaler codec.JSONCodec, message json.RawMessage) []abci.ValidatorUpdate {
genesisState := types.DefaultGenesis()
if err := a.keeper.Params.Set(ctx, genesisState.Params); err != nil {
didGenesisState := types.DefaultGenesis()
if err := a.keeper.Params.Set(ctx, didGenesisState.Params); err != nil {
panic(err)
}
nftGenesisState := nft.DefaultGenesisState()
if err := types.DefaultNFTClasses(nftGenesisState); err != nil {
panic(err)
}
a.nftKeeper.InitGenesis(ctx, nftGenesisState)
return nil
}

View File

@ -1,10 +1,16 @@
package types
import (
"crypto/ecdsa"
"crypto/sha256"
"encoding/hex"
"strings"
fmt "fmt"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"golang.org/x/crypto/sha3"
"github.com/cosmos/btcutil/bech32"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
@ -71,7 +77,11 @@ func (c ChainCode) FormatAddress(pubKey *PubKey) (string, error) {
return bech32.Encode("bc", pubKey.Bytes())
case ChainCodeETH:
return bech32.Encode("eth", pubKey.Bytes())
epk, err := pubKey.ECDSA()
if err != nil {
return "", err
}
return ComputeEthAddress(*epk), nil
case ChainCodeSNR:
return bech32.Encode("idx", pubKey.Bytes())
@ -198,3 +208,30 @@ func ComputeOriginTXTRecord(origin string) string {
h.Write([]byte(origin))
return fmt.Sprintf("v=sonr,o=%s,p=%x", origin, h.Sum(nil))
}
func ComputeEthAddress(pk ecdsa.PublicKey) string {
// Generate Ethereum address
address := ethcrypto.PubkeyToAddress(pk)
// Apply ERC-55 checksum encoding
addr := address.Hex()
addr = strings.ToLower(addr)
addr = strings.TrimPrefix(addr, "0x")
hash := sha3.NewLegacyKeccak256()
hash.Write([]byte(addr))
hashBytes := hash.Sum(nil)
result := "0x"
for i, c := range addr {
if c >= '0' && c <= '9' {
result += string(c)
} else {
if hashBytes[i/2]>>(4-i%2*4)&0xf >= 8 {
result += strings.ToUpper(string(c))
} else {
result += string(c)
}
}
}
return result
}

View File

@ -2,10 +2,11 @@ package types
import (
"encoding/json"
"cosmossdk.io/collections"
fmt "fmt"
ormv1alpha1 "cosmossdk.io/api/cosmos/orm/v1alpha1"
"cosmossdk.io/collections"
"cosmossdk.io/x/nft"
)
// ParamsKey saves the current module params.
@ -35,7 +36,8 @@ const DefaultIndex uint64 = 1
func DefaultGenesis() *GenesisState {
return &GenesisState{
// this line is used by starport scaffolding # genesis/types/default
Params: DefaultParams(),
GlobalIntegrity: DefaultGlobalIntegrity(),
Params: DefaultParams(),
}
}
@ -43,15 +45,24 @@ func DefaultGenesis() *GenesisState {
// failure.
func (gs GenesisState) Validate() error {
// this line is used by starport scaffolding # genesis/types/validate
if gs.GlobalIntegrity == nil {
return fmt.Errorf("global integrity proof is nil")
}
return gs.Params.Validate()
}
// DefaultNFTClasses configures the Initial DIDNamespace NFT classes
func DefaultNFTClasses(nftGenesis *nft.GenesisState) error {
for _, n := range DIDNamespace_value {
nftGenesis.Classes = append(nftGenesis.Classes, DIDNamespace(n).GetNFTClass())
}
return nil
}
// DefaultParams returns default module parameters.
func DefaultParams() Params {
return Params{
WhitelistedAssets: DefaultAssets(),
WhitelistedChains: DefaultChains(),
AllowedPublicKeys: DefaultKeyInfos(),
LocalhostRegistrationEnabled: true,
ConveyancePreference: "direct",
@ -59,6 +70,25 @@ func DefaultParams() Params {
}
}
// DefaultGlobalIntegrity returns the default global integrity proof
func DefaultGlobalIntegrity() *GlobalIntegrity {
return &GlobalIntegrity{
Controller: "did:sonr:0x0",
Seed: DefaultSeedMessage(),
Accumulator: []byte{},
Count: 0,
}
}
// DefaultSeedMessage returns the default seed message
func DefaultSeedMessage() string {
l1 := "The Sonr Network shall make no protocol that respects the establishment of centralized authority,"
l2 := "or prohibits the free exercise of decentralized identity; or abridges the freedom of data sovereignty,"
l3 := "or of encrypted communication; or the right of the users to peaceally interact and transact,"
l4 := "and to petition the Network for the redress of vulnerabilities."
return fmt.Sprintf("%s %s %s %s", l1, l2, l3, l4)
}
// DefaultAssets returns the default asset infos: BTC, ETH, SNR, and USDC
func DefaultAssets() []*AssetInfo {
return []*AssetInfo{
@ -89,17 +119,12 @@ func DefaultAssets() []*AssetInfo {
}
}
// DefaultChains returns the default chain infos: Bitcoin, Ethereum, and Sonr.
func DefaultChains() []*ChainInfo {
return []*ChainInfo{}
}
// DefaultKeyInfos returns the default key infos: secp256k1, ed25519, keccak256, and bls12381.
func DefaultKeyInfos() []*KeyInfo {
return []*KeyInfo{
func DefaultKeyInfos() map[string]*KeyInfo {
return map[string]*KeyInfo{
// Identity Key Info
// Sonr Controller Key Info - From MPC
{
"auth.dwn": {
Role: KeyRole_KEY_ROLE_INVOCATION,
Curve: KeyCurve_KEY_CURVE_P256,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_ECDSA,
@ -108,7 +133,7 @@ func DefaultKeyInfos() []*KeyInfo {
},
// Sonr Vault Shared Key Info - From Registration
{
"auth.zk": {
Role: KeyRole_KEY_ROLE_ASSERTION,
Curve: KeyCurve_KEY_CURVE_BLS12381,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_UNSPECIFIED,
@ -118,7 +143,7 @@ func DefaultKeyInfos() []*KeyInfo {
// Blockchain Key Info
// Ethereum Key Info
{
"auth.ethereum": {
Role: KeyRole_KEY_ROLE_DELEGATION,
Curve: KeyCurve_KEY_CURVE_KECCAK256,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_ECDSA,
@ -126,7 +151,7 @@ func DefaultKeyInfos() []*KeyInfo {
Type: KeyType_KEY_TYPE_BIP32,
},
// Bitcoin/IBC Key Info
{
"auth.bitcoin": {
Role: KeyRole_KEY_ROLE_DELEGATION,
Curve: KeyCurve_KEY_CURVE_SECP256K1,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_ECDSA,
@ -136,7 +161,7 @@ func DefaultKeyInfos() []*KeyInfo {
// Authentication Key Info
// Browser based WebAuthn
{
"webauthn.browser": {
Role: KeyRole_KEY_ROLE_AUTHENTICATION,
Curve: KeyCurve_KEY_CURVE_P256,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_ES256,
@ -144,7 +169,7 @@ func DefaultKeyInfos() []*KeyInfo {
Type: KeyType_KEY_TYPE_WEBAUTHN,
},
// FIDO U2F
{
"webauthn.fido": {
Role: KeyRole_KEY_ROLE_AUTHENTICATION,
Curve: KeyCurve_KEY_CURVE_P256,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_ES256,
@ -152,7 +177,7 @@ func DefaultKeyInfos() []*KeyInfo {
Type: KeyType_KEY_TYPE_WEBAUTHN,
},
// Cross-Platform Passkeys
{
"webauthn.passkey": {
Role: KeyRole_KEY_ROLE_AUTHENTICATION,
Curve: KeyCurve_KEY_CURVE_ED25519,
Algorithm: KeyAlgorithm_KEY_ALGORITHM_EDDSA,
@ -195,14 +220,6 @@ func (a *AssetInfo) Equal(b *AssetInfo) bool {
return false
}
// Equal returns true if two chain infos are equal
func (c *ChainInfo) Equal(b *ChainInfo) bool {
if c == nil && b == nil {
return true
}
return false
}
// Equal returns true if two key infos are equal
func (k *KeyInfo) Equal(b *KeyInfo) bool {
if k == nil && b == nil {

File diff suppressed because it is too large Load Diff

27
x/did/types/integrity.go Normal file
View File

@ -0,0 +1,27 @@
package types
import "lukechampine.com/blake3"
func (g *GlobalIntegrity) Update(address string) bool {
return true
}
func (g *GlobalIntegrity) getProof() (*Proof, error) {
if g.Accumulator == nil {
return NewProof(g.Controller, g.proofProperty(), g.seedKdf())
}
return &Proof{
Issuer: g.Controller,
Property: g.proofProperty(),
Accumulator: g.Accumulator,
}, nil
}
func (g *GlobalIntegrity) proofProperty() string {
return "did:sonr:integrity"
}
func (g *GlobalIntegrity) seedKdf() []byte {
res := blake3.Sum256([]byte(g.Seed))
return res[:]
}

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