refactor: bring external packages home and restructure
- Imported packages from separate repos: - github.com/Snider/config -> pkg/config - github.com/Snider/display -> pkg/display - github.com/Snider/help -> pkg/help - github.com/Snider/i18n -> pkg/i18n - github.com/Snider/updater -> pkg/updater - Moved core code from root to pkg/core - Flattened nested package structures - Updated all import paths to github.com/Snider/Core/pkg/* - Added Display interface to Core - Updated go.work for workspace modules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
60c0d4f6c7
commit
4e02d5bc97
239 changed files with 65949 additions and 63 deletions
|
|
@ -1,9 +0,0 @@
|
|||
package core
|
||||
|
||||
// ActionServiceStartup is a message sent when the application's services are starting up.
|
||||
// This provides a hook for services to perform initialization tasks.
|
||||
type ActionServiceStartup struct{}
|
||||
|
||||
// ActionServiceShutdown is a message sent when the application is shutting down.
|
||||
// This allows services to perform cleanup tasks, such as saving state or closing resources.
|
||||
type ActionServiceShutdown struct{}
|
||||
|
|
@ -2,10 +2,7 @@ module core-gui
|
|||
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/Snider/Core v0.0.0-20251024151010-ccfd407949f1
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41
|
||||
)
|
||||
require github.com/wailsapp/wails/v3 v3.0.0-alpha.41
|
||||
|
||||
replace github.com/Snider/Core => ../../
|
||||
|
||||
|
|
|
|||
8
go.work
8
go.work
|
|
@ -3,5 +3,11 @@ go 1.25
|
|||
use (
|
||||
.
|
||||
./cmd/core-gui
|
||||
cmd/examples/core-static-di
|
||||
./cmd/examples/core-static-di
|
||||
./pkg/config
|
||||
./pkg/core
|
||||
./pkg/display
|
||||
./pkg/help
|
||||
./pkg/i18n
|
||||
./pkg/updater
|
||||
)
|
||||
|
|
|
|||
44
go.work.sum
44
go.work.sum
|
|
@ -6,6 +6,8 @@ atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
|
|||
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
|
||||
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
|
||||
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
|
||||
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
|
|
@ -16,6 +18,8 @@ github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
|
|||
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg=
|
||||
|
|
@ -108,8 +112,7 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn
|
|||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
|
||||
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
|
|
@ -140,6 +143,10 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
|
|||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ=
|
||||
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||
|
|
@ -168,8 +175,6 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
|
|||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jackmordaunt/icns/v2 v2.2.7 h1:K/RbfvuzjmjVY5y4g+XENRs8ZZatwz4YnLHypa2KwQg=
|
||||
|
|
@ -197,7 +202,6 @@ github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kK
|
|||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/clir v1.7.0 h1:xiAnhl7ryPwuH3ERwPWZp/pCHk8wTeiwuAOt6MiNyAw=
|
||||
|
|
@ -227,6 +231,8 @@ github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A=
|
|||
github.com/mattn/go-zglob v0.0.6/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
|
|
@ -265,7 +271,6 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
|
|||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
|
||||
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
|
||||
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
|
||||
|
|
@ -282,9 +287,7 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
|||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY=
|
||||
github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
|
|
@ -296,6 +299,8 @@ github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0
|
|||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
|
||||
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
|
||||
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
|
|
@ -315,14 +320,9 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
|||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
|
|
@ -338,6 +338,8 @@ github.com/tc-hib/winres v0.3.1 h1:CwRjEGrKdbi5CvZ4ID+iyVhgyfatxFoizjPhzez9Io4=
|
|||
github.com/tc-hib/winres v0.3.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/wailsapp/task/v3 v3.40.1-patched3 h1:i6O1WNdSur9CGaiMDIYGjsmj/qS4465zqv+WEs6sPRs=
|
||||
github.com/wailsapp/task/v3 v3.40.1-patched3/go.mod h1:jIP48r8ftoSQNlxFP4+aEnkvGQqQXqCnRi/B7ROaecE=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
|
|
@ -363,7 +365,8 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
|||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.11.1-0.20230711161743-2e82bdd1719d/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
|
|
@ -382,8 +385,8 @@ golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
|||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
|
|
@ -399,8 +402,9 @@ golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
|||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -420,12 +424,13 @@ golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 h1:dHQOQddU4YHS5gY33/6
|
|||
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
|
||||
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU=
|
||||
golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
|
||||
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo=
|
||||
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
|
|
@ -443,6 +448,8 @@ golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
|||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
|
|
@ -450,7 +457,6 @@ google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh
|
|||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||
|
|
|
|||
33
pkg/config/.github/workflows/ci.yml
vendored
Normal file
33
pkg/config/.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: Go CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.25'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
||||
|
||||
- name: Test
|
||||
run: go test -v -coverprofile=coverage.out ./...
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.out
|
||||
verbose: true
|
||||
24
pkg/config/.github/workflows/release.yml
vendored
Normal file
24
pkg/config/.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
18
pkg/config/.gitignore
vendored
Normal file
18
pkg/config/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Go
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.test
|
||||
*.out
|
||||
*.prof
|
||||
demo-cli
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
43
pkg/config/.goreleaser.yaml
Normal file
43
pkg/config/.goreleaser.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
archives:
|
||||
- replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
brews:
|
||||
- name: config
|
||||
tap:
|
||||
owner: Snider
|
||||
name: homebrew-tap
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: goreleaser@carlosbecker.com
|
||||
homepage: "https://github.com/Snider/config"
|
||||
description: "Config module for the Core Framework"
|
||||
license: "EUPL-1.2"
|
||||
287
pkg/config/LICENSE
Normal file
287
pkg/config/LICENSE
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
|
||||
below) which is provided under the terms of this Licence. Any use of the Work,
|
||||
other than as authorised under this Licence is prohibited (to the extent such
|
||||
use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This Licence
|
||||
does not define the extent of modification or dependence on the Original Work
|
||||
required in order to classify a work as a Derivative Work; this extent is
|
||||
determined by copyright law applicable in the country mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which is
|
||||
meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright vested
|
||||
in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case may
|
||||
be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make effective
|
||||
the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
|
||||
any patents held by the Licensor, to the extent necessary to make use of the
|
||||
rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates, in
|
||||
a notice following the copyright notice attached to the Work, a repository where
|
||||
the Source Code is easily and freely accessible for as long as the Licensor
|
||||
continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from
|
||||
any exception or limitation to the exclusive rights of the rights owners in the
|
||||
Work, of the exhaustion of those rights or of other applicable limitations
|
||||
thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and a
|
||||
copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of the
|
||||
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions on
|
||||
the Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed under
|
||||
a Compatible Licence, this Distribution or Communication can be done under the
|
||||
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
|
||||
Licence’ refers to the licences listed in the appendix attached to this Licence.
|
||||
Should the Licensee's obligations under the Compatible Licence conflict with
|
||||
his/her obligations under this Licence, the obligations of the Compatible
|
||||
Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the Work,
|
||||
the Licensee will provide a machine-readable copy of the Source Code or indicate
|
||||
a repository where this Source will be easily and freely available for as long
|
||||
as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings
|
||||
to the Work are owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’ basis
|
||||
and without warranties of any kind concerning the Work, including without
|
||||
limitation merchantability, fitness for a particular purpose, absence of defects
|
||||
or errors, accuracy, non-infringement of intellectual property rights other than
|
||||
copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition
|
||||
for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the use
|
||||
of the Work, including without limitation, damages for loss of goodwill, work
|
||||
stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such damage.
|
||||
However, the Licensor will be liable under statutory product liability laws as
|
||||
far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional agreement,
|
||||
defining obligations or services consistent with this Licence. However, if
|
||||
accepting obligations, You may act only on your own behalf and on your sole
|
||||
responsibility, not on behalf of the original Licensor or any other Contributor,
|
||||
and only if You agree to indemnify, defend, and hold each Contributor harmless
|
||||
for any liability incurred by, or claims asserted against such Contributor by
|
||||
the fact You have accepted any warranty or additional liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
|
||||
placed under the bottom of a window displaying the text of this Licence or by
|
||||
affirming consent in any other similar way, in accordance with the rules of
|
||||
applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this Licence,
|
||||
such as the use of the Work, the creation by You of a Derivative Work or the
|
||||
Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of electronic
|
||||
communication by You (for example, by offering to download the Work from a
|
||||
remote location) the distribution channel or media (for example, a website) must
|
||||
at least provide to the public the information requested by the applicable law
|
||||
regarding the Licensor, the Licence and the way it may be accessible, concluded,
|
||||
stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions of
|
||||
this Licence or updated versions of the Appendix, so far this is required and
|
||||
reasonable, without reducing the scope of the rights granted by the Licence. New
|
||||
versions of the Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty on
|
||||
the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive jurisdiction
|
||||
of the competent court where the Licensor resides or conducts its primary
|
||||
business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the above
|
||||
licences without producing a new version of the EUPL, as long as they provide
|
||||
the rights granted in Article 2 of this Licence and protect the covered Source
|
||||
Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a new
|
||||
EUPL version.
|
||||
166
pkg/config/README.md
Normal file
166
pkg/config/README.md
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
# Config Module
|
||||
|
||||
[](https://github.com/Snider/config/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/Snider/config)
|
||||
|
||||
This repository is a config module for the Core Framework. It includes a Go backend, an Angular custom element, and a full release cycle configuration.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://github.com/Snider/config.git
|
||||
```
|
||||
|
||||
2. **Install the dependencies:**
|
||||
```bash
|
||||
cd config
|
||||
go mod tidy
|
||||
cd ui
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Run the development server:**
|
||||
```bash
|
||||
go run ./cmd/demo-cli serve
|
||||
```
|
||||
This will start the Go backend and serve the Angular custom element.
|
||||
|
||||
## Building the Custom Element
|
||||
|
||||
To build the Angular custom element, run the following command:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm run build
|
||||
```
|
||||
|
||||
This will create a single JavaScript file in the `dist` directory that you can use in any HTML page.
|
||||
|
||||
## Usage
|
||||
|
||||
The `config` service provides a generic way to load and save configuration files in various formats. This is useful for other packages that need to persist their own configuration without being tied to a specific format.
|
||||
|
||||
### Supported Formats
|
||||
|
||||
* JSON (`.json`)
|
||||
* YAML (`.yml`, `.yaml`)
|
||||
* INI (`.ini`)
|
||||
* XML (`.xml`)
|
||||
|
||||
### Saving Configuration
|
||||
|
||||
To save a set of key-value pairs, you can use the `SaveKeyValues` method. The format is determined by the file extension of the `key` you provide.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/Snider/config/pkg/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get a new config service instance
|
||||
configSvc, err := config.New()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create config service: %v", err)
|
||||
}
|
||||
|
||||
// Example data to save
|
||||
data := map[string]interface{}{
|
||||
"setting1": "value1",
|
||||
"enabled": true,
|
||||
"retries": 3,
|
||||
}
|
||||
|
||||
// Save as a JSON file
|
||||
if err := configSvc.SaveKeyValues("my-app-settings.json", data); err != nil {
|
||||
log.Fatalf("Failed to save JSON config: %v", err)
|
||||
}
|
||||
|
||||
// Save as a YAML file
|
||||
if err := configSvc.SaveKeyValues("my-app-settings.yaml", data); err != nil {
|
||||
log.Fatalf("Failed to save YAML config: %v", err)
|
||||
}
|
||||
|
||||
// For INI, keys are typically in `section.key` format
|
||||
iniData := map[string]interface{}{
|
||||
"general.setting1": "value1",
|
||||
"general.enabled": true,
|
||||
"network.retries": 3,
|
||||
}
|
||||
if err := configSvc.SaveKeyValues("my-app-settings.ini", iniData); err != nil {
|
||||
log.Fatalf("Failed to save INI config: %v", err)
|
||||
}
|
||||
|
||||
// Save as an XML file
|
||||
if err := configSvc.SaveKeyValues("my-app-settings.xml", data); err != nil {
|
||||
log.Fatalf("Failed to save XML config: %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Loading Configuration
|
||||
|
||||
To load a configuration file, use the `LoadKeyValues` method. It will automatically parse the file based on its extension and return a `map[string]interface{}`.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/Snider/config/pkg/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get a new config service instance
|
||||
configSvc, err := config.New()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create config service: %v", err)
|
||||
}
|
||||
|
||||
// Load a JSON file
|
||||
jsonData, err := configSvc.LoadKeyValues("my-app-settings.json")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load JSON config: %v", err)
|
||||
}
|
||||
fmt.Printf("Loaded from JSON: %v\n", jsonData)
|
||||
// Note: Numbers from JSON are unmarshaled as float64
|
||||
|
||||
// Load a YAML file
|
||||
yamlData, err := configSvc.LoadKeyValues("my-app-settings.yaml")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load YAML config: %v", err)
|
||||
}
|
||||
fmt.Printf("Loaded from YAML: %v\n", yamlData)
|
||||
// Note: Numbers from YAML without decimals are unmarshaled as int
|
||||
|
||||
// Load an INI file
|
||||
iniData, err := configSvc.LoadKeyValues("my-app-settings.ini")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load INI config: %v", err)
|
||||
}
|
||||
fmt.Printf("Loaded from INI: %v\n", iniData)
|
||||
// Note: All values from INI are loaded as strings
|
||||
|
||||
// Load an XML file
|
||||
xmlData, err := configSvc.LoadKeyValues("my-app-settings.xml")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load XML config: %v", err)
|
||||
}
|
||||
fmt.Printf("Loaded from XML: %v\n", xmlData)
|
||||
// Note: All values from XML are loaded as strings
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the EUPL-1.2 License - see the [LICENSE](LICENSE) file for details.
|
||||
328
pkg/config/config.go
Normal file
328
pkg/config/config.go
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
// Package config provides a configuration management service that handles
|
||||
// loading, saving, and accessing application settings. It supports both a
|
||||
// main JSON configuration file and auxiliary data stored in various formats
|
||||
// like YAML, INI, and XML. The service is designed to be extensible and
|
||||
// can be used with static or dynamic dependency injection.
|
||||
//
|
||||
// The Service struct is the core of the package, providing methods to
|
||||
// interact with the configuration. It manages file paths, default values,
|
||||
// and the serialization/deserialization of data.
|
||||
//
|
||||
// Basic usage involves creating a new Service instance and then using its
|
||||
// methods to get, set, and manage configuration data.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Create a new config service.
|
||||
// cfg, err := config.New()
|
||||
// if err != nil {
|
||||
// log.Fatalf("failed to create config service: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Set a new value.
|
||||
// err = cfg.Set("language", "fr")
|
||||
// if err != nil {
|
||||
// log.Fatalf("failed to set config value: %v", err)
|
||||
// }
|
||||
//
|
||||
// // Retrieve a value.
|
||||
// var lang string
|
||||
// err = cfg.Get("language", &lang)
|
||||
// if err != nil {
|
||||
// log.Fatalf("failed to get config value: %v", err)
|
||||
// }
|
||||
// fmt.Printf("Language: %s\n", lang)
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
"github.com/adrg/xdg"
|
||||
)
|
||||
|
||||
const appName = "lethean"
|
||||
const configFileName = "config.json"
|
||||
|
||||
// Options holds configuration for the config service. This struct is provided
|
||||
// for future extensibility and currently has no fields.
|
||||
type Options struct{}
|
||||
|
||||
// Service provides access to the application's configuration.
|
||||
// It handles loading, saving, and providing access to configuration values,
|
||||
// abstracting away the details of file I/O and data serialization.
|
||||
// The Service is designed to be a central point for all configuration-related
|
||||
// operations within the application.
|
||||
//
|
||||
// The fields of the Service struct are automatically saved to and loaded from
|
||||
// a JSON configuration file. The `json:"-"` tag on ServiceRuntime prevents
|
||||
// it from being serialized.
|
||||
type Service struct {
|
||||
*core.ServiceRuntime[Options] `json:"-"`
|
||||
|
||||
// Persistent fields, saved to config.json.
|
||||
ConfigPath string `json:"configPath,omitempty"`
|
||||
UserHomeDir string `json:"userHomeDir,omitempty"`
|
||||
RootDir string `json:"rootDir,omitempty"`
|
||||
CacheDir string `json:"cacheDir,omitempty"`
|
||||
ConfigDir string `json:"configDir,omitempty"`
|
||||
DataDir string `json:"dataDir,omitempty"`
|
||||
WorkspaceDir string `json:"workspaceDir,omitempty"`
|
||||
DefaultRoute string `json:"default_route"`
|
||||
Features []string `json:"features"`
|
||||
Language string `json:"language"`
|
||||
}
|
||||
|
||||
// createServiceInstance handles the setup of the configuration service. It
|
||||
// resolves necessary paths, creates directories, and loads the configuration
|
||||
// file if it exists. If the configuration file is not found, it creates a new
|
||||
// one with default values. This function is not exported and is used internally
|
||||
// by the New and Register constructors.
|
||||
func createServiceInstance() (*Service, error) {
|
||||
// --- Path and Directory Setup ---
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve user home directory: %w", err)
|
||||
}
|
||||
userHomeDir := filepath.Join(homeDir, appName)
|
||||
|
||||
rootDir, err := xdg.DataFile(appName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve data directory: %w", err)
|
||||
}
|
||||
|
||||
cacheDir, err := xdg.CacheFile(appName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not resolve cache directory: %w", err)
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
UserHomeDir: userHomeDir,
|
||||
RootDir: rootDir,
|
||||
CacheDir: cacheDir,
|
||||
ConfigDir: filepath.Join(userHomeDir, "config"),
|
||||
DataDir: filepath.Join(userHomeDir, "data"),
|
||||
WorkspaceDir: filepath.Join(userHomeDir, "workspace"),
|
||||
DefaultRoute: "/",
|
||||
Features: []string{},
|
||||
Language: "en",
|
||||
}
|
||||
s.ConfigPath = filepath.Join(s.ConfigDir, configFileName)
|
||||
|
||||
dirs := []string{s.RootDir, s.ConfigDir, s.DataDir, s.CacheDir, s.WorkspaceDir, s.UserHomeDir}
|
||||
for _, dir := range dirs {
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return nil, fmt.Errorf("could not create directory %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Load or Create Configuration ---
|
||||
if data, err := os.ReadFile(s.ConfigPath); err == nil {
|
||||
// Config file exists, load it.
|
||||
if err := json.Unmarshal(data, s); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
} else if os.IsNotExist(err) {
|
||||
// Config file does not exist, create it with default values.
|
||||
if err := s.Save(); err != nil {
|
||||
return nil, fmt.Errorf("failed to create default config file: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Another error occurred reading the file.
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// New creates a new instance of the configuration service. This constructor is
|
||||
// intended for static dependency injection, where the service is created and
|
||||
// managed manually. It initializes the service with default paths and values,
|
||||
// and loads any existing configuration from disk.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// cfg, err := config.New()
|
||||
// if err != nil {
|
||||
// log.Fatalf("Failed to initialize config: %v", err)
|
||||
// }
|
||||
// // Use cfg to access configuration settings.
|
||||
func New() (*Service, error) {
|
||||
return createServiceInstance()
|
||||
}
|
||||
|
||||
// Register creates a new instance of the configuration service and registers it
|
||||
// with the application's core. This constructor is intended for dynamic
|
||||
// dependency injection, where services are managed by a central core component.
|
||||
// It performs the same initialization as New, but also integrates the service
|
||||
// with the provided core instance.
|
||||
func Register(c *core.Core) (any, error) {
|
||||
s, err := createServiceInstance()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Defensive check: createServiceInstance should not return nil service with nil error
|
||||
if s == nil {
|
||||
return nil, errors.New("config: createServiceInstance returned a nil service instance with no error")
|
||||
}
|
||||
s.ServiceRuntime = core.NewServiceRuntime(c, Options{})
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Save writes the current configuration to a JSON file. The location of the file
|
||||
// is determined by the ConfigPath field of the Service struct. This method is
|
||||
// typically called automatically by Set, but can be used to explicitly save
|
||||
// changes.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// err := cfg.Save()
|
||||
// if err != nil {
|
||||
// log.Printf("Error saving configuration: %v", err)
|
||||
// }
|
||||
func (s *Service) Save() error {
|
||||
data, err := json.MarshalIndent(s, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(s.ConfigPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a configuration value by its key. The key corresponds to the
|
||||
// JSON tag of a field in the Service struct. The retrieved value is stored in
|
||||
// the `out` parameter, which must be a non-nil pointer to a variable of the
|
||||
// correct type.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var currentLanguage string
|
||||
// err := cfg.Get("language", ¤tLanguage)
|
||||
// if err != nil {
|
||||
// log.Printf("Could not retrieve language setting: %v", err)
|
||||
// }
|
||||
// fmt.Println("Current language is:", currentLanguage)
|
||||
func (s *Service) Get(key string, out any) error {
|
||||
val := reflect.ValueOf(s).Elem()
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag != "" && jsonTag != "-" {
|
||||
jsonName := strings.Split(jsonTag, ",")[0]
|
||||
if strings.EqualFold(jsonName, key) {
|
||||
outVal := reflect.ValueOf(out)
|
||||
if outVal.Kind() != reflect.Ptr || outVal.IsNil() {
|
||||
return errors.New("output argument must be a non-nil pointer")
|
||||
}
|
||||
targetVal := outVal.Elem()
|
||||
srcVal := val.Field(i)
|
||||
|
||||
if !srcVal.Type().AssignableTo(targetVal.Type()) {
|
||||
return fmt.Errorf("cannot assign config value of type %s to output of type %s", srcVal.Type(), targetVal.Type())
|
||||
}
|
||||
targetVal.Set(srcVal)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("key '%s' not found in config", key)
|
||||
}
|
||||
|
||||
// SaveStruct saves an arbitrary struct to a JSON file in the config directory.
|
||||
// This is useful for storing complex data that is not part of the main
|
||||
// configuration. The `key` parameter is used as the filename (with a .json
|
||||
// extension).
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type UserPreferences struct {
|
||||
// Theme string `json:"theme"`
|
||||
// Notifications bool `json:"notifications"`
|
||||
// }
|
||||
// prefs := UserPreferences{Theme: "dark", Notifications: true}
|
||||
// err := cfg.SaveStruct("user_prefs", prefs)
|
||||
// if err != nil {
|
||||
// log.Printf("Error saving user preferences: %v", err)
|
||||
// }
|
||||
func (s *Service) SaveStruct(key string, data interface{}) error {
|
||||
filePath := filepath.Join(s.ConfigDir, key+".json")
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal struct for key '%s': %w", key, err)
|
||||
}
|
||||
return os.WriteFile(filePath, jsonData, 0644)
|
||||
}
|
||||
|
||||
// LoadStruct loads an arbitrary struct from a JSON file in the config directory.
|
||||
// The `key` parameter specifies the filename (without the .json extension). The
|
||||
// loaded data is unmarshaled into the `data` parameter, which must be a
|
||||
// non-nil pointer to a struct.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var prefs UserPreferences
|
||||
// err := cfg.LoadStruct("user_prefs", &prefs)
|
||||
// if err != nil {
|
||||
// log.Printf("Error loading user preferences: %v", err)
|
||||
// }
|
||||
// fmt.Printf("User theme is: %s", prefs.Theme)
|
||||
func (s *Service) LoadStruct(key string, data interface{}) error {
|
||||
filePath := filepath.Join(s.ConfigDir, key+".json")
|
||||
jsonData, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil // Return nil if the file doesn't exist
|
||||
}
|
||||
return fmt.Errorf("failed to read struct file for key '%s': %w", key, err)
|
||||
}
|
||||
return json.Unmarshal(jsonData, data)
|
||||
}
|
||||
|
||||
// Set updates a configuration value and saves the change to the configuration
|
||||
// file. The key corresponds to the JSON tag of a field in the Service struct.
|
||||
// The provided value `v` must be of a type that is assignable to the field.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// err := cfg.Set("default_route", "/home")
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to set default route: %v", err)
|
||||
// }
|
||||
func (s *Service) Set(key string, v any) error {
|
||||
val := reflect.ValueOf(s).Elem()
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
jsonTag := field.Tag.Get("json")
|
||||
if jsonTag != "" && jsonTag != "-" {
|
||||
jsonName := strings.Split(jsonTag, ",")[0]
|
||||
if strings.EqualFold(jsonName, key) {
|
||||
fieldVal := val.Field(i)
|
||||
if !fieldVal.CanSet() {
|
||||
return fmt.Errorf("cannot set config field for key '%s'", key)
|
||||
}
|
||||
newVal := reflect.ValueOf(v)
|
||||
if !newVal.Type().AssignableTo(fieldVal.Type()) {
|
||||
return fmt.Errorf("type mismatch for key '%s': expected %s, got %s", key, fieldVal.Type(), newVal.Type())
|
||||
}
|
||||
fieldVal.Set(newVal)
|
||||
return s.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("key '%s' not found in config", key)
|
||||
}
|
||||
349
pkg/config/config_test.go
Normal file
349
pkg/config/config_test.go
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/Snider/Core/pkg/core"
|
||||
)
|
||||
|
||||
// setupTestEnv creates a temporary home directory for testing and ensures a clean environment.
|
||||
func setupTestEnv(t *testing.T) (string, func()) {
|
||||
tempHomeDir, err := os.MkdirTemp("", "test_home_*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp home directory: %v", err)
|
||||
}
|
||||
|
||||
oldHome := os.Getenv("HOME")
|
||||
os.Setenv("HOME", tempHomeDir)
|
||||
|
||||
// Unset XDG vars to ensure HOME is used for path resolution, creating a hermetic test.
|
||||
oldXdgData := os.Getenv("XDG_DATA_HOME")
|
||||
oldXdgCache := os.Getenv("XDG_CACHE_HOME")
|
||||
os.Unsetenv("XDG_DATA_HOME")
|
||||
os.Unsetenv("XDG_CACHE_HOME")
|
||||
|
||||
cleanup := func() {
|
||||
os.Setenv("HOME", oldHome)
|
||||
os.Setenv("XDG_DATA_HOME", oldXdgData)
|
||||
os.Setenv("XDG_CACHE_HOME", oldXdgCache)
|
||||
os.RemoveAll(tempHomeDir)
|
||||
}
|
||||
|
||||
return tempHomeDir, cleanup
|
||||
}
|
||||
|
||||
// newTestCore creates a new, empty core instance for testing.
|
||||
func newTestCore(t *testing.T) *core.Core {
|
||||
c, err := core.New()
|
||||
if err != nil {
|
||||
t.Fatalf("core.New() failed: %v", err)
|
||||
}
|
||||
if c == nil {
|
||||
t.Fatalf("core.New() returned a nil instance")
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func TestConfigServiceGood(t *testing.T) {
|
||||
t.Run("New service creates default config", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
serviceInstance, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
// Check that the config file was created
|
||||
if _, err := os.Stat(serviceInstance.ConfigPath); os.IsNotExist(err) {
|
||||
t.Errorf("config.json was not created at %s", serviceInstance.ConfigPath)
|
||||
}
|
||||
|
||||
// Check default values
|
||||
if serviceInstance.Language != "en" {
|
||||
t.Errorf("Expected default language 'en', got '%s'", serviceInstance.Language)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New service loads existing config", func(t *testing.T) {
|
||||
tempHomeDir, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
// Manually create a config file with non-default values
|
||||
configDir := filepath.Join(tempHomeDir, appName, "config")
|
||||
if err := os.MkdirAll(configDir, os.ModePerm); err != nil {
|
||||
t.Fatalf("Failed to create test config dir: %v", err)
|
||||
}
|
||||
configPath := filepath.Join(configDir, configFileName)
|
||||
|
||||
customConfig := `{"language": "fr", "features": ["beta-testing"]}`
|
||||
if err := os.WriteFile(configPath, []byte(customConfig), 0644); err != nil {
|
||||
t.Fatalf("Failed to write custom config file: %v", err)
|
||||
}
|
||||
|
||||
serviceInstance, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed while loading existing config: %v", err)
|
||||
}
|
||||
|
||||
if serviceInstance.Language != "fr" {
|
||||
t.Errorf("Expected language 'fr', got '%s'", serviceInstance.Language)
|
||||
}
|
||||
// A check for IsFeatureEnabled would require a proper core instance and service registration.
|
||||
// This is a simplified check for now.
|
||||
found := false
|
||||
for _, f := range serviceInstance.Features {
|
||||
if f == "beta-testing" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected 'beta-testing' feature to be enabled")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Set and Get", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
key := "language"
|
||||
expectedValue := "de"
|
||||
if err := s.Set(key, expectedValue); err != nil {
|
||||
t.Fatalf("Set() failed: %v", err)
|
||||
}
|
||||
|
||||
var actualValue string
|
||||
if err := s.Get(key, &actualValue); err != nil {
|
||||
t.Fatalf("Get() failed: %v", err)
|
||||
}
|
||||
|
||||
if actualValue != expectedValue {
|
||||
t.Errorf("Expected value '%s', got '%s'", expectedValue, actualValue)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Save and Load Struct", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
type CustomConfig struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Timeout int `json:"timeout"`
|
||||
}
|
||||
|
||||
key := "custom"
|
||||
expectedConfig := CustomConfig{
|
||||
APIKey: "12345",
|
||||
Timeout: 30,
|
||||
}
|
||||
|
||||
if err := s.SaveStruct(key, expectedConfig); err != nil {
|
||||
t.Fatalf("SaveStruct() failed: %v", err)
|
||||
}
|
||||
|
||||
var actualConfig CustomConfig
|
||||
if err := s.LoadStruct(key, &actualConfig); err != nil {
|
||||
t.Fatalf("LoadStruct() failed: %v", err)
|
||||
}
|
||||
|
||||
if actualConfig.APIKey != expectedConfig.APIKey {
|
||||
t.Errorf("Expected APIKey '%s', got '%s'", expectedConfig.APIKey, actualConfig.APIKey)
|
||||
}
|
||||
if actualConfig.Timeout != expectedConfig.Timeout {
|
||||
t.Errorf("Expected Timeout '%d', got '%d'", expectedConfig.Timeout, actualConfig.Timeout)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigServiceUgly(t *testing.T) {
|
||||
t.Run("LoadStruct with nil value", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
key := "nil-value"
|
||||
filePath := filepath.Join(s.ConfigDir, key+".json")
|
||||
if err := os.WriteFile(filePath, []byte("null"), 0644); err != nil {
|
||||
t.Fatalf("Failed to write nil value file: %v", err)
|
||||
}
|
||||
|
||||
type CustomConfig struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Timeout int `json:"timeout"`
|
||||
}
|
||||
|
||||
var actualConfig CustomConfig
|
||||
err = s.LoadStruct(key, &actualConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadStruct() should not have failed with a nil value, but it did: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Concurrent access", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
// Run concurrent Set and Get operations
|
||||
done := make(chan bool)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
s.Set("language", "en")
|
||||
done <- true
|
||||
}()
|
||||
go func() {
|
||||
var lang string
|
||||
s.Get("language", &lang)
|
||||
done <- true
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
<-done
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigServiceBad(t *testing.T) {
|
||||
t.Run("Load non-existent struct", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
type CustomConfig struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Timeout int `json:"timeout"`
|
||||
}
|
||||
|
||||
var actualConfig CustomConfig
|
||||
if err := s.LoadStruct("non-existent", &actualConfig); err != nil {
|
||||
t.Fatalf("LoadStruct() failed: %v", err)
|
||||
}
|
||||
|
||||
// Expect the struct to be zero-valued
|
||||
if actualConfig.APIKey != "" {
|
||||
t.Errorf("Expected empty APIKey, got '%s'", actualConfig.APIKey)
|
||||
}
|
||||
if actualConfig.Timeout != 0 {
|
||||
t.Errorf("Expected zero Timeout, got '%d'", actualConfig.Timeout)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Get non-existent key", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
var value string
|
||||
err = s.Get("non-existent", &value)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error for non-existent key, but got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Set non-existent key", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
err = s.Set("non-existent", "value")
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error for non-existent key, but got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SaveStruct with unmarshallable type", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
err = s.SaveStruct("test", make(chan int))
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error for unmarshallable type, but got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LoadStruct with invalid JSON", func(t *testing.T) {
|
||||
_, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() failed: %v", err)
|
||||
}
|
||||
|
||||
key := "invalid"
|
||||
filePath := filepath.Join(s.ConfigDir, key+".json")
|
||||
if err := os.WriteFile(filePath, []byte("invalid json"), 0644); err != nil {
|
||||
t.Fatalf("Failed to write invalid json file: %v", err)
|
||||
}
|
||||
|
||||
type CustomConfig struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Timeout int `json:"timeout"`
|
||||
}
|
||||
|
||||
var actualConfig CustomConfig
|
||||
err = s.LoadStruct(key, &actualConfig)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error for invalid JSON, but got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("New service with empty config file", func(t *testing.T) {
|
||||
tempHomeDir, cleanup := setupTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
// Manually create an empty config file
|
||||
configDir := filepath.Join(tempHomeDir, appName, "config")
|
||||
if err := os.MkdirAll(configDir, os.ModePerm); err != nil {
|
||||
t.Fatalf("Failed to create test config dir: %v", err)
|
||||
}
|
||||
configPath := filepath.Join(configDir, configFileName)
|
||||
if err := os.WriteFile(configPath, []byte(""), 0644); err != nil {
|
||||
t.Fatalf("Failed to write empty config file: %v", err)
|
||||
}
|
||||
|
||||
_, err := New()
|
||||
if err == nil {
|
||||
t.Fatalf("New() should have failed with an empty config file, but it did not")
|
||||
}
|
||||
})
|
||||
}
|
||||
46
pkg/config/docs/architecture.md
Normal file
46
pkg/config/docs/architecture.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Architecture Overview
|
||||
|
||||
This project implements a modular configuration system with a Go backend and an Angular frontend, orchestrated by a CLI runner.
|
||||
|
||||
## Components
|
||||
|
||||
The architecture consists of three main components:
|
||||
|
||||
1. **Backend Library (`pkg/config`)**
|
||||
- **Responsibility**: Manages configuration loading, saving, and persistence.
|
||||
- **Tech Stack**: Go.
|
||||
- **Features**:
|
||||
- Struct-based configuration with JSON persistence.
|
||||
- Generic key-value storage supporting JSON, YAML, INI, and XML.
|
||||
- XDG-compliant directory management.
|
||||
- **Integration**: Can be used via static (`New`) or dynamic (`Register`) dependency injection.
|
||||
|
||||
2. **Frontend (`ui`)**
|
||||
- **Responsibility**: Provides a user interface for the application.
|
||||
- **Tech Stack**: Angular.
|
||||
- **Deployment**: Built as a static asset (or Custom Element) served by the backend.
|
||||
|
||||
3. **CLI Runner (`cmd/demo-cli`)**
|
||||
- **Responsibility**: Entry point for the application.
|
||||
- **Tech Stack**: Go (`spf13/cobra`).
|
||||
- **Functions**:
|
||||
- `serve`: Starts a web server that hosts the compiled Angular frontend and API endpoints.
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. **Initialization**: The CLI starts and initializes the `pkg/config` service to load settings from disk.
|
||||
2. **Serving**: The CLI's `serve` command spins up an HTTP server.
|
||||
3. **Interaction**: Users interact with the Angular UI in the browser.
|
||||
4. **API Communication**: The UI communicates with the backend via REST API endpoints (e.g., `/api/v1/demo`).
|
||||
5. **Persistence**: Configuration changes made via the backend service are persisted to the filesystem.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
├── cmd/
|
||||
│ └── demo-cli/ # CLI Application entry point
|
||||
├── pkg/
|
||||
│ └── config/ # Core configuration logic (Go)
|
||||
├── ui/ # Frontend application (Angular)
|
||||
└── docs/ # Project documentation
|
||||
```
|
||||
146
pkg/config/docs/backend.md
Normal file
146
pkg/config/docs/backend.md
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
# Backend Documentation (`pkg/config`)
|
||||
|
||||
The `pkg/config` package provides a robust configuration management service for Go applications. It handles loading, saving, and accessing application settings, supporting both a main JSON configuration file and auxiliary data stored in various formats like YAML, INI, and XML.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Service
|
||||
|
||||
The `Service` struct is the central point for all configuration-related operations. It manages:
|
||||
- File paths and directories.
|
||||
- Default values.
|
||||
- Serialization and deserialization of data.
|
||||
- Integration with the core application framework (if used).
|
||||
|
||||
The `Service` struct fields are automatically saved to and loaded from a JSON configuration file (`config.json`).
|
||||
|
||||
### Initialization
|
||||
|
||||
There are two ways to initialize the configuration service:
|
||||
|
||||
#### 1. Static Injection (`New`)
|
||||
|
||||
Use `config.New()` when you want to manage the service instance manually.
|
||||
|
||||
```go
|
||||
cfg, err := config.New()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to initialize config: %v", err)
|
||||
}
|
||||
// Use cfg...
|
||||
```
|
||||
|
||||
#### 2. Dynamic Injection (`Register`)
|
||||
|
||||
Use `config.Register(coreInstance)` when integrating with the `core` package for dynamic dependency injection.
|
||||
|
||||
```go
|
||||
// Assuming 'c' is your *core.Core instance
|
||||
svc, err := config.Register(c)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Configuration (Get/Set)
|
||||
|
||||
The service provides type-safe methods to get and set configuration values that correspond to the fields defined in the `Service` struct.
|
||||
|
||||
### Set a Value
|
||||
|
||||
```go
|
||||
err := cfg.Set("language", "fr")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to set config value: %v", err)
|
||||
}
|
||||
```
|
||||
*Note: `Set` automatically persists the changes to the `config.json` file.*
|
||||
|
||||
### Get a Value
|
||||
|
||||
```go
|
||||
var lang string
|
||||
err := cfg.Get("language", &lang)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get config value: %v", err)
|
||||
}
|
||||
fmt.Printf("Language: %s\n", lang)
|
||||
```
|
||||
|
||||
## Arbitrary Struct Persistence
|
||||
|
||||
You can save and load arbitrary Go structs to JSON files within the configuration directory using `SaveStruct` and `LoadStruct`. This is useful for complex data that doesn't fit into the main configuration schema.
|
||||
|
||||
### Saving a Struct
|
||||
|
||||
```go
|
||||
type UserPreferences struct {
|
||||
Theme string `json:"theme"`
|
||||
Notifications bool `json:"notifications"`
|
||||
}
|
||||
|
||||
prefs := UserPreferences{Theme: "dark", Notifications: true}
|
||||
|
||||
// Saves to <ConfigDir>/user_prefs.json
|
||||
err := cfg.SaveStruct("user_prefs", prefs)
|
||||
if err != nil {
|
||||
log.Printf("Error saving user preferences: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
### Loading a Struct
|
||||
|
||||
```go
|
||||
var prefs UserPreferences
|
||||
err := cfg.LoadStruct("user_prefs", &prefs)
|
||||
if err != nil {
|
||||
log.Printf("Error loading user preferences: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
## Generic Key-Value Persistence
|
||||
|
||||
For more flexible data storage, the service supports generic key-value pairs in multiple file formats. The format is determined by the file extension.
|
||||
|
||||
### Supported Formats
|
||||
|
||||
- **JSON** (`.json`)
|
||||
- **YAML** (`.yaml`, `.yml`)
|
||||
- **INI** (`.ini`)
|
||||
- **XML** (`.xml`)
|
||||
|
||||
### Saving Key-Values
|
||||
|
||||
```go
|
||||
data := map[string]interface{}{
|
||||
"host": "localhost",
|
||||
"port": 8080,
|
||||
}
|
||||
|
||||
// Save as YAML
|
||||
if err := cfg.SaveKeyValues("database.yml", data); err != nil {
|
||||
log.Printf("Error saving database config: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
### Loading Key-Values
|
||||
|
||||
```go
|
||||
dbConfig, err := cfg.LoadKeyValues("database.yml")
|
||||
if err != nil {
|
||||
log.Printf("Error loading database config: %v", err)
|
||||
}
|
||||
|
||||
port := dbConfig["port"]
|
||||
```
|
||||
|
||||
## Configuration Directory
|
||||
|
||||
The service automatically resolves appropriate directories for storing configuration and data, respecting XDG standards on Linux/Unix-like systems and standard paths on other OSs.
|
||||
|
||||
- **Config Path**: `<ConfigDir>/config.json`
|
||||
- **Config Dir**: `~/.local/share/lethean/config` (example on Linux)
|
||||
- **Data Dir**: `~/.local/share/lethean/data`
|
||||
- **Cache Dir**: `~/.cache/lethean`
|
||||
|
||||
These paths are accessible via the `Service` struct fields (e.g., `cfg.ConfigDir`).
|
||||
50
pkg/config/docs/cli.md
Normal file
50
pkg/config/docs/cli.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# CLI Documentation (`cmd/demo-cli`)
|
||||
|
||||
The `demo-cli` is a command-line interface application built to demonstrate the capabilities of the Config Module and serve the frontend application. It uses the `cobra` library for command management.
|
||||
|
||||
## Installation
|
||||
|
||||
You can run the CLI directly using `go run`:
|
||||
|
||||
```bash
|
||||
go run ./cmd/demo-cli <command>
|
||||
```
|
||||
|
||||
Or build it into a binary:
|
||||
|
||||
```bash
|
||||
go build -o demo-cli ./cmd/demo-cli
|
||||
./demo-cli <command>
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### `serve`
|
||||
|
||||
The `serve` command starts an HTTP server that serves both the Angular frontend and a demo API endpoint.
|
||||
|
||||
**Usage:**
|
||||
|
||||
```bash
|
||||
go run ./cmd/demo-cli serve
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- **Frontend Serving**: Serves static files from `ui/dist/config/browser`.
|
||||
- **API Endpoint**: Exposes a demo endpoint at `/api/v1/demo`.
|
||||
- **Port**: Listens on port `8080`.
|
||||
|
||||
**Example Output:**
|
||||
```
|
||||
Listening on :8080...
|
||||
```
|
||||
|
||||
Access the application at `http://localhost:8080`.
|
||||
|
||||
### Root Command
|
||||
|
||||
Running the CLI without any subcommands prints the help message or executes the default action (if configured).
|
||||
|
||||
```bash
|
||||
go run ./cmd/demo-cli
|
||||
```
|
||||
66
pkg/config/docs/frontend.md
Normal file
66
pkg/config/docs/frontend.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Frontend Documentation (`ui`)
|
||||
|
||||
The frontend of this project is an Angular application located in the `ui` directory. It is designed to be built as a custom element (Web Component) or a standard Angular application.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Ensure you have the following installed:
|
||||
- Node.js (Latest LTS recommended)
|
||||
- npm (comes with Node.js)
|
||||
|
||||
## Setup
|
||||
|
||||
Navigate to the `ui` directory and install the dependencies:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm install
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
To start the local development server:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
This will run the application at `http://localhost:4200/`. The application automatically reloads if you change any of the source files.
|
||||
|
||||
## Building
|
||||
|
||||
The project can be built for production using the standard Angular CLI build command:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
### Custom Element Build
|
||||
|
||||
*Note: If the application is configured as a Custom Element (Web Component), the build output in `dist/` typically includes a main JavaScript file that can be included in other HTML pages.*
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Unit tests are written using Jasmine and run with Karma. To execute them:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
### End-to-End Tests
|
||||
|
||||
End-to-end tests can be run via:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `src/`: Source code of the Angular application.
|
||||
- `angular.json`: Angular CLI configuration.
|
||||
- `package.json`: Project dependencies and scripts.
|
||||
239
pkg/config/formats.go
Normal file
239
pkg/config/formats.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// ConfigFormat defines an interface for loading and saving configuration data in
|
||||
// various formats. Each format implementation is responsible for serializing and
|
||||
// deserializing data between a file and a map of key-value pairs.
|
||||
type ConfigFormat interface {
|
||||
// Load reads data from the specified path and returns it as a map.
|
||||
Load(path string) (map[string]interface{}, error)
|
||||
// Save writes the provided data map to the specified path.
|
||||
Save(path string, data map[string]interface{}) error
|
||||
}
|
||||
|
||||
// JSONFormat implements the ConfigFormat interface for JSON files. It provides
|
||||
// methods to read from and write to files in JSON format.
|
||||
type JSONFormat struct{}
|
||||
|
||||
// Load reads a JSON file from the given path and decodes it into a map.
|
||||
// The keys of the map are strings, and the values are of type interface{}.
|
||||
func (f *JSONFormat) Load(path string) (map[string]interface{}, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result map[string]interface{}
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Save encodes the provided map into JSON format and writes it to the given
|
||||
// path. The output is indented for readability.
|
||||
func (f *JSONFormat) Save(path string, data map[string]interface{}) error {
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, jsonData, 0644)
|
||||
}
|
||||
|
||||
// YAMLFormat implements the ConfigFormat interface for YAML files. It provides
|
||||
// methods to read from and write to files in YAML format.
|
||||
type YAMLFormat struct{}
|
||||
|
||||
// Load reads a YAML file from the given path and decodes it into a map.
|
||||
func (f *YAMLFormat) Load(path string) (map[string]interface{}, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result map[string]interface{}
|
||||
if err := yaml.Unmarshal(data, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Save encodes the provided map into YAML format and writes it to the given
|
||||
// path.
|
||||
func (f *YAMLFormat) Save(path string, data map[string]interface{}) error {
|
||||
yamlData, err := yaml.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, yamlData, 0644)
|
||||
}
|
||||
|
||||
// INIFormat implements the ConfigFormat interface for INI files. It handles
|
||||
// the structured format of INI files, including sections and keys.
|
||||
type INIFormat struct{}
|
||||
|
||||
// Load reads an INI file and converts its sections and keys into a single map.
|
||||
// Keys in the map are formed by concatenating the section name and key name with
|
||||
// a dot (e.g., "section.key").
|
||||
func (f *INIFormat) Load(path string) (map[string]interface{}, error) {
|
||||
cfg, err := ini.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]interface{})
|
||||
for _, section := range cfg.Sections() {
|
||||
for _, key := range section.Keys() {
|
||||
result[section.Name()+"."+key.Name()] = key.Value()
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Save writes a map of key-value pairs to an INI file. Keys in the map are
|
||||
// split by a dot to determine the section and key for the INI file.
|
||||
func (f *INIFormat) Save(path string, data map[string]interface{}) error {
|
||||
cfg := ini.Empty()
|
||||
for key, value := range data {
|
||||
parts := strings.SplitN(key, ".", 2)
|
||||
section := ini.DefaultSection
|
||||
keyName := parts[0]
|
||||
if len(parts) > 1 {
|
||||
section = parts[0]
|
||||
keyName = parts[1]
|
||||
}
|
||||
if _, err := cfg.Section(section).NewKey(keyName, fmt.Sprintf("%v", value)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return cfg.SaveTo(path)
|
||||
}
|
||||
|
||||
// XMLFormat implements the ConfigFormat interface for XML files. It uses a
|
||||
// simple structure with a root "config" element containing a series of "entry"
|
||||
// elements, each with a "key" and "value".
|
||||
type XMLFormat struct{}
|
||||
|
||||
// xmlEntry is a helper struct for marshaling and unmarshaling XML data.
|
||||
type xmlEntry struct {
|
||||
Key string `xml:"key"`
|
||||
Value string `xml:"value"`
|
||||
}
|
||||
|
||||
// Load reads an XML file and parses it into a map. It expects the XML to have
|
||||
// a specific structure as defined by the xmlEntry struct.
|
||||
func (f *XMLFormat) Load(path string) (map[string]interface{}, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var v struct {
|
||||
XMLName xml.Name `xml:"config"`
|
||||
Entries []xmlEntry `xml:"entry"`
|
||||
}
|
||||
if err := xml.Unmarshal(data, &v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]interface{})
|
||||
for _, entry := range v.Entries {
|
||||
result[entry.Key] = entry.Value
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Save writes a map of key-value pairs to an XML file. The data is structured
|
||||
// with a root "config" element and child "entry" elements.
|
||||
func (f *XMLFormat) Save(path string, data map[string]interface{}) error {
|
||||
var entries []xmlEntry
|
||||
for key, value := range data {
|
||||
entries = append(entries, xmlEntry{
|
||||
Key: key,
|
||||
Value: fmt.Sprintf("%v", value),
|
||||
})
|
||||
}
|
||||
xmlData, err := xml.MarshalIndent(struct {
|
||||
XMLName xml.Name `xml:"config"`
|
||||
Entries []xmlEntry `xml:"entry"`
|
||||
}{Entries: entries}, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, xmlData, 0644)
|
||||
}
|
||||
|
||||
// GetConfigFormat returns a ConfigFormat implementation based on the file
|
||||
// extension of the provided path. This allows the config service to dynamically
|
||||
// handle different file formats.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// format, err := GetConfigFormat("settings.json")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// // format is now a JSONFormat
|
||||
func GetConfigFormat(path string) (ConfigFormat, error) {
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
switch ext {
|
||||
case ".json":
|
||||
return &JSONFormat{}, nil
|
||||
case ".yaml", ".yml":
|
||||
return &YAMLFormat{}, nil
|
||||
case ".ini":
|
||||
return &INIFormat{}, nil
|
||||
case ".xml":
|
||||
return &XMLFormat{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported config format: %s", ext)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveKeyValues saves a map of key-value pairs to a file in the config
|
||||
// directory. The file format is determined by the extension of the `key`
|
||||
// parameter. This method is a convenient way to store structured data in a
|
||||
// format of choice.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// data := map[string]interface{}{"host": "localhost", "port": 8080}
|
||||
// err := cfg.SaveKeyValues("database.yml", data)
|
||||
// if err != nil {
|
||||
// log.Printf("Error saving database config: %v", err)
|
||||
// }
|
||||
func (s *Service) SaveKeyValues(key string, data map[string]interface{}) error {
|
||||
format, err := GetConfigFormat(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filePath := filepath.Join(s.ConfigDir, key)
|
||||
return format.Save(filePath, data)
|
||||
}
|
||||
|
||||
// LoadKeyValues loads a map of key-value pairs from a file in the config
|
||||
// directory. The file format is determined by the extension of the `key`
|
||||
// parameter. This allows for easy retrieval of data stored in various formats.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// dbConfig, err := cfg.LoadKeyValues("database.yml")
|
||||
// if err != nil {
|
||||
// log.Printf("Error loading database config: %v", err)
|
||||
// }
|
||||
// port, ok := dbConfig["port"].(int)
|
||||
// // ...
|
||||
func (s *Service) LoadKeyValues(key string) (map[string]interface{}, error) {
|
||||
format, err := GetConfigFormat(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filePath := filepath.Join(s.ConfigDir, key)
|
||||
return format.Load(filePath)
|
||||
}
|
||||
107
pkg/config/formats_test.go
Normal file
107
pkg/config/formats_test.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigFormats(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "config-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
service := &Service{
|
||||
ConfigDir: tempDir,
|
||||
}
|
||||
|
||||
testData := map[string]interface{}{
|
||||
"key1": "value1",
|
||||
"key2": 123.0,
|
||||
"key3": true,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
format string
|
||||
filename string
|
||||
}{
|
||||
{"json", "test.json"},
|
||||
{"yaml", "test.yaml"},
|
||||
{"ini", "test.ini"},
|
||||
{"xml", "test.xml"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.format, func(t *testing.T) {
|
||||
// Test SaveKeyValues
|
||||
err := service.SaveKeyValues(tc.filename, testData)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveKeyValues failed for %s: %v", tc.format, err)
|
||||
}
|
||||
|
||||
// Test LoadKeyValues
|
||||
loadedData, err := service.LoadKeyValues(tc.filename)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadKeyValues failed for %s: %v", tc.format, err)
|
||||
}
|
||||
|
||||
// INI format saves everything as strings, so we need to adjust the expected data
|
||||
expectedData := testData
|
||||
if tc.format == "ini" {
|
||||
expectedData = map[string]interface{}{
|
||||
"DEFAULT.key1": "value1",
|
||||
"DEFAULT.key2": "123",
|
||||
"DEFAULT.key3": "true",
|
||||
}
|
||||
}
|
||||
|
||||
if tc.format == "yaml" {
|
||||
// The yaml library unmarshals numbers as int if they don't have a decimal point.
|
||||
if val, ok := loadedData["key2"].(int); ok {
|
||||
loadedData["key2"] = float64(val)
|
||||
}
|
||||
}
|
||||
|
||||
if tc.format == "xml" {
|
||||
expectedData = map[string]interface{}{
|
||||
"key1": "value1",
|
||||
"key2": "123",
|
||||
"key3": "true",
|
||||
}
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedData, loadedData) {
|
||||
t.Errorf("Loaded data does not match original data for %s.\nExpected: %v\nGot: %v", tc.format, expectedData, loadedData)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfigFormat(t *testing.T) {
|
||||
testCases := []struct {
|
||||
filename string
|
||||
expectedType interface{}
|
||||
expectError bool
|
||||
}{
|
||||
{"config.json", &JSONFormat{}, false},
|
||||
{"config.yaml", &YAMLFormat{}, false},
|
||||
{"config.yml", &YAMLFormat{}, false},
|
||||
{"config.ini", &INIFormat{}, false},
|
||||
{"config.xml", &XMLFormat{}, false},
|
||||
{"config.txt", nil, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.filename, func(t *testing.T) {
|
||||
format, err := GetConfigFormat(tc.filename)
|
||||
if (err != nil) != tc.expectError {
|
||||
t.Fatalf("Expected error: %v, got: %v", tc.expectError, err)
|
||||
}
|
||||
if !tc.expectError && reflect.TypeOf(format) != reflect.TypeOf(tc.expectedType) {
|
||||
t.Errorf("Expected format type %T, got %T", tc.expectedType, format)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
20
pkg/config/go.mod
Normal file
20
pkg/config/go.mod
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
module github.com/Snider/Core/pkg/config
|
||||
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/spf13/cobra v1.10.1
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
28
pkg/config/go.sum
Normal file
28
pkg/config/go.sum
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
17
pkg/config/ui/.editorconfig
Normal file
17
pkg/config/ui/.editorconfig
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
ij_typescript_use_double_quotes = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
43
pkg/config/ui/.gitignore
vendored
Normal file
43
pkg/config/ui/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
__screenshots__/
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
4
pkg/config/ui/.vscode/extensions.json
vendored
Normal file
4
pkg/config/ui/.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
20
pkg/config/ui/.vscode/launch.json
vendored
Normal file
20
pkg/config/ui/.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
pkg/config/ui/.vscode/tasks.json
vendored
Normal file
42
pkg/config/ui/.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
59
pkg/config/ui/README.md
Normal file
59
pkg/config/ui/README.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Config
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.9.
|
||||
|
||||
## Development server
|
||||
|
||||
To start a local development server, run:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build the project run:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
99
pkg/config/ui/angular.json
Normal file
99
pkg/config/ui/angular.json
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"config": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"standalone": false
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"standalone": false
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"standalone": false
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "none"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "config:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "config:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular/build:extract-i18n"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular/build:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": [],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9676
pkg/config/ui/package-lock.json
generated
Normal file
9676
pkg/config/ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
49
pkg/config/ui/package.json
Normal file
49
pkg/config/ui/package.json
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "config",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test --watch=false --browsers=ChromeHeadless"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^20.3.0",
|
||||
"@angular/compiler": "^20.3.0",
|
||||
"@angular/core": "^20.3.0",
|
||||
"@angular/elements": "^20.3.10",
|
||||
"@angular/forms": "^20.3.0",
|
||||
"@angular/platform-browser": "^20.3.0",
|
||||
"@angular/router": "^20.3.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^20.3.9",
|
||||
"@angular/cli": "^20.3.9",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.9.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.9.2"
|
||||
}
|
||||
}
|
||||
BIN
pkg/config/ui/public/favicon.ico
Normal file
BIN
pkg/config/ui/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
23
pkg/config/ui/src/app/app-module.ts
Normal file
23
pkg/config/ui/src/app/app-module.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { DoBootstrap, Injector, NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { createCustomElement } from '@angular/elements';
|
||||
|
||||
import { App } from './app';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
App
|
||||
],
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners()
|
||||
]
|
||||
})
|
||||
export class AppModule implements DoBootstrap {
|
||||
constructor(private injector: Injector) {
|
||||
const el = createCustomElement(App, { injector });
|
||||
customElements.define('config-element', el);
|
||||
}
|
||||
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
1
pkg/config/ui/src/app/app.html
Normal file
1
pkg/config/ui/src/app/app.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<h1>Hello, {{ title() }}</h1>
|
||||
29
pkg/config/ui/src/app/app.spec.ts
Normal file
29
pkg/config/ui/src/app/app.spec.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { App } from './app';
|
||||
|
||||
describe('App', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [App],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'config'`, () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title()).toEqual('config');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, config');
|
||||
});
|
||||
});
|
||||
10
pkg/config/ui/src/app/app.ts
Normal file
10
pkg/config/ui/src/app/app.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Component, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'config-element',
|
||||
templateUrl: './app.html',
|
||||
standalone: true
|
||||
})
|
||||
export class App {
|
||||
public readonly title = signal('config');
|
||||
}
|
||||
13
pkg/config/ui/src/index.html
Normal file
13
pkg/config/ui/src/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Config</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<config-element></config-element>
|
||||
</body>
|
||||
</html>
|
||||
7
pkg/config/ui/src/main.ts
Normal file
7
pkg/config/ui/src/main.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { platformBrowser } from '@angular/platform-browser';
|
||||
import { AppModule } from './app/app-module';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
1
pkg/config/ui/src/styles.css
Normal file
1
pkg/config/ui/src/styles.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
15
pkg/config/ui/tsconfig.app.json
Normal file
15
pkg/config/ui/tsconfig.app.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
34
pkg/config/ui/tsconfig.json
Normal file
34
pkg/config/ui/tsconfig.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
pkg/config/ui/tsconfig.spec.json
Normal file
14
pkg/config/ui/tsconfig.spec.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -278,6 +278,12 @@ func (c *Core) Config() Config {
|
|||
return cfg
|
||||
}
|
||||
|
||||
// Display returns the registered Display service.
|
||||
func (c *Core) Display() Display {
|
||||
d := MustServiceFor[Display](c, "display")
|
||||
return d
|
||||
}
|
||||
|
||||
func (c *Core) Core() *Core { return c }
|
||||
|
||||
// Assets returns the embedded filesystem containing the application's assets.
|
||||
|
|
@ -153,14 +153,14 @@ func TestCore_WithWails_Good(t *testing.T) {
|
|||
assert.Equal(t, app, c.App)
|
||||
}
|
||||
|
||||
//go:embed core/testdata
|
||||
//go:embed testdata
|
||||
var testFS embed.FS
|
||||
|
||||
func TestCore_WithAssets_Good(t *testing.T) {
|
||||
c, err := New(WithAssets(testFS))
|
||||
assert.NoError(t, err)
|
||||
assets := c.Assets()
|
||||
file, err := assets.Open("core/testdata/test.txt")
|
||||
file, err := assets.Open("testdata/test.txt")
|
||||
assert.NoError(t, err)
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(file)
|
||||
55
pkg/core/go.mod
Normal file
55
pkg/core/go.mod
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
module github.com/Snider/Core/pkg/core
|
||||
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/adrg/xdg v0.5.3 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.16.4 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/lmittmann/tint v1.1.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.23 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
127
pkg/core/go.sum
Normal file
127
pkg/core/go.sum
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ=
|
||||
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
|
||||
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
|
||||
github.com/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/wailsapp/go-webview2 v1.0.23 h1:jmv8qhz1lHibCc79bMM/a/FqOnnzOGEisLav+a0b9P0=
|
||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41 h1:DYcC1/vtO862sxnoyCOMfLLypbzpFWI257fR6zDYY+Y=
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41/go.mod h1:7i8tSuA74q97zZ5qEJlcVZdnO+IR7LT2KU8UpzYMPsw=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
@ -82,3 +82,22 @@ type Config interface {
|
|||
// Set stores a configuration value by key.
|
||||
Set(key string, v any) error
|
||||
}
|
||||
|
||||
// WindowOption is an interface for applying configuration options to a window.
|
||||
type WindowOption interface {
|
||||
Apply(any)
|
||||
}
|
||||
|
||||
// Display provides access to windowing and visual elements.
|
||||
type Display interface {
|
||||
// OpenWindow creates a new window with the given options.
|
||||
OpenWindow(opts ...WindowOption) error
|
||||
}
|
||||
|
||||
// ActionServiceStartup is a message sent when the application's services are starting up.
|
||||
// This provides a hook for services to perform initialization tasks.
|
||||
type ActionServiceStartup struct{}
|
||||
|
||||
// ActionServiceShutdown is a message sent when the application is shutting down.
|
||||
// This allows services to perform cleanup tasks, such as saving state or closing resources.
|
||||
type ActionServiceShutdown struct{}
|
||||
|
|
@ -8,6 +8,33 @@ import (
|
|||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// ServiceRuntime is a helper struct embedded in services to provide access to the core application.
|
||||
// It is generic and can be parameterized with a service-specific options struct.
|
||||
type ServiceRuntime[T any] struct {
|
||||
core *Core
|
||||
opts T
|
||||
}
|
||||
|
||||
// NewServiceRuntime creates a new ServiceRuntime instance for a service.
|
||||
// This is typically called by a service's constructor.
|
||||
func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] {
|
||||
return &ServiceRuntime[T]{
|
||||
core: c,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
// Core returns the central core instance, providing access to all registered services.
|
||||
func (r *ServiceRuntime[T]) Core() *Core {
|
||||
return r.core
|
||||
}
|
||||
|
||||
// Config returns the registered Config service from the core application.
|
||||
// This is a convenience method for accessing the application's configuration.
|
||||
func (r *ServiceRuntime[T]) Config() Config {
|
||||
return r.core.Config()
|
||||
}
|
||||
|
||||
// Runtime is the container that holds all instantiated services.
|
||||
// Its fields are the concrete types, allowing Wails to bind them directly.
|
||||
// This struct is the primary entry point for the Wails application.
|
||||
22
pkg/display/.github/workflows/go.yml
vendored
Normal file
22
pkg/display/.github/workflows/go.yml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
name: Go
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
||||
- name: Install dependencies
|
||||
run: go get .
|
||||
- name: Test with coverage
|
||||
run: go test -coverprofile=coverage.out
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./coverage.out
|
||||
fail_ci_if_error: true
|
||||
24
pkg/display/.github/workflows/release.yml
vendored
Normal file
24
pkg/display/.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
17
pkg/display/.gitignore
vendored
Normal file
17
pkg/display/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Go
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.test
|
||||
*.out
|
||||
*.prof
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
43
pkg/display/.goreleaser.yaml
Normal file
43
pkg/display/.goreleaser.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
archives:
|
||||
- replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
brews:
|
||||
- name: display
|
||||
tap:
|
||||
owner: Snider
|
||||
name: homebrew-tap
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: goreleaser@carlosbecker.com
|
||||
homepage: "https://github.com/Snider/display"
|
||||
description: "A display module for the core web3 framework"
|
||||
license: "EUPL-1.2"
|
||||
287
pkg/display/LICENSE
Normal file
287
pkg/display/LICENSE
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
|
||||
below) which is provided under the terms of this Licence. Any use of the Work,
|
||||
other than as authorised under this Licence is prohibited (to the extent such
|
||||
use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This Licence
|
||||
does not define the extent of modification or dependence on the Original Work
|
||||
required in order to classify a work as a Derivative Work; this extent is
|
||||
determined by copyright law applicable in the country mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which is
|
||||
meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright vested
|
||||
in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case may
|
||||
be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make effective
|
||||
the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
|
||||
any patents held by the Licensor, to the extent necessary to make use of the
|
||||
rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates, in
|
||||
a notice following the copyright notice attached to the Work, a repository where
|
||||
the Source Code is easily and freely accessible for as long as the Licensor
|
||||
continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from
|
||||
any exception or limitation to the exclusive rights of the rights owners in the
|
||||
Work, of the exhaustion of those rights or of other applicable limitations
|
||||
thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and a
|
||||
copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of the
|
||||
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions on
|
||||
the Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed under
|
||||
a Compatible Licence, this Distribution or Communication can be done under the
|
||||
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
|
||||
Licence’ refers to the licences listed in the appendix attached to this Licence.
|
||||
Should the Licensee's obligations under the Compatible Licence conflict with
|
||||
his/her obligations under this Licence, the obligations of the Compatible
|
||||
Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the Work,
|
||||
the Licensee will provide a machine-readable copy of the Source Code or indicate
|
||||
a repository where this Source will be easily and freely available for as long
|
||||
as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings
|
||||
to the Work are owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’ basis
|
||||
and without warranties of any kind concerning the Work, including without
|
||||
limitation merchantability, fitness for a particular purpose, absence of defects
|
||||
or errors, accuracy, non-infringement of intellectual property rights other than
|
||||
copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition
|
||||
for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the use
|
||||
of the Work, including without limitation, damages for loss of goodwill, work
|
||||
stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such damage.
|
||||
However, the Licensor will be liable under statutory product liability laws as
|
||||
far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional agreement,
|
||||
defining obligations or services consistent with this Licence. However, if
|
||||
accepting obligations, You may act only on your own behalf and on your sole
|
||||
responsibility, not on behalf of the original Licensor or any other Contributor,
|
||||
and only if You agree to indemnify, defend, and hold each Contributor harmless
|
||||
for any liability incurred by, or claims asserted against such Contributor by
|
||||
the fact You have accepted any warranty or additional liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
|
||||
placed under the bottom of a window displaying the text of this Licence or by
|
||||
affirming consent in any other similar way, in accordance with the rules of
|
||||
applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this Licence,
|
||||
such as the use of the Work, the creation by You of a Derivative Work or the
|
||||
Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of electronic
|
||||
communication by You (for example, by offering to download the Work from a
|
||||
remote location) the distribution channel or media (for example, a website) must
|
||||
at least provide to the public the information requested by the applicable law
|
||||
regarding the Licensor, the Licence and the way it may be accessible, concluded,
|
||||
stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions of
|
||||
this Licence or updated versions of the Appendix, so far this is required and
|
||||
reasonable, without reducing the scope of the rights granted by the Licence. New
|
||||
versions of the Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty on
|
||||
the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive jurisdiction
|
||||
of the competent court where the Licensor resides or conducts its primary
|
||||
business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the above
|
||||
licences without producing a new version of the EUPL, as long as they provide
|
||||
the rights granted in Article 2 of this Licence and protect the covered Source
|
||||
Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a new
|
||||
EUPL version.
|
||||
43
pkg/display/README.md
Normal file
43
pkg/display/README.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Display
|
||||
|
||||
This repository is a display module for the core web3 framework. It includes a Go backend, an Angular custom element, and a full release cycle configuration.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://github.com/Snider/display.git
|
||||
```
|
||||
|
||||
2. **Install the dependencies:**
|
||||
```bash
|
||||
cd display
|
||||
go mod tidy
|
||||
cd ui
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Run the development server:**
|
||||
```bash
|
||||
go run ./cmd/demo-cli serve
|
||||
```
|
||||
This will start the Go backend and serve the Angular custom element.
|
||||
|
||||
## Building the Custom Element
|
||||
|
||||
To build the Angular custom element, run the following command:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm run build
|
||||
```
|
||||
|
||||
This will create a single JavaScript file in the `dist` directory that you can use in any HTML page.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the EUPL-1.2 License - see the [LICENSE](LICENSE) file for details.
|
||||
20
pkg/display/actions.go
Normal file
20
pkg/display/actions.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package display
|
||||
|
||||
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||
|
||||
// ActionOpenWindow is an IPC message used to request a new window. It contains
|
||||
// the options for the new window.
|
||||
//
|
||||
// example:
|
||||
//
|
||||
// action := display.ActionOpenWindow{
|
||||
// WebviewWindowOptions: application.WebviewWindowOptions{
|
||||
// Name: "my-window",
|
||||
// Title: "My Window",
|
||||
// Width: 800,
|
||||
// Height: 600,
|
||||
// },
|
||||
// }
|
||||
type ActionOpenWindow struct {
|
||||
application.WebviewWindowOptions
|
||||
}
|
||||
29
pkg/display/cmd/demo-cli/cmd/root.go
Normal file
29
pkg/display/cmd/demo-cli/cmd/root.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "demo-cli",
|
||||
Short: "A demo CLI for the display module",
|
||||
Long: `A longer description that spans multiple lines and likely contains
|
||||
examples and usage of using your application.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Hello from the demo CLI!")
|
||||
},
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags
|
||||
// appropriately. This is called by main.main(). It only needs to happen once
|
||||
// to the rootCmd.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
34
pkg/display/cmd/demo-cli/cmd/serve.go
Normal file
34
pkg/display/cmd/demo-cli/cmd/serve.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// serveCmd represents the serve command
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Starts the HTTP server",
|
||||
Long: `Starts the HTTP server to serve the frontend and the API.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
http.HandleFunc("/api/v1/demo", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, world!")
|
||||
})
|
||||
|
||||
fs := http.FileServer(http.Dir("./ui/dist/display/browser"))
|
||||
http.Handle("/", fs)
|
||||
|
||||
log.Println("Listening on :8080...")
|
||||
err := http.ListenAndServe(":8080", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
9
pkg/display/cmd/demo-cli/main.go
Normal file
9
pkg/display/cmd/demo-cli/main.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/Snider/Core/pkg/display/cmd/demo-cli/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
||||
184
pkg/display/display.go
Normal file
184
pkg/display/display.go
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
"github.com/wailsapp/wails/v3/pkg/events"
|
||||
)
|
||||
|
||||
// Options holds configuration for the display service.
|
||||
// This struct is used to configure the display service at startup.
|
||||
type Options struct{}
|
||||
|
||||
// Service manages windowing, dialogs, and other visual elements.
|
||||
// It is the primary interface for interacting with the UI.
|
||||
type Service struct {
|
||||
app *application.App
|
||||
config Options
|
||||
}
|
||||
|
||||
// newDisplayService contains the common logic for initializing a Service struct.
|
||||
// It is called by the New function.
|
||||
func newDisplayService() (*Service, error) {
|
||||
return &Service{}, nil
|
||||
}
|
||||
|
||||
// New is the constructor for the display service.
|
||||
// It creates a new Service and returns it.
|
||||
//
|
||||
// example:
|
||||
//
|
||||
// displayService, err := display.New()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
func New() (*Service, error) {
|
||||
s, err := newDisplayService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Startup is called when the app starts. It initializes the display service
|
||||
// and sets up the main application window and system tray.
|
||||
//
|
||||
// err := displayService.Startup(ctx)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
func (s *Service) Startup(ctx context.Context) error {
|
||||
s.app = application.Get()
|
||||
s.app.Logger.Info("Display service started")
|
||||
s.buildMenu()
|
||||
s.systemTray()
|
||||
return s.OpenWindow()
|
||||
}
|
||||
|
||||
// handleOpenWindowAction processes a message to configure and create a new window
|
||||
// using the specified name and options.
|
||||
func (s *Service) handleOpenWindowAction(msg map[string]any) error {
|
||||
opts := parseWindowOptions(msg)
|
||||
s.app.Window.NewWithOptions(opts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseWindowOptions extracts window configuration from a map and returns it
|
||||
// as a `application.WebviewWindowOptions` struct. This function is used by
|
||||
// `handleOpenWindowAction` to parse the incoming message.
|
||||
func parseWindowOptions(msg map[string]any) application.WebviewWindowOptions {
|
||||
opts := application.WebviewWindowOptions{}
|
||||
if name, ok := msg["name"].(string); ok {
|
||||
opts.Name = name
|
||||
}
|
||||
if optsMap, ok := msg["options"].(map[string]any); ok {
|
||||
if title, ok := optsMap["Title"].(string); ok {
|
||||
opts.Title = title
|
||||
}
|
||||
if width, ok := optsMap["Width"].(float64); ok {
|
||||
opts.Width = int(width)
|
||||
}
|
||||
if height, ok := optsMap["Height"].(float64); ok {
|
||||
opts.Height = int(height)
|
||||
}
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// ShowEnvironmentDialog displays a dialog containing detailed information about
|
||||
// the application's runtime environment. This is useful for debugging and
|
||||
// understanding the context in which the application is running.
|
||||
//
|
||||
// example:
|
||||
//
|
||||
// displayService.ShowEnvironmentDialog()
|
||||
func (s *Service) ShowEnvironmentDialog() {
|
||||
envInfo := s.app.Env.Info()
|
||||
|
||||
details := "Environment Information:\n\n"
|
||||
details += fmt.Sprintf("Operating System: %s\n", envInfo.OS)
|
||||
details += fmt.Sprintf("Architecture: %s\n", envInfo.Arch)
|
||||
details += fmt.Sprintf("Debug Mode: %t\n\n", envInfo.Debug)
|
||||
details += fmt.Sprintf("Dark Mode: %t\n\n", s.app.Env.IsDarkMode())
|
||||
details += "Platform Information:"
|
||||
|
||||
// Add platform-specific details
|
||||
for key, value := range envInfo.PlatformInfo {
|
||||
details += fmt.Sprintf("\n%s: %v", key, value)
|
||||
}
|
||||
|
||||
if envInfo.OSInfo != nil {
|
||||
details += fmt.Sprintf("\n\nOS Details:\nName: %s\nVersion: %s",
|
||||
envInfo.OSInfo.Name,
|
||||
envInfo.OSInfo.Version)
|
||||
}
|
||||
|
||||
dialog := s.app.Dialog.Info()
|
||||
dialog.SetTitle("Environment Information")
|
||||
dialog.SetMessage(details)
|
||||
dialog.Show()
|
||||
}
|
||||
|
||||
// OpenWindow creates a new window with the given options. If no options are
|
||||
// provided, it will use the default options.
|
||||
//
|
||||
// example:
|
||||
//
|
||||
// err := displayService.OpenWindow(
|
||||
// display.WithName("my-window"),
|
||||
// display.WithTitle("My Window"),
|
||||
// display.WithWidth(800),
|
||||
// display.WithHeight(600),
|
||||
// )
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
func (s *Service) OpenWindow(opts ...WindowOption) error {
|
||||
wailsOpts := buildWailsWindowOptions(opts...)
|
||||
s.app.Window.NewWithOptions(wailsOpts)
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildWailsWindowOptions creates Wails window options from the given
|
||||
// `WindowOption`s. This function is used by `OpenWindow` to construct the
|
||||
// options for the new window.
|
||||
func buildWailsWindowOptions(opts ...WindowOption) application.WebviewWindowOptions {
|
||||
// Default options
|
||||
winOpts := &WindowConfig{
|
||||
Name: "main",
|
||||
Title: "Core",
|
||||
Width: 1280,
|
||||
Height: 800,
|
||||
URL: "/",
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, opt := range opts {
|
||||
opt.Apply(winOpts)
|
||||
}
|
||||
|
||||
// Create Wails window options
|
||||
return application.WebviewWindowOptions{
|
||||
Name: winOpts.Name,
|
||||
Title: winOpts.Title,
|
||||
Width: winOpts.Width,
|
||||
Height: winOpts.Height,
|
||||
URL: winOpts.URL,
|
||||
AlwaysOnTop: winOpts.AlwaysOnTop,
|
||||
Hidden: winOpts.Hidden,
|
||||
MinimiseButtonState: winOpts.MinimiseButtonState,
|
||||
MaximiseButtonState: winOpts.MaximiseButtonState,
|
||||
CloseButtonState: winOpts.CloseButtonState,
|
||||
Frameless: winOpts.Frameless,
|
||||
}
|
||||
}
|
||||
|
||||
// monitorScreenChanges listens for theme change events and logs when the screen
|
||||
// configuration changes.
|
||||
func (s *Service) monitorScreenChanges() {
|
||||
s.app.Event.OnApplicationEvent(events.Common.ThemeChanged, func(event *application.ApplicationEvent) {
|
||||
s.app.Logger.Info("Screen configuration changed")
|
||||
})
|
||||
}
|
||||
353
pkg/display/display_test.go
Normal file
353
pkg/display/display_test.go
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
func TestParseWindowOptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
msg map[string]any
|
||||
want application.WebviewWindowOptions
|
||||
}{
|
||||
{
|
||||
name: "Valid options",
|
||||
msg: map[string]any{
|
||||
"name": "main",
|
||||
"options": map[string]any{
|
||||
"Title": "My App",
|
||||
"Width": 1024.0,
|
||||
"Height": 768.0,
|
||||
},
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "My App",
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "All options valid",
|
||||
msg: map[string]any{
|
||||
"name": "secondary",
|
||||
"options": map[string]any{
|
||||
"Title": "Another Window",
|
||||
"Width": 800.0,
|
||||
"Height": 600.0,
|
||||
},
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "secondary",
|
||||
Title: "Another Window",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Missing options",
|
||||
msg: map[string]any{
|
||||
"name": "main",
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty message",
|
||||
msg: map[string]any{},
|
||||
want: application.WebviewWindowOptions{},
|
||||
},
|
||||
{
|
||||
name: "Invalid width type",
|
||||
msg: map[string]any{
|
||||
"name": "main",
|
||||
"options": map[string]any{
|
||||
"Title": "My App",
|
||||
"Width": "not a number",
|
||||
"Height": 768.0,
|
||||
},
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "My App",
|
||||
Height: 768,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid height type",
|
||||
msg: map[string]any{
|
||||
"name": "main",
|
||||
"options": map[string]any{
|
||||
"Title": "My App",
|
||||
"Width": 1024.0,
|
||||
"Height": "not a number",
|
||||
},
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "My App",
|
||||
Width: 1024,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Deeply nested and complex message",
|
||||
msg: map[string]any{
|
||||
"name": "main",
|
||||
"options": map[string]any{
|
||||
"Title": "My App",
|
||||
"Width": 1024.0,
|
||||
"Height": 768.0,
|
||||
"nested": map[string]any{
|
||||
"another_level": "some_value",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "My App",
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := parseWindowOptions(tt.msg); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseWindowOptions() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// mockWindowOption is a mock implementation of the WindowOption interface for testing.
|
||||
type mockWindowOption struct {
|
||||
applyFunc func(*WindowConfig)
|
||||
}
|
||||
|
||||
func (m *mockWindowOption) Apply(opts *WindowConfig) {
|
||||
m.applyFunc(opts)
|
||||
}
|
||||
|
||||
func TestBuildWailsWindowOptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts []WindowOption
|
||||
want application.WebviewWindowOptions
|
||||
}{
|
||||
{
|
||||
name: "Default options",
|
||||
opts: []WindowOption{},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "Core",
|
||||
Width: 1280,
|
||||
Height: 800,
|
||||
URL: "/",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Chaining many options",
|
||||
opts: func() []WindowOption {
|
||||
opts := make([]WindowOption, 1000)
|
||||
for i := 0; i < 1000; i++ {
|
||||
opts[i] = WithTitle("Test")
|
||||
}
|
||||
return opts
|
||||
}(),
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "Test",
|
||||
Width: 1280,
|
||||
Height: 800,
|
||||
URL: "/",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Override options",
|
||||
opts: []WindowOption{
|
||||
&mockWindowOption{
|
||||
applyFunc: func(opts *WindowConfig) {
|
||||
opts.Name = "test"
|
||||
opts.Title = "Test Window"
|
||||
opts.Width = 1920
|
||||
opts.Height = 1080
|
||||
opts.URL = "/test"
|
||||
opts.AlwaysOnTop = true
|
||||
opts.Hidden = true
|
||||
opts.MinimiseButtonState = application.ButtonHidden
|
||||
opts.MaximiseButtonState = application.ButtonDisabled
|
||||
opts.CloseButtonState = application.ButtonEnabled
|
||||
opts.Frameless = true
|
||||
},
|
||||
},
|
||||
},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "test",
|
||||
Title: "Test Window",
|
||||
Width: 1920,
|
||||
Height: 1080,
|
||||
URL: "/test",
|
||||
AlwaysOnTop: true,
|
||||
Hidden: true,
|
||||
MinimiseButtonState: application.ButtonHidden,
|
||||
MaximiseButtonState: application.ButtonDisabled,
|
||||
CloseButtonState: application.ButtonEnabled,
|
||||
Frameless: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Nil options",
|
||||
opts: nil,
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "Core",
|
||||
Width: 1280,
|
||||
Height: 800,
|
||||
URL: "/",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Empty options slice",
|
||||
opts: []WindowOption{},
|
||||
want: application.WebviewWindowOptions{
|
||||
Name: "main",
|
||||
Title: "Core",
|
||||
Width: 1280,
|
||||
Height: 800,
|
||||
URL: "/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := buildWailsWindowOptions(tt.opts...); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("buildWailsWindowOptions() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewAndNewDisplayService(t *testing.T) {
|
||||
s, err := New()
|
||||
if err != nil {
|
||||
t.Fatalf("New() error = %v, wantErr nil", err)
|
||||
}
|
||||
if s == nil {
|
||||
t.Fatal("New() returned nil")
|
||||
}
|
||||
|
||||
s, err = newDisplayService()
|
||||
if err != nil {
|
||||
t.Fatalf("newDisplayService() error = %v, wantErr nil", err)
|
||||
}
|
||||
if s == nil {
|
||||
t.Fatal("newDisplayService() returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowOptions(t *testing.T) {
|
||||
config := &WindowConfig{}
|
||||
|
||||
WithName("test-name").Apply(config)
|
||||
if config.Name != "test-name" {
|
||||
t.Errorf("WithName() got = %v, want %v", config.Name, "test-name")
|
||||
}
|
||||
|
||||
WithTitle("test-title").Apply(config)
|
||||
if config.Title != "test-title" {
|
||||
t.Errorf("WithTitle() got = %v, want %v", config.Title, "test-title")
|
||||
}
|
||||
|
||||
WithWidth(100).Apply(config)
|
||||
if config.Width != 100 {
|
||||
t.Errorf("WithWidth() got = %v, want %v", config.Width, 100)
|
||||
}
|
||||
|
||||
WithHeight(200).Apply(config)
|
||||
if config.Height != 200 {
|
||||
t.Errorf("WithHeight() got = %v, want %v", config.Height, 200)
|
||||
}
|
||||
|
||||
WithURL("/testurl").Apply(config)
|
||||
if config.URL != "/testurl" {
|
||||
t.Errorf("WithURL() got = %v, want %v", config.URL, "/testurl")
|
||||
}
|
||||
|
||||
WithAlwaysOnTop(true).Apply(config)
|
||||
if !config.AlwaysOnTop {
|
||||
t.Errorf("WithAlwaysOnTop() got = %v, want %v", config.AlwaysOnTop, true)
|
||||
}
|
||||
|
||||
WithHidden(true).Apply(config)
|
||||
if !config.Hidden {
|
||||
t.Errorf("WithHidden() got = %v, want %v", config.Hidden, true)
|
||||
}
|
||||
|
||||
WithMinimiseButtonState(application.ButtonHidden).Apply(config)
|
||||
if config.MinimiseButtonState != application.ButtonHidden {
|
||||
t.Errorf("WithMinimiseButtonState() got = %v, want %v", config.MinimiseButtonState, application.ButtonHidden)
|
||||
}
|
||||
|
||||
WithMaximiseButtonState(application.ButtonDisabled).Apply(config)
|
||||
if config.MaximiseButtonState != application.ButtonDisabled {
|
||||
t.Errorf("WithMaximiseButtonState() got = %v, want %v", config.MaximiseButtonState, application.ButtonDisabled)
|
||||
}
|
||||
|
||||
WithCloseButtonState(application.ButtonEnabled).Apply(config)
|
||||
if config.CloseButtonState != application.ButtonEnabled {
|
||||
t.Errorf("WithCloseButtonState() got = %v, want %v", config.CloseButtonState, application.ButtonEnabled)
|
||||
}
|
||||
|
||||
WithFrameless(true).Apply(config)
|
||||
if !config.Frameless {
|
||||
t.Errorf("WithFrameless() got = %v, want %v", config.Frameless, true)
|
||||
}
|
||||
}
|
||||
|
||||
func TestService_HandleOpenWindowAction(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
_ = s.handleOpenWindowAction(map[string]any{})
|
||||
}
|
||||
|
||||
func TestService_ShowEnvironmentDialog(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
s.ShowEnvironmentDialog()
|
||||
}
|
||||
|
||||
func TestService_OpenWindow(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
_ = s.OpenWindow()
|
||||
}
|
||||
|
||||
func TestService_MonitorScreenChanges(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
s.monitorScreenChanges()
|
||||
}
|
||||
|
||||
func TestService_BuildMenu(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
s.buildMenu()
|
||||
}
|
||||
|
||||
func TestService_SystemTray(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
s.systemTray()
|
||||
}
|
||||
|
||||
func TestService_Startup(t *testing.T) {
|
||||
t.Skip("Skipping test that requires a running Wails application.")
|
||||
s, _ := New()
|
||||
_ = s.Startup(nil)
|
||||
}
|
||||
54
pkg/display/docs/backend.md
Normal file
54
pkg/display/docs/backend.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Backend Documentation
|
||||
|
||||
The backend is written in Go and uses the `github.com/Snider/display` package. It utilizes the Wails v3 framework to bridge Go and the web frontend.
|
||||
|
||||
## Core Types
|
||||
|
||||
### `Service`
|
||||
The `Service` struct is the main entry point for the display logic.
|
||||
|
||||
- **Initialization:**
|
||||
- `New() (*Service, error)`: Creates a new instance of the service.
|
||||
- `Startup(ctx context.Context) error`: Initializes the Wails application, builds the menu, sets up the system tray, and opens the main window.
|
||||
|
||||
- **Window Management:**
|
||||
- `OpenWindow(opts ...WindowOption) error`: Opens a new window with the specified options.
|
||||
|
||||
- **Dialogs:**
|
||||
- `ShowEnvironmentDialog()`: Displays a native dialog containing information about the runtime environment (OS, Arch, Debug mode, etc.).
|
||||
|
||||
### `WindowConfig` & `WindowOption`
|
||||
Window configuration is handled using the Functional Options pattern. The `WindowConfig` struct holds parameters like:
|
||||
- `Name`, `Title`
|
||||
- `Width`, `Height`
|
||||
- `URL`
|
||||
- `AlwaysOnTop`, `Hidden`, `Frameless`
|
||||
- Window button states (`MinimiseButtonState`, `MaximiseButtonState`, `CloseButtonState`)
|
||||
|
||||
**Available Options:**
|
||||
- `WithName(name string)`
|
||||
- `WithTitle(title string)`
|
||||
- `WithWidth(width int)`
|
||||
- `WithHeight(height int)`
|
||||
- `WithURL(url string)`
|
||||
- `WithAlwaysOnTop(bool)`
|
||||
- `WithHidden(bool)`
|
||||
- `WithFrameless(bool)`
|
||||
- `WithMinimiseButtonState(state)`
|
||||
- `WithMaximiseButtonState(state)`
|
||||
- `WithCloseButtonState(state)`
|
||||
|
||||
## Subsystems
|
||||
|
||||
### Menu (`menu.go`)
|
||||
The `buildMenu` method constructs the application's main menu, adding standard roles like File, Edit, Window, and Help. It allows for platform-specific adjustments (e.g., AppMenu on macOS).
|
||||
|
||||
### System Tray (`tray.go`)
|
||||
The `systemTray` method initializes the system tray icon and its context menu. It supports:
|
||||
- Showing/Hiding all windows.
|
||||
- Displaying environment info.
|
||||
- Quitting the application.
|
||||
- Attaching a hidden window for advanced tray interactions.
|
||||
|
||||
### Actions (`actions.go`)
|
||||
Defines structured messages for Inter-Process Communication (IPC) or internal event handling, such as `ActionOpenWindow` which wraps `application.WebviewWindowOptions`.
|
||||
64
pkg/display/docs/development.md
Normal file
64
pkg/display/docs/development.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Development Guide
|
||||
|
||||
This guide covers how to set up the development environment, build the project, and run the demo.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Go:** Version 1.25 or later.
|
||||
2. **Node.js & npm:** For building the Angular frontend.
|
||||
3. **Wails Dependencies:**
|
||||
- **Linux:** `libgtk-3-dev`, `libwebkit2gtk-4.1-dev`
|
||||
- **macOS/Windows:** Standard development tools (Xcode Command Line Tools / build-essential).
|
||||
|
||||
## Setup
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/Snider/display.git
|
||||
cd display
|
||||
```
|
||||
|
||||
2. Install Go dependencies:
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
3. Install Frontend dependencies:
|
||||
```bash
|
||||
cd ui
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
## Running the Demo
|
||||
|
||||
The project includes a CLI to facilitate development.
|
||||
|
||||
### Serve Mode (Web Preview)
|
||||
To start a simple HTTP server that serves the frontend and a mock API:
|
||||
|
||||
1. Build the frontend first:
|
||||
```bash
|
||||
cd ui && npm run build && cd ..
|
||||
```
|
||||
2. Run the serve command:
|
||||
```bash
|
||||
go run ./cmd/demo-cli serve
|
||||
```
|
||||
Access the app at `http://localhost:8080`.
|
||||
|
||||
## Building the Project
|
||||
|
||||
### Frontend
|
||||
```bash
|
||||
cd ui
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Backend / Application
|
||||
This project is a library/module. However, it can be tested via the demo CLI or by integrating it into a Wails application entry point.
|
||||
|
||||
To run the tests:
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
29
pkg/display/docs/frontend.md
Normal file
29
pkg/display/docs/frontend.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Frontend Documentation
|
||||
|
||||
The frontend is an Angular application located in the `ui/` directory. It is designed to be built as a custom element or a standalone application that runs inside the Wails webview.
|
||||
|
||||
## Structure
|
||||
|
||||
- **Path:** `ui/`
|
||||
- **Framework:** Angular
|
||||
- **Output:** The build process generates artifacts in `ui/dist`.
|
||||
|
||||
## Development
|
||||
|
||||
The frontend can be developed using standard Angular CLI commands or via the provided demo CLI which serves the built files.
|
||||
|
||||
## Build Process
|
||||
|
||||
To build the frontend for production or integration with the Go backend:
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
This will compile the Angular application and place the output in the `dist/` directory, which the Go backend can then serve or embed.
|
||||
|
||||
## Integration
|
||||
|
||||
The frontend communicates with the Go backend through the Wails runtime. It can trigger actions defined in the backend (like opening windows) and receive events.
|
||||
25
pkg/display/docs/overview.md
Normal file
25
pkg/display/docs/overview.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Overview
|
||||
|
||||
The `display` module is a core component responsible for the visual presentation and system integration of the application. It leverages **Wails v3** to create a desktop application backend in Go and **Angular** for the frontend user interface.
|
||||
|
||||
## Architecture
|
||||
|
||||
The project consists of two main parts:
|
||||
|
||||
1. **Backend (Go):** Handles window management, system tray integration, application menus, and communication with the frontend. It is located in the root directory and packaged as a Go module.
|
||||
2. **Frontend (Angular):** Provides the user interface. It is located in the `ui/` directory and is built as a custom element that interacts with the backend.
|
||||
|
||||
## Key Components
|
||||
|
||||
### Display Service (`display`)
|
||||
The core service that manages the application lifecycle. It wraps the Wails application instance and exposes methods to:
|
||||
- Open and configure windows.
|
||||
- Manage the system tray.
|
||||
- Show system dialogs (e.g., environment info).
|
||||
|
||||
### System Integration
|
||||
- **Menu:** A standard application menu (File, Edit, View, etc.) is constructed in `menu.go`.
|
||||
- **System Tray:** A system tray icon and menu are configured in `tray.go`, allowing quick access to common actions like showing/hiding windows or viewing environment info.
|
||||
|
||||
### Demo CLI
|
||||
A command-line interface (`cmd/demo-cli`) is provided to run and test the display module. It includes a `serve` command for web-based development.
|
||||
54
pkg/display/go.mod
Normal file
54
pkg/display/go.mod
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
module github.com/Snider/Core/pkg/display
|
||||
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/adrg/xdg v0.5.3 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/ebitengine/purego v0.9.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.16.4 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
github.com/leaanthony/u v1.1.1 // indirect
|
||||
github.com/lmittmann/tint v1.1.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/wailsapp/go-webview2 v1.0.23 // indirect
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
147
pkg/display/go.sum
Normal file
147
pkg/display/go.sum
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/godbus/dbus/v5 v5.2.0 h1:3WexO+U+yg9T70v9FdHr9kCxYlazaAXUhx2VMkbfax8=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 h1:njuLRcjAuMKr7kI3D85AXWkw6/+v9PwtV6M6o11sWHQ=
|
||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
|
||||
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
|
||||
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
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/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M=
|
||||
github.com/leaanthony/u v1.1.1/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
|
||||
github.com/lmittmann/tint v1.1.2 h1:2CQzrL6rslrsyjqLDwD11bZ5OpLBPU+g3G/r5LSfS8w=
|
||||
github.com/lmittmann/tint v1.1.2/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/wailsapp/go-webview2 v1.0.23 h1:jmv8qhz1lHibCc79bMM/a/FqOnnzOGEisLav+a0b9P0=
|
||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.41 h1:DYcC1/vtO862sxnoyCOMfLLypbzpFWI257fR6zDYY+Y=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
33
pkg/display/menu.go
Normal file
33
pkg/display/menu.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// buildMenu creates and sets the main application menu. This function is called
|
||||
// during the startup of the display service.
|
||||
func (s *Service) buildMenu() {
|
||||
appMenu := s.app.Menu.New()
|
||||
if runtime.GOOS == "darwin" {
|
||||
appMenu.AddRole(application.AppMenu)
|
||||
}
|
||||
appMenu.AddRole(application.FileMenu)
|
||||
appMenu.AddRole(application.ViewMenu)
|
||||
appMenu.AddRole(application.EditMenu)
|
||||
|
||||
workspace := appMenu.AddSubmenu("Workspace")
|
||||
workspace.Add("New").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
workspace.Add("List").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
|
||||
// Add brand-specific menu items
|
||||
//if s.brand == DeveloperHub {
|
||||
// appMenu.AddSubmenu("Developer")
|
||||
//}
|
||||
|
||||
appMenu.AddRole(application.WindowMenu)
|
||||
appMenu.AddRole(application.HelpMenu)
|
||||
|
||||
s.app.Menu.Set(appMenu)
|
||||
}
|
||||
73
pkg/display/tray.go
Normal file
73
pkg/display/tray.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package display
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
// systemTray configures and creates the system tray icon and menu. This
|
||||
// function is called during the startup of the display service.
|
||||
func (s *Service) systemTray() {
|
||||
|
||||
systray := s.app.SystemTray.New()
|
||||
systray.SetTooltip("Core")
|
||||
systray.SetLabel("Core")
|
||||
//appTrayIcon, _ := d.assets.ReadFile("assets/apptray.png")
|
||||
//
|
||||
//if runtime.GOOS == "darwin" {
|
||||
// systray.SetTemplateIcon(appTrayIcon)
|
||||
//} else {
|
||||
// // Support for light/dark mode icons
|
||||
// systray.SetDarkModeIcon(appTrayIcon)
|
||||
// systray.SetIcon(appTrayIcon)
|
||||
//}
|
||||
// Create a hidden window for the system tray menu to interact with
|
||||
trayWindow := s.app.Window.NewWithOptions(application.WebviewWindowOptions{
|
||||
Name: "system-tray",
|
||||
Title: "System Tray Status",
|
||||
URL: "system-tray.html",
|
||||
Width: 400,
|
||||
Frameless: true,
|
||||
Hidden: true,
|
||||
})
|
||||
systray.AttachWindow(trayWindow).WindowOffset(5)
|
||||
|
||||
// --- Build Tray Menu ---
|
||||
trayMenu := s.app.Menu.New()
|
||||
trayMenu.Add("Open Desktop").OnClick(func(ctx *application.Context) {
|
||||
for _, window := range s.app.Window.GetAll() {
|
||||
window.Show()
|
||||
}
|
||||
})
|
||||
trayMenu.Add("Close Desktop").OnClick(func(ctx *application.Context) {
|
||||
for _, window := range s.app.Window.GetAll() {
|
||||
window.Hide()
|
||||
}
|
||||
})
|
||||
|
||||
trayMenu.Add("Environment Info").OnClick(func(ctx *application.Context) {
|
||||
s.ShowEnvironmentDialog()
|
||||
})
|
||||
// Add brand-specific menu items
|
||||
//switch d.brand {
|
||||
//case AdminHub:
|
||||
// trayMenu.Add("Manage Workspace").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case ServerHub:
|
||||
// trayMenu.Add("Server Control").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case GatewayHub:
|
||||
// trayMenu.Add("Routing Table").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case DeveloperHub:
|
||||
// trayMenu.Add("Debug Console").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//case ClientHub:
|
||||
// trayMenu.Add("Connect").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
// trayMenu.Add("Disconnect").OnClick(func(ctx *application.Context) { /* TODO */ })
|
||||
//}
|
||||
|
||||
trayMenu.AddSeparator()
|
||||
trayMenu.Add("Quit").OnClick(func(ctx *application.Context) {
|
||||
s.app.Quit()
|
||||
})
|
||||
|
||||
systray.SetMenu(trayMenu)
|
||||
}
|
||||
17
pkg/display/ui/.editorconfig
Normal file
17
pkg/display/ui/.editorconfig
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
ij_typescript_use_double_quotes = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
43
pkg/display/ui/.gitignore
vendored
Normal file
43
pkg/display/ui/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
__screenshots__/
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
4
pkg/display/ui/.vscode/extensions.json
vendored
Normal file
4
pkg/display/ui/.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
||||
20
pkg/display/ui/.vscode/launch.json
vendored
Normal file
20
pkg/display/ui/.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
pkg/display/ui/.vscode/tasks.json
vendored
Normal file
42
pkg/display/ui/.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
59
pkg/display/ui/README.md
Normal file
59
pkg/display/ui/README.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# CoreElementTemplate
|
||||
|
||||
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.9.
|
||||
|
||||
## Development server
|
||||
|
||||
To start a local development server, run:
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
```
|
||||
|
||||
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
|
||||
|
||||
```bash
|
||||
ng generate component component-name
|
||||
```
|
||||
|
||||
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
|
||||
|
||||
```bash
|
||||
ng generate --help
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To build the project run:
|
||||
|
||||
```bash
|
||||
ng build
|
||||
```
|
||||
|
||||
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
For end-to-end (e2e) testing, run:
|
||||
|
||||
```bash
|
||||
ng e2e
|
||||
```
|
||||
|
||||
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||
81
pkg/display/ui/angular.json
Normal file
81
pkg/display/ui/angular.json
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"display": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"standalone": false
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"standalone": false
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"standalone": false
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular/build:application",
|
||||
"options": {
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "public"
|
||||
}
|
||||
],
|
||||
"styles": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "1MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "4kB",
|
||||
"maximumError": "8kB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "none"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "display:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "display:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular/build:extract-i18n"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8316
pkg/display/ui/package-lock.json
generated
Normal file
8316
pkg/display/ui/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
49
pkg/display/ui/package.json
Normal file
49
pkg/display/ui/package.json
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "display",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^20.3.0",
|
||||
"@angular/compiler": "^20.3.0",
|
||||
"@angular/core": "^20.3.0",
|
||||
"@angular/elements": "^20.3.10",
|
||||
"@angular/forms": "^20.3.0",
|
||||
"@angular/platform-browser": "^20.3.0",
|
||||
"@angular/router": "^20.3.0",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/build": "^20.3.9",
|
||||
"@angular/cli": "^20.3.9",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.9.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
"karma-coverage": "~2.2.0",
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.9.2"
|
||||
}
|
||||
}
|
||||
BIN
pkg/display/ui/public/favicon.ico
Normal file
BIN
pkg/display/ui/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
23
pkg/display/ui/src/app/app-module.ts
Normal file
23
pkg/display/ui/src/app/app-module.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { DoBootstrap, Injector, NgModule, provideBrowserGlobalErrorListeners } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { createCustomElement } from '@angular/elements';
|
||||
|
||||
import { App } from './app';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
App
|
||||
],
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners()
|
||||
]
|
||||
})
|
||||
export class AppModule implements DoBootstrap {
|
||||
constructor(private injector: Injector) {
|
||||
const el = createCustomElement(App, { injector });
|
||||
customElements.define('core-display', el);
|
||||
}
|
||||
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
1
pkg/display/ui/src/app/app.html
Normal file
1
pkg/display/ui/src/app/app.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<h1>Hello, {{ title() }}</h1>
|
||||
10
pkg/display/ui/src/app/app.ts
Normal file
10
pkg/display/ui/src/app/app.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Component, signal } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'core-display',
|
||||
templateUrl: './app.html',
|
||||
standalone: true
|
||||
})
|
||||
export class App {
|
||||
protected readonly title = signal('display');
|
||||
}
|
||||
13
pkg/display/ui/src/index.html
Normal file
13
pkg/display/ui/src/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Display</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<core-display></core-display>
|
||||
</body>
|
||||
</html>
|
||||
7
pkg/display/ui/src/main.ts
Normal file
7
pkg/display/ui/src/main.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { platformBrowser } from '@angular/platform-browser';
|
||||
import { AppModule } from './app/app-module';
|
||||
|
||||
platformBrowser().bootstrapModule(AppModule, {
|
||||
ngZoneEventCoalescing: true,
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
1
pkg/display/ui/src/styles.css
Normal file
1
pkg/display/ui/src/styles.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
15
pkg/display/ui/tsconfig.app.json
Normal file
15
pkg/display/ui/tsconfig.app.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
34
pkg/display/ui/tsconfig.json
Normal file
34
pkg/display/ui/tsconfig.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
pkg/display/ui/tsconfig.spec.json
Normal file
14
pkg/display/ui/tsconfig.spec.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
||||
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
111
pkg/display/window.go
Normal file
111
pkg/display/window.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package display
|
||||
|
||||
import "github.com/wailsapp/wails/v3/pkg/application"
|
||||
|
||||
// WindowConfig holds the configuration for a window. This struct is used to
|
||||
// create a new window with the specified options.
|
||||
type WindowConfig struct {
|
||||
Name string
|
||||
Title string
|
||||
Width int
|
||||
Height int
|
||||
URL string
|
||||
AlwaysOnTop bool
|
||||
Hidden bool
|
||||
MinimiseButtonState application.ButtonState
|
||||
MaximiseButtonState application.ButtonState
|
||||
CloseButtonState application.ButtonState
|
||||
Frameless bool
|
||||
}
|
||||
|
||||
// WindowOption is an interface for applying configuration options to a
|
||||
// WindowConfig.
|
||||
type WindowOption interface {
|
||||
Apply(*WindowConfig)
|
||||
}
|
||||
|
||||
// WindowOptionFunc is a function that implements the WindowOption interface.
|
||||
// This allows us to use ordinary functions as window options.
|
||||
type WindowOptionFunc func(*WindowConfig)
|
||||
|
||||
// Apply calls the underlying function to apply the configuration.
|
||||
func (f WindowOptionFunc) Apply(c *WindowConfig) {
|
||||
f(c)
|
||||
}
|
||||
|
||||
// WithName sets the name of the window.
|
||||
func WithName(name string) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.Name = name
|
||||
})
|
||||
}
|
||||
|
||||
// WithTitle sets the title of the window.
|
||||
func WithTitle(title string) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.Title = title
|
||||
})
|
||||
}
|
||||
|
||||
// WithWidth sets the width of the window.
|
||||
func WithWidth(width int) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.Width = width
|
||||
})
|
||||
}
|
||||
|
||||
// WithHeight sets the height of the window.
|
||||
func WithHeight(height int) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.Height = height
|
||||
})
|
||||
}
|
||||
|
||||
// WithURL sets the URL that the window will load.
|
||||
func WithURL(url string) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.URL = url
|
||||
})
|
||||
}
|
||||
|
||||
// WithAlwaysOnTop sets the window to always be on top of other windows.
|
||||
func WithAlwaysOnTop(alwaysOnTop bool) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.AlwaysOnTop = alwaysOnTop
|
||||
})
|
||||
}
|
||||
|
||||
// WithHidden sets the window to be hidden when it is created.
|
||||
func WithHidden(hidden bool) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.Hidden = hidden
|
||||
})
|
||||
}
|
||||
|
||||
// WithMinimiseButtonState sets the state of the minimise button.
|
||||
func WithMinimiseButtonState(state application.ButtonState) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.MinimiseButtonState = state
|
||||
})
|
||||
}
|
||||
|
||||
// WithMaximiseButtonState sets the state of the maximise button.
|
||||
func WithMaximiseButtonState(state application.ButtonState) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.MaximiseButtonState = state
|
||||
})
|
||||
}
|
||||
|
||||
// WithCloseButtonState sets the state of the close button.
|
||||
func WithCloseButtonState(state application.ButtonState) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.CloseButtonState = state
|
||||
})
|
||||
}
|
||||
|
||||
// WithFrameless sets the window to be frameless.
|
||||
func WithFrameless(frameless bool) WindowOption {
|
||||
return WindowOptionFunc(func(c *WindowConfig) {
|
||||
c.Frameless = frameless
|
||||
})
|
||||
}
|
||||
248
pkg/help/.github/copilot-instructions.md
vendored
Normal file
248
pkg/help/.github/copilot-instructions.md
vendored
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
# Copilot Instructions for Help Module
|
||||
|
||||
## Project Overview
|
||||
|
||||
This repository contains the `help` module, which was formerly part of the `Snider/Core` framework. The module provides assistance and documentation functionality for applications, allowing them to embed and display help documentation.
|
||||
|
||||
The project consists of three main components:
|
||||
|
||||
1. **Go Library**: A Go module that provides a help service for displaying documentation
|
||||
2. **Documentation**: MkDocs-based documentation that can be embedded in applications
|
||||
3. **Angular UI Component**: A custom HTML element built with Angular Elements for the help interface
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **Go Library**: Go 1.25 with embedded file systems
|
||||
- **Documentation**: MkDocs Material theme with Python
|
||||
- **UI Component**: Angular 20.3+ with Angular Elements
|
||||
- **Build Tools**:
|
||||
- GoReleaser for Go library releases
|
||||
- Angular CLI for UI builds
|
||||
- Task (Taskfile) for documentation builds
|
||||
- **Package Management**:
|
||||
- Go modules for Go dependencies
|
||||
- pip for Python/MkDocs dependencies
|
||||
- npm for Node.js/Angular dependencies
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
.
|
||||
├── help.go # Main Go library implementation
|
||||
├── help_test.go # Go tests
|
||||
├── go.mod # Go module definition
|
||||
├── examples/ # Example applications
|
||||
│ ├── show_help/ # Example: showing help
|
||||
│ └── show_at/ # Example: showing help at specific anchor
|
||||
├── src/ # MkDocs documentation source
|
||||
│ ├── index.md # Main documentation page
|
||||
│ ├── images/ # Documentation images
|
||||
│ └── stylesheets/ # Custom CSS
|
||||
├── public/ # Built documentation (embedded in Go binary)
|
||||
├── ui/ # Angular help UI component
|
||||
│ ├── src/ # Angular source code
|
||||
│ ├── public/ # Static assets
|
||||
│ └── package.json # Node.js dependencies
|
||||
├── mkdocs.yml # MkDocs configuration
|
||||
├── taskfile.dist.yml # Task runner configuration
|
||||
└── .github/ # GitHub workflows and configurations
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.25 or later
|
||||
- Python 3 with pip
|
||||
- Node.js and npm
|
||||
- Task (task runner) - optional but recommended
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. Install Go dependencies:
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
2. Install documentation dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Install UI dependencies (if working on the Angular component):
|
||||
```bash
|
||||
cd ui
|
||||
npm install
|
||||
```
|
||||
|
||||
### Running the Documentation Server
|
||||
|
||||
Using Task (recommended):
|
||||
```bash
|
||||
task dev
|
||||
```
|
||||
|
||||
Or using MkDocs directly:
|
||||
```bash
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
This starts a live-reloading development server for the documentation at `http://localhost:8000`.
|
||||
|
||||
## Building
|
||||
|
||||
### Build Documentation
|
||||
|
||||
The documentation is built into the `public` directory and embedded in the Go binary:
|
||||
|
||||
```bash
|
||||
task build
|
||||
# or
|
||||
mkdocs build --clean -d public
|
||||
```
|
||||
|
||||
### Build UI Component
|
||||
|
||||
```bash
|
||||
cd ui
|
||||
npm run build
|
||||
```
|
||||
|
||||
This creates the Angular custom element in the `dist` directory.
|
||||
|
||||
## Testing
|
||||
|
||||
### Go Tests
|
||||
|
||||
Run all Go tests with coverage:
|
||||
```bash
|
||||
go test -v -coverprofile=coverage.out ./...
|
||||
```
|
||||
|
||||
The test suite includes:
|
||||
- Service initialization tests
|
||||
- Display functionality tests
|
||||
- Error handling tests
|
||||
- Custom source and asset loading tests
|
||||
|
||||
### UI Tests
|
||||
|
||||
Run Angular tests:
|
||||
```bash
|
||||
cd ui
|
||||
npm test
|
||||
```
|
||||
|
||||
## Code Style and Conventions
|
||||
|
||||
### Go Code
|
||||
|
||||
- Follow standard Go conventions and formatting (use `gofmt` or `go fmt`)
|
||||
- Use meaningful variable and function names
|
||||
- Keep functions focused and single-purpose
|
||||
- Follow the existing patterns for interfaces (Logger, App, Core, Display, Help)
|
||||
- Use context.Context for service lifecycle management
|
||||
- Embed static assets using `//go:embed` directives
|
||||
|
||||
### Documentation (Markdown)
|
||||
|
||||
- Follow MkDocs Material conventions
|
||||
- Use clear, concise language
|
||||
- Include code examples where appropriate
|
||||
- Keep line length reasonable for readability
|
||||
- Use proper heading hierarchy
|
||||
|
||||
### Angular/TypeScript Code
|
||||
|
||||
- Follow Angular style guide
|
||||
- Use Prettier for code formatting (configuration in `ui/package.json`)
|
||||
- Settings:
|
||||
- Print width: 100 characters
|
||||
- Single quotes preferred
|
||||
- Angular parser for HTML files
|
||||
|
||||
### General Guidelines
|
||||
|
||||
- Write clear, self-documenting code
|
||||
- Add comments for complex logic or non-obvious decisions
|
||||
- Keep commits atomic and well-described
|
||||
- Follow the existing code patterns in the repository
|
||||
|
||||
## API Overview
|
||||
|
||||
### Main Interfaces
|
||||
|
||||
- **Help**: Main interface for the help service with `Show()`, `ShowAt(anchor string)`, and `ServiceStartup(ctx context.Context)` methods
|
||||
- **Core**: Application core interface providing ACTION dispatch and App access
|
||||
- **App**: Application interface providing Logger access
|
||||
- **Logger**: Logging interface with Info and Error methods
|
||||
- **Display**: Marker interface for display service dependency checking
|
||||
|
||||
### Options
|
||||
|
||||
The `Options` struct allows configuration:
|
||||
- `Source`: Path to custom static site directory
|
||||
- `Assets`: Custom `fs.FS` for documentation assets (can be `embed.FS` or any `fs.FS` implementation)
|
||||
|
||||
### Usage Pattern
|
||||
|
||||
```go
|
||||
// Initialize with default embedded documentation
|
||||
helpService, err := help.New(help.Options{})
|
||||
|
||||
// Or with custom embedded assets
|
||||
helpService, err := help.New(help.Options{
|
||||
Assets: myDocs,
|
||||
})
|
||||
|
||||
// Or with custom directory source
|
||||
helpService, err := help.New(help.Options{
|
||||
Source: "path/to/docs",
|
||||
})
|
||||
|
||||
// Start the service
|
||||
err = helpService.ServiceStartup(ctx)
|
||||
|
||||
// Show help
|
||||
err = helpService.Show()
|
||||
|
||||
// Show help at specific location
|
||||
err = helpService.ShowAt("ui/how/settings#resetPassword")
|
||||
```
|
||||
|
||||
## Important Notes for AI Assistants
|
||||
|
||||
1. **Minimal Changes**: Make the smallest possible changes to achieve the goal
|
||||
2. **No Breaking Changes**: Don't modify working code unless necessary
|
||||
3. **Testing**: Always run `go test ./...` before and after changes to ensure nothing breaks
|
||||
4. **Build Verification**: Ensure `go build` succeeds after making changes
|
||||
5. **Dependencies**: Check for security vulnerabilities before adding new dependencies using `govulncheck` or similar security scanning tools
|
||||
6. **Module Name**: The Go module is `github.com/Snider/help` - do not change this
|
||||
7. **Embedded Assets**: The `public` directory is embedded in the Go binary via `//go:embed all:public/*`
|
||||
8. **Documentation**: When changing documentation, rebuild with `task build` or `mkdocs build`
|
||||
|
||||
## CI/CD Workflows
|
||||
|
||||
- **Go CI** (`.github/workflows/go.yml`): Runs tests and uploads coverage on push/PR to main
|
||||
- **Release** (`.github/workflows/release.yml`): Uses GoReleaser to build and publish releases
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing to this repository:
|
||||
|
||||
1. Run tests before making changes: `go test ./...`
|
||||
2. Make minimal, focused changes
|
||||
3. Run tests after changes to verify nothing broke
|
||||
4. Follow the existing code style
|
||||
5. Update documentation if the API changes
|
||||
6. Ensure all CI checks pass
|
||||
7. Keep commits atomic with clear messages
|
||||
|
||||
## Common Tasks
|
||||
|
||||
- **Run tests**: `go test -v ./...`
|
||||
- **Run tests with coverage**: `go test -v -coverprofile=coverage.out ./...`
|
||||
- **Build documentation**: `task build` or `mkdocs build --clean -d public`
|
||||
- **Serve documentation**: `task dev` or `mkdocs serve`
|
||||
- **Format Go code**: `go fmt ./...`
|
||||
- **Tidy dependencies**: `go mod tidy`
|
||||
31
pkg/help/.github/workflows/go.yml
vendored
Normal file
31
pkg/help/.github/workflows/go.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
name: Go CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.25'
|
||||
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
||||
|
||||
- name: Install dependencies
|
||||
run: go mod tidy
|
||||
|
||||
- name: Run tests and generate coverage report
|
||||
run: go test -v -coverprofile=coverage.out ./...
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
24
pkg/help/.github/workflows/release.yml
vendored
Normal file
24
pkg/help/.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
18
pkg/help/.gitignore
vendored
Normal file
18
pkg/help/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Go
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.test
|
||||
*.out
|
||||
*.prof
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
dist/
|
||||
.DS_Store
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
demo-cli
|
||||
43
pkg/help/.goreleaser.yaml
Normal file
43
pkg/help/.goreleaser.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
# you may remove this if you don't need go generate
|
||||
- go generate ./...
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
archives:
|
||||
- replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
brews:
|
||||
- name: help
|
||||
tap:
|
||||
owner: Snider
|
||||
name: homebrew-tap
|
||||
commit_author:
|
||||
name: goreleaserbot
|
||||
email: goreleaser@carlosbecker.com
|
||||
homepage: "https://github.com/Snider/help"
|
||||
description: "Help module for the core framework"
|
||||
license: "EUPL-1.2"
|
||||
287
pkg/help/LICENSE
Normal file
287
pkg/help/LICENSE
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
EUROPEAN UNION PUBLIC LICENCE v. 1.2
|
||||
EUPL © the European Union 2007, 2016
|
||||
|
||||
This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined
|
||||
below) which is provided under the terms of this Licence. Any use of the Work,
|
||||
other than as authorised under this Licence is prohibited (to the extent such
|
||||
use is covered by a right of the copyright holder of the Work).
|
||||
|
||||
The Work is provided under the terms of this Licence when the Licensor (as
|
||||
defined below) has placed the following notice immediately following the
|
||||
copyright notice for the Work:
|
||||
|
||||
Licensed under the EUPL
|
||||
|
||||
or has expressed by any other means his willingness to license under the EUPL.
|
||||
|
||||
1. Definitions
|
||||
|
||||
In this Licence, the following terms have the following meaning:
|
||||
|
||||
- ‘The Licence’: this Licence.
|
||||
|
||||
- ‘The Original Work’: the work or software distributed or communicated by the
|
||||
Licensor under this Licence, available as Source Code and also as Executable
|
||||
Code as the case may be.
|
||||
|
||||
- ‘Derivative Works’: the works or software that could be created by the
|
||||
Licensee, based upon the Original Work or modifications thereof. This Licence
|
||||
does not define the extent of modification or dependence on the Original Work
|
||||
required in order to classify a work as a Derivative Work; this extent is
|
||||
determined by copyright law applicable in the country mentioned in Article 15.
|
||||
|
||||
- ‘The Work’: the Original Work or its Derivative Works.
|
||||
|
||||
- ‘The Source Code’: the human-readable form of the Work which is the most
|
||||
convenient for people to study and modify.
|
||||
|
||||
- ‘The Executable Code’: any code which has generally been compiled and which is
|
||||
meant to be interpreted by a computer as a program.
|
||||
|
||||
- ‘The Licensor’: the natural or legal person that distributes or communicates
|
||||
the Work under the Licence.
|
||||
|
||||
- ‘Contributor(s)’: any natural or legal person who modifies the Work under the
|
||||
Licence, or otherwise contributes to the creation of a Derivative Work.
|
||||
|
||||
- ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of
|
||||
the Work under the terms of the Licence.
|
||||
|
||||
- ‘Distribution’ or ‘Communication’: any act of selling, giving, lending,
|
||||
renting, distributing, communicating, transmitting, or otherwise making
|
||||
available, online or offline, copies of the Work or providing access to its
|
||||
essential functionalities at the disposal of any other natural or legal
|
||||
person.
|
||||
|
||||
2. Scope of the rights granted by the Licence
|
||||
|
||||
The Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
|
||||
sublicensable licence to do the following, for the duration of copyright vested
|
||||
in the Original Work:
|
||||
|
||||
- use the Work in any circumstance and for all usage,
|
||||
- reproduce the Work,
|
||||
- modify the Work, and make Derivative Works based upon the Work,
|
||||
- communicate to the public, including the right to make available or display
|
||||
the Work or copies thereof to the public and perform publicly, as the case may
|
||||
be, the Work,
|
||||
- distribute the Work or copies thereof,
|
||||
- lend and rent the Work or copies thereof,
|
||||
- sublicense rights in the Work or copies thereof.
|
||||
|
||||
Those rights can be exercised on any media, supports and formats, whether now
|
||||
known or later invented, as far as the applicable law permits so.
|
||||
|
||||
In the countries where moral rights apply, the Licensor waives his right to
|
||||
exercise his moral right to the extent allowed by law in order to make effective
|
||||
the licence of the economic rights here above listed.
|
||||
|
||||
The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to
|
||||
any patents held by the Licensor, to the extent necessary to make use of the
|
||||
rights granted on the Work under this Licence.
|
||||
|
||||
3. Communication of the Source Code
|
||||
|
||||
The Licensor may provide the Work either in its Source Code form, or as
|
||||
Executable Code. If the Work is provided as Executable Code, the Licensor
|
||||
provides in addition a machine-readable copy of the Source Code of the Work
|
||||
along with each copy of the Work that the Licensor distributes or indicates, in
|
||||
a notice following the copyright notice attached to the Work, a repository where
|
||||
the Source Code is easily and freely accessible for as long as the Licensor
|
||||
continues to distribute or communicate the Work.
|
||||
|
||||
4. Limitations on copyright
|
||||
|
||||
Nothing in this Licence is intended to deprive the Licensee of the benefits from
|
||||
any exception or limitation to the exclusive rights of the rights owners in the
|
||||
Work, of the exhaustion of those rights or of other applicable limitations
|
||||
thereto.
|
||||
|
||||
5. Obligations of the Licensee
|
||||
|
||||
The grant of the rights mentioned above is subject to some restrictions and
|
||||
obligations imposed on the Licensee. Those obligations are the following:
|
||||
|
||||
Attribution right: The Licensee shall keep intact all copyright, patent or
|
||||
trademarks notices and all notices that refer to the Licence and to the
|
||||
disclaimer of warranties. The Licensee must include a copy of such notices and a
|
||||
copy of the Licence with every copy of the Work he/she distributes or
|
||||
communicates. The Licensee must cause any Derivative Work to carry prominent
|
||||
notices stating that the Work has been modified and the date of modification.
|
||||
|
||||
Copyleft clause: If the Licensee distributes or communicates copies of the
|
||||
Original Works or Derivative Works, this Distribution or Communication will be
|
||||
done under the terms of this Licence or of a later version of this Licence
|
||||
unless the Original Work is expressly distributed only under this version of the
|
||||
Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee
|
||||
(becoming Licensor) cannot offer or impose any additional terms or conditions on
|
||||
the Work or Derivative Work that alter or restrict the terms of the Licence.
|
||||
|
||||
Compatibility clause: If the Licensee Distributes or Communicates Derivative
|
||||
Works or copies thereof based upon both the Work and another work licensed under
|
||||
a Compatible Licence, this Distribution or Communication can be done under the
|
||||
terms of this Compatible Licence. For the sake of this clause, ‘Compatible
|
||||
Licence’ refers to the licences listed in the appendix attached to this Licence.
|
||||
Should the Licensee's obligations under the Compatible Licence conflict with
|
||||
his/her obligations under this Licence, the obligations of the Compatible
|
||||
Licence shall prevail.
|
||||
|
||||
Provision of Source Code: When distributing or communicating copies of the Work,
|
||||
the Licensee will provide a machine-readable copy of the Source Code or indicate
|
||||
a repository where this Source will be easily and freely available for as long
|
||||
as the Licensee continues to distribute or communicate the Work.
|
||||
|
||||
Legal Protection: This Licence does not grant permission to use the trade names,
|
||||
trademarks, service marks, or names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the copyright notice.
|
||||
|
||||
6. Chain of Authorship
|
||||
|
||||
The original Licensor warrants that the copyright in the Original Work granted
|
||||
hereunder is owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each Contributor warrants that the copyright in the modifications he/she brings
|
||||
to the Work are owned by him/her or licensed to him/her and that he/she has the
|
||||
power and authority to grant the Licence.
|
||||
|
||||
Each time You accept the Licence, the original Licensor and subsequent
|
||||
Contributors grant You a licence to their contributions to the Work, under the
|
||||
terms of this Licence.
|
||||
|
||||
7. Disclaimer of Warranty
|
||||
|
||||
The Work is a work in progress, which is continuously improved by numerous
|
||||
Contributors. It is not a finished work and may therefore contain defects or
|
||||
‘bugs’ inherent to this type of development.
|
||||
|
||||
For the above reason, the Work is provided under the Licence on an ‘as is’ basis
|
||||
and without warranties of any kind concerning the Work, including without
|
||||
limitation merchantability, fitness for a particular purpose, absence of defects
|
||||
or errors, accuracy, non-infringement of intellectual property rights other than
|
||||
copyright as stated in Article 6 of this Licence.
|
||||
|
||||
This disclaimer of warranty is an essential part of the Licence and a condition
|
||||
for the grant of any rights to the Work.
|
||||
|
||||
8. Disclaimer of Liability
|
||||
|
||||
Except in the cases of wilful misconduct or damages directly caused to natural
|
||||
persons, the Licensor will in no event be liable for any direct or indirect,
|
||||
material or moral, damages of any kind, arising out of the Licence or of the use
|
||||
of the Work, including without limitation, damages for loss of goodwill, work
|
||||
stoppage, computer failure or malfunction, loss of data or any commercial
|
||||
damage, even if the Licensor has been advised of the possibility of such damage.
|
||||
However, the Licensor will be liable under statutory product liability laws as
|
||||
far such laws apply to the Work.
|
||||
|
||||
9. Additional agreements
|
||||
|
||||
While distributing the Work, You may choose to conclude an additional agreement,
|
||||
defining obligations or services consistent with this Licence. However, if
|
||||
accepting obligations, You may act only on your own behalf and on your sole
|
||||
responsibility, not on behalf of the original Licensor or any other Contributor,
|
||||
and only if You agree to indemnify, defend, and hold each Contributor harmless
|
||||
for any liability incurred by, or claims asserted against such Contributor by
|
||||
the fact You have accepted any warranty or additional liability.
|
||||
|
||||
10. Acceptance of the Licence
|
||||
|
||||
The provisions of this Licence can be accepted by clicking on an icon ‘I agree’
|
||||
placed under the bottom of a window displaying the text of this Licence or by
|
||||
affirming consent in any other similar way, in accordance with the rules of
|
||||
applicable law. Clicking on that icon indicates your clear and irrevocable
|
||||
acceptance of this Licence and all of its terms and conditions.
|
||||
|
||||
Similarly, you irrevocably accept this Licence and all of its terms and
|
||||
conditions by exercising any rights granted to You by Article 2 of this Licence,
|
||||
such as the use of the Work, the creation by You of a Derivative Work or the
|
||||
Distribution or Communication by You of the Work or copies thereof.
|
||||
|
||||
11. Information to the public
|
||||
|
||||
In case of any Distribution or Communication of the Work by means of electronic
|
||||
communication by You (for example, by offering to download the Work from a
|
||||
remote location) the distribution channel or media (for example, a website) must
|
||||
at least provide to the public the information requested by the applicable law
|
||||
regarding the Licensor, the Licence and the way it may be accessible, concluded,
|
||||
stored and reproduced by the Licensee.
|
||||
|
||||
12. Termination of the Licence
|
||||
|
||||
The Licence and the rights granted hereunder will terminate automatically upon
|
||||
any breach by the Licensee of the terms of the Licence.
|
||||
|
||||
Such a termination will not terminate the licences of any person who has
|
||||
received the Work from the Licensee under the Licence, provided such persons
|
||||
remain in full compliance with the Licence.
|
||||
|
||||
13. Miscellaneous
|
||||
|
||||
Without prejudice of Article 9 above, the Licence represents the complete
|
||||
agreement between the Parties as to the Work.
|
||||
|
||||
If any provision of the Licence is invalid or unenforceable under applicable
|
||||
law, this will not affect the validity or enforceability of the Licence as a
|
||||
whole. Such provision will be construed or reformed so as necessary to make it
|
||||
valid and enforceable.
|
||||
|
||||
The European Commission may publish other linguistic versions or new versions of
|
||||
this Licence or updated versions of the Appendix, so far this is required and
|
||||
reasonable, without reducing the scope of the rights granted by the Licence. New
|
||||
versions of the Licence will be published with a unique version number.
|
||||
|
||||
All linguistic versions of this Licence, approved by the European Commission,
|
||||
have identical value. Parties can take advantage of the linguistic version of
|
||||
their choice.
|
||||
|
||||
14. Jurisdiction
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- any litigation resulting from the interpretation of this License, arising
|
||||
between the European Union institutions, bodies, offices or agencies, as a
|
||||
Licensor, and any Licensee, will be subject to the jurisdiction of the Court
|
||||
of Justice of the European Union, as laid down in article 272 of the Treaty on
|
||||
the Functioning of the European Union,
|
||||
|
||||
- any litigation arising between other parties and resulting from the
|
||||
interpretation of this License, will be subject to the exclusive jurisdiction
|
||||
of the competent court where the Licensor resides or conducts its primary
|
||||
business.
|
||||
|
||||
15. Applicable Law
|
||||
|
||||
Without prejudice to specific agreement between parties,
|
||||
|
||||
- this Licence shall be governed by the law of the European Union Member State
|
||||
where the Licensor has his seat, resides or has his registered office,
|
||||
|
||||
- this licence shall be governed by Belgian law if the Licensor has no seat,
|
||||
residence or registered office inside a European Union Member State.
|
||||
|
||||
Appendix
|
||||
|
||||
‘Compatible Licences’ according to Article 5 EUPL are:
|
||||
|
||||
- GNU General Public License (GPL) v. 2, v. 3
|
||||
- GNU Affero General Public License (AGPL) v. 3
|
||||
- Open Software License (OSL) v. 2.1, v. 3.0
|
||||
- Eclipse Public License (EPL) v. 1.0
|
||||
- CeCILL v. 2.0, v. 2.1
|
||||
- Mozilla Public Licence (MPL) v. 2
|
||||
- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3
|
||||
- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for
|
||||
works other than software
|
||||
- European Union Public Licence (EUPL) v. 1.1, v. 1.2
|
||||
- Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong
|
||||
Reciprocity (LiLiQ-R+).
|
||||
|
||||
The European Commission may update this Appendix to later versions of the above
|
||||
licences without producing a new version of the EUPL, as long as they provide
|
||||
the rights granted in Article 2 of this Licence and protect the covered Source
|
||||
Code from exclusive appropriation.
|
||||
|
||||
All other changes or additions to this Appendix require the production of a new
|
||||
EUPL version.
|
||||
92
pkg/help/README.md
Normal file
92
pkg/help/README.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
# Help Module
|
||||
|
||||
[](https://pkg.go.dev/github.com/Snider/help)
|
||||
[](https://goreportcard.com/report/github.com/Snider/help)
|
||||
[](https://codecov.io/gh/Snider/help)
|
||||
[](https://github.com/Snider/help/actions/workflows/go.yml)
|
||||
[](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12)
|
||||
|
||||
This repository contains the `help` module, which was formerly part of the `Snider/Core` framework. This module provides assistance and documentation functionality.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project uses `mkdocs-material` to build the documentation. To get started, you will need to have Python and `pip` installed.
|
||||
|
||||
1. **Install the dependencies:**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Run the development server:**
|
||||
```bash
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To use the `help` module, you first need to import it in your Go project:
|
||||
|
||||
```go
|
||||
import "github.com/Snider/help"
|
||||
```
|
||||
|
||||
Next, initialize the help service by calling the `New` function. The `New` function accepts an `Options` struct, which allows you to configure the documentation source.
|
||||
|
||||
### Using a custom `embed.FS`
|
||||
|
||||
You can provide your own `embed.FS` as a documentation source. This is useful when you want to bundle the documentation with your application.
|
||||
|
||||
```go
|
||||
import (
|
||||
"embed"
|
||||
"github.com/Snider/help"
|
||||
)
|
||||
|
||||
//go:embed all:my-docs/build
|
||||
var myDocs embed.FS
|
||||
|
||||
func main() {
|
||||
helpService, err := help.New(help.Options{
|
||||
Assets: myDocs,
|
||||
})
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Static Site Source
|
||||
|
||||
You can also provide a custom directory containing a static website as the documentation source. To do this, set the `Source` field in the `Options` struct to the path of your static site directory:
|
||||
|
||||
```go
|
||||
helpService, err := help.New(help.Options{
|
||||
Source: "path/to/your/static/site",
|
||||
})
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
Once the help service is initialized, you can use the `Show()` and `ShowAt()` methods to display the documentation.
|
||||
|
||||
### Displaying Help
|
||||
|
||||
The `Show()` method opens the help window to the main page.
|
||||
|
||||
```go
|
||||
err := helpService.Show()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
The `ShowAt()` method opens the help window to a specific anchor. The provided anchor is normalized into a URL. For example, calling `ShowAt("ui/how/settings#resetPassword")` will open the help window to a URL similar to `http://localhost:8080/docs/ui/how/settings/index.html#resetPassword`. The exact URL depends on how your display service resolves these paths.
|
||||
|
||||
```go
|
||||
err := helpService.ShowAt("ui/how/settings#resetPassword")
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue