refactor: move plans to tasks/, framework docs to core-gui
- plans/ → tasks/plans/ (planning documents) - framework/ → core-gui/docs/framework/ (GUI framework docs) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8601e5b355
commit
77a7237d71
31 changed files with 0 additions and 3529 deletions
|
|
@ -1,134 +0,0 @@
|
||||||
# Architecture
|
|
||||||
|
|
||||||
Core follows a modular, service-based architecture designed for maintainability and testability.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────┐
|
|
||||||
│ Wails Application │
|
|
||||||
├─────────────────────────────────────────────────────────┤
|
|
||||||
│ Core │
|
|
||||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
||||||
│ │ Display │ │ WebView │ │ MCP │ │ Config │ │
|
|
||||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
||||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
||||||
│ │ Crypt │ │ I18n │ │ IO │ │Workspace │ │
|
|
||||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
||||||
├─────────────────────────────────────────────────────────┤
|
|
||||||
│ Plugin System │
|
|
||||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
||||||
│ │ Plugin A │ │ Plugin B │ │ Plugin C │ │
|
|
||||||
│ └──────────┘ └──────────┘ └──────────┘ │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Container
|
|
||||||
|
|
||||||
The `Core` struct is the central service container:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Core struct {
|
|
||||||
services map[string]any // Service registry
|
|
||||||
actions []ActionHandler // IPC handlers
|
|
||||||
Features *Features // Feature flags
|
|
||||||
servicesLocked bool // Prevent late registration
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Registration
|
|
||||||
|
|
||||||
Services are registered using factory functions:
|
|
||||||
|
|
||||||
```go
|
|
||||||
core.New(
|
|
||||||
core.WithService(display.NewService), // Auto-discovered name
|
|
||||||
core.WithName("custom", myFactory), // Explicit name
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Retrieval
|
|
||||||
|
|
||||||
Type-safe service retrieval:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Returns error if not found
|
|
||||||
svc, err := core.ServiceFor[*display.Service](c, "display")
|
|
||||||
|
|
||||||
// Panics if not found (use in init code)
|
|
||||||
svc := core.MustServiceFor[*display.Service](c, "display")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Service Lifecycle
|
|
||||||
|
|
||||||
Services can implement lifecycle interfaces:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Called when app starts
|
|
||||||
type Startable interface {
|
|
||||||
OnStartup(ctx context.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when app shuts down
|
|
||||||
type Stoppable interface {
|
|
||||||
OnShutdown(ctx context.Context) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## IPC / Actions
|
|
||||||
|
|
||||||
Services communicate via the action system:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Register a handler
|
|
||||||
c.RegisterAction(func(c *core.Core, msg core.Message) error {
|
|
||||||
if msg.Type == "my-action" {
|
|
||||||
// Handle message
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// Send a message
|
|
||||||
c.ACTION(core.Message{
|
|
||||||
Type: "my-action",
|
|
||||||
Data: map[string]any{"key": "value"},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Bindings
|
|
||||||
|
|
||||||
Wails generates TypeScript bindings automatically:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Auto-generated from Go service
|
|
||||||
import { ShowNotification } from '@bindings/display/service';
|
|
||||||
|
|
||||||
await ShowNotification({
|
|
||||||
title: "Hello",
|
|
||||||
message: "From TypeScript!"
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Package Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
pkg/
|
|
||||||
├── core/ # Core container and interfaces
|
|
||||||
├── display/ # Window, tray, dialogs, clipboard
|
|
||||||
├── webview/ # JS execution, DOM, screenshots
|
|
||||||
├── mcp/ # Model Context Protocol server
|
|
||||||
├── config/ # Configuration persistence
|
|
||||||
├── crypt/ # Encryption and signing
|
|
||||||
├── i18n/ # Internationalization
|
|
||||||
├── io/ # File system helpers
|
|
||||||
├── workspace/ # Project management
|
|
||||||
├── plugin/ # Plugin system
|
|
||||||
└── module/ # Module system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Design Principles
|
|
||||||
|
|
||||||
1. **Dependency Injection**: Services receive dependencies via constructor
|
|
||||||
2. **Interface Segregation**: Small, focused interfaces
|
|
||||||
3. **Testability**: All services are mockable
|
|
||||||
4. **No Globals**: State contained in Core instance
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
# Config Service
|
|
||||||
|
|
||||||
The Config service (`pkg/config`) provides unified configuration management with automatic persistence, feature flags, and XDG-compliant directory paths.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- JSON configuration with auto-save
|
|
||||||
- Feature flag management
|
|
||||||
- XDG Base Directory support
|
|
||||||
- Struct serialization helpers
|
|
||||||
- Type-safe get/set operations
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/config"
|
|
||||||
|
|
||||||
// Standalone usage
|
|
||||||
cfg, err := config.New()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// With Core framework
|
|
||||||
c, _ := core.New(
|
|
||||||
core.WithService(config.Register),
|
|
||||||
)
|
|
||||||
cfg := core.MustServiceFor[*config.Service](c, "config")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Get & Set Values
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Set a value (auto-saves)
|
|
||||||
err := cfg.Set("language", "fr")
|
|
||||||
|
|
||||||
// Get a value
|
|
||||||
var lang string
|
|
||||||
err := cfg.Get("language", &lang)
|
|
||||||
```
|
|
||||||
|
|
||||||
Available configuration keys:
|
|
||||||
|
|
||||||
| Key | Type | Description |
|
|
||||||
|-----|------|-------------|
|
|
||||||
| `language` | string | UI language code |
|
|
||||||
| `default_route` | string | Default navigation route |
|
|
||||||
| `configDir` | string | Config files directory |
|
|
||||||
| `dataDir` | string | Data files directory |
|
|
||||||
| `cacheDir` | string | Cache directory |
|
|
||||||
| `workspaceDir` | string | Workspaces directory |
|
|
||||||
|
|
||||||
## Feature Flags
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Enable a feature
|
|
||||||
cfg.EnableFeature("dark_mode")
|
|
||||||
|
|
||||||
// Check if enabled
|
|
||||||
if cfg.IsFeatureEnabled("dark_mode") {
|
|
||||||
// Apply dark theme
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable a feature
|
|
||||||
cfg.DisableFeature("dark_mode")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Struct Serialization
|
|
||||||
|
|
||||||
Store complex data structures in separate JSON files:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type UserPrefs struct {
|
|
||||||
Theme string `json:"theme"`
|
|
||||||
Notifications bool `json:"notifications"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save struct to config/user_prefs.json
|
|
||||||
prefs := UserPrefs{Theme: "dark", Notifications: true}
|
|
||||||
err := cfg.SaveStruct("user_prefs", prefs)
|
|
||||||
|
|
||||||
// Load struct from file
|
|
||||||
var loaded UserPrefs
|
|
||||||
err := cfg.LoadStruct("user_prefs", &loaded)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directory Paths
|
|
||||||
|
|
||||||
The service automatically creates XDG-compliant directories:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Access directory paths
|
|
||||||
fmt.Println(cfg.ConfigDir) // ~/.config/lethean or ~/lethean/config
|
|
||||||
fmt.Println(cfg.DataDir) // Data storage
|
|
||||||
fmt.Println(cfg.CacheDir) // Cache files
|
|
||||||
fmt.Println(cfg.WorkspaceDir) // User workspaces
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Save
|
|
||||||
|
|
||||||
Changes are auto-saved, but you can save explicitly:
|
|
||||||
|
|
||||||
```go
|
|
||||||
err := cfg.Save()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Usage (TypeScript)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { Get, Set, IsFeatureEnabled } from '@bindings/config/service';
|
|
||||||
|
|
||||||
// Get configuration
|
|
||||||
const lang = await Get("language");
|
|
||||||
|
|
||||||
// Set configuration
|
|
||||||
await Set("default_route", "/dashboard");
|
|
||||||
|
|
||||||
// Check feature flag
|
|
||||||
if (await IsFeatureEnabled("dark_mode")) {
|
|
||||||
applyDarkTheme();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -1,312 +0,0 @@
|
||||||
# Core API Reference
|
|
||||||
|
|
||||||
Complete API reference for the Core framework (`pkg/core`).
|
|
||||||
|
|
||||||
## Core Struct
|
|
||||||
|
|
||||||
The central application container.
|
|
||||||
|
|
||||||
### Creation
|
|
||||||
|
|
||||||
```go
|
|
||||||
func New(opts ...Option) (*Core, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Creates a new Core instance with the specified options.
|
|
||||||
|
|
||||||
### Methods
|
|
||||||
|
|
||||||
#### Service Access
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ServiceFor[T any](c *Core, name string) (T, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Retrieves a service by name with type safety.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func MustServiceFor[T any](c *Core, name string) T
|
|
||||||
```
|
|
||||||
|
|
||||||
Retrieves a service by name, panics if not found or wrong type.
|
|
||||||
|
|
||||||
#### Actions
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Core) ACTION(msg Message) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Broadcasts a message to all registered action handlers.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Core) RegisterAction(handler func(*Core, Message) error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Registers an action handler.
|
|
||||||
|
|
||||||
#### Service Registration
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Core) AddService(name string, svc any) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Manually adds a service to the registry.
|
|
||||||
|
|
||||||
#### Config Access
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (c *Core) Config() *config.Service
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns the config service if registered.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
### WithService
|
|
||||||
|
|
||||||
```go
|
|
||||||
func WithService(factory ServiceFactory) Option
|
|
||||||
```
|
|
||||||
|
|
||||||
Registers a service using its factory function.
|
|
||||||
|
|
||||||
```go
|
|
||||||
c, _ := core.New(
|
|
||||||
core.WithService(config.Register),
|
|
||||||
core.WithService(display.NewService),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### WithName
|
|
||||||
|
|
||||||
```go
|
|
||||||
func WithName(name string, factory ServiceFactory) Option
|
|
||||||
```
|
|
||||||
|
|
||||||
Registers a service with an explicit name.
|
|
||||||
|
|
||||||
```go
|
|
||||||
c, _ := core.New(
|
|
||||||
core.WithName("mydb", database.NewService),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### WithAssets
|
|
||||||
|
|
||||||
```go
|
|
||||||
func WithAssets(assets embed.FS) Option
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets embedded assets for the application.
|
|
||||||
|
|
||||||
### WithServiceLock
|
|
||||||
|
|
||||||
```go
|
|
||||||
func WithServiceLock() Option
|
|
||||||
```
|
|
||||||
|
|
||||||
Prevents late service registration after initialization.
|
|
||||||
|
|
||||||
## ServiceFactory
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ServiceFactory func(c *Core) (any, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Factory function signature for service creation.
|
|
||||||
|
|
||||||
## Message
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Message interface{}
|
|
||||||
```
|
|
||||||
|
|
||||||
Messages can be any type. Common patterns:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Map-based message
|
|
||||||
c.ACTION(map[string]any{
|
|
||||||
"action": "user.created",
|
|
||||||
"id": "123",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Typed message
|
|
||||||
type UserCreated struct {
|
|
||||||
ID string
|
|
||||||
Email string
|
|
||||||
}
|
|
||||||
c.ACTION(UserCreated{ID: "123", Email: "user@example.com"})
|
|
||||||
```
|
|
||||||
|
|
||||||
## ServiceRuntime
|
|
||||||
|
|
||||||
Generic helper for services that need Core access.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ServiceRuntime[T any] struct {
|
|
||||||
core *Core
|
|
||||||
options T
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creation
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Methods
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (r *ServiceRuntime[T]) Core() *Core
|
|
||||||
func (r *ServiceRuntime[T]) Options() T
|
|
||||||
func (r *ServiceRuntime[T]) Config() *config.Service
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyOptions struct {
|
|
||||||
Timeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
type MyService struct {
|
|
||||||
*core.ServiceRuntime[MyOptions]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMyService(c *core.Core) (any, error) {
|
|
||||||
opts := MyOptions{Timeout: 30 * time.Second}
|
|
||||||
return &MyService{
|
|
||||||
ServiceRuntime: core.NewServiceRuntime(c, opts),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MyService) DoSomething() {
|
|
||||||
timeout := s.Options().Timeout
|
|
||||||
cfg := s.Config()
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lifecycle Interfaces
|
|
||||||
|
|
||||||
### Startable
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Startable interface {
|
|
||||||
OnStartup(ctx context.Context) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Implement for initialization on app start.
|
|
||||||
|
|
||||||
### Stoppable
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Stoppable interface {
|
|
||||||
OnShutdown(ctx context.Context) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Implement for cleanup on app shutdown.
|
|
||||||
|
|
||||||
### IPC Handler
|
|
||||||
|
|
||||||
```go
|
|
||||||
type IPCHandler interface {
|
|
||||||
HandleIPCEvents(c *Core, msg Message) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Automatically registered when using `WithService`.
|
|
||||||
|
|
||||||
## Built-in Actions
|
|
||||||
|
|
||||||
### ActionServiceStartup
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ActionServiceStartup struct{}
|
|
||||||
```
|
|
||||||
|
|
||||||
Sent to all services when application starts.
|
|
||||||
|
|
||||||
### ActionServiceShutdown
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ActionServiceShutdown struct{}
|
|
||||||
```
|
|
||||||
|
|
||||||
Sent to all services when application shuts down.
|
|
||||||
|
|
||||||
## Error Helpers
|
|
||||||
|
|
||||||
```go
|
|
||||||
func E(service, operation string, err error) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Creates a contextual error with service and operation info.
|
|
||||||
|
|
||||||
```go
|
|
||||||
if err != nil {
|
|
||||||
return core.E("myservice", "Connect", err)
|
|
||||||
}
|
|
||||||
// Error: myservice.Connect: connection refused
|
|
||||||
```
|
|
||||||
|
|
||||||
## Complete Example
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/Snider/Core/pkg/core"
|
|
||||||
"github.com/Snider/Core/pkg/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MyService struct {
|
|
||||||
*core.ServiceRuntime[struct{}]
|
|
||||||
data string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMyService(c *core.Core) (any, error) {
|
|
||||||
return &MyService{
|
|
||||||
ServiceRuntime: core.NewServiceRuntime(c, struct{}{}),
|
|
||||||
data: "initialized",
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MyService) OnStartup(ctx context.Context) error {
|
|
||||||
// Startup logic
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MyService) OnShutdown(ctx context.Context) error {
|
|
||||||
// Cleanup logic
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *MyService) HandleIPCEvents(c *core.Core, msg core.Message) error {
|
|
||||||
switch m := msg.(type) {
|
|
||||||
case map[string]any:
|
|
||||||
if m["action"] == "myservice.update" {
|
|
||||||
s.data = m["data"].(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
c, err := core.New(
|
|
||||||
core.WithService(config.Register),
|
|
||||||
core.WithService(NewMyService),
|
|
||||||
core.WithServiceLock(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
svc := core.MustServiceFor[*MyService](c, "main")
|
|
||||||
_ = svc
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
# Crypt Service
|
|
||||||
|
|
||||||
The Crypt service (`pkg/crypt`) provides cryptographic utilities including hashing, checksums, RSA encryption, and PGP operations.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Multiple hash algorithms (SHA512, SHA256, SHA1, MD5)
|
|
||||||
- Checksum functions (Fletcher, Luhn)
|
|
||||||
- RSA key generation and encryption
|
|
||||||
- PGP encryption, signing, and verification
|
|
||||||
- Symmetric PGP encryption
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/crypt"
|
|
||||||
|
|
||||||
// Standalone usage
|
|
||||||
crypto, err := crypt.New()
|
|
||||||
|
|
||||||
// With Core framework
|
|
||||||
c, _ := core.New(
|
|
||||||
core.WithService(crypt.Register),
|
|
||||||
)
|
|
||||||
crypto := core.MustServiceFor[*crypt.Service](c, "crypt")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hashing
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Available algorithms: SHA512, SHA256, SHA1, MD5, LTHN
|
|
||||||
hash := crypto.Hash(crypt.SHA256, "hello world")
|
|
||||||
|
|
||||||
// Check if string is valid hash algorithm
|
|
||||||
isValid := crypto.IsHashAlgo("sha256")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Checksums
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Luhn validation (credit card numbers)
|
|
||||||
isValid := crypto.Luhn("4532015112830366")
|
|
||||||
|
|
||||||
// Fletcher checksums
|
|
||||||
f16 := crypto.Fletcher16("data")
|
|
||||||
f32 := crypto.Fletcher32("data")
|
|
||||||
f64 := crypto.Fletcher64("data")
|
|
||||||
```
|
|
||||||
|
|
||||||
## RSA Encryption
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Generate key pair (2048 or 4096 bits recommended)
|
|
||||||
publicKey, privateKey, err := crypto.GenerateRSAKeyPair(2048)
|
|
||||||
|
|
||||||
// Encrypt with public key
|
|
||||||
ciphertext, err := crypto.EncryptRSA(publicKey, "secret message")
|
|
||||||
|
|
||||||
// Decrypt with private key
|
|
||||||
plaintext, err := crypto.DecryptRSA(privateKey, ciphertext)
|
|
||||||
```
|
|
||||||
|
|
||||||
## PGP Encryption
|
|
||||||
|
|
||||||
### Key Generation
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Generate PGP key pair
|
|
||||||
publicKey, privateKey, err := crypto.GeneratePGPKeyPair(
|
|
||||||
"User Name",
|
|
||||||
"user@example.com",
|
|
||||||
"Key comment",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Asymmetric Encryption
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Encrypt for recipient
|
|
||||||
ciphertext, err := crypto.EncryptPGPToString(recipientPublicKey, "secret data")
|
|
||||||
|
|
||||||
// Decrypt with private key
|
|
||||||
plaintext, err := crypto.DecryptPGP(privateKey, ciphertext)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Symmetric Encryption
|
|
||||||
|
|
||||||
```go
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := crypto.SymmetricallyEncryptPGP(&buf, "data", "passphrase")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Signing & Verification
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Sign data
|
|
||||||
signature, err := crypto.SignPGP(privateKey, "data to sign")
|
|
||||||
|
|
||||||
// Verify signature
|
|
||||||
err := crypto.VerifyPGP(publicKey, "data to sign", signature)
|
|
||||||
if err != nil {
|
|
||||||
// Signature invalid
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hash Types
|
|
||||||
|
|
||||||
| Constant | Algorithm |
|
|
||||||
|----------|-----------|
|
|
||||||
| `crypt.SHA512` | SHA-512 |
|
|
||||||
| `crypt.SHA256` | SHA-256 |
|
|
||||||
| `crypt.SHA1` | SHA-1 |
|
|
||||||
| `crypt.MD5` | MD5 |
|
|
||||||
| `crypt.LTHN` | Custom LTHN hash |
|
|
||||||
|
|
||||||
## Frontend Usage (TypeScript)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
Hash,
|
|
||||||
GenerateRSAKeyPair,
|
|
||||||
EncryptRSA,
|
|
||||||
DecryptRSA
|
|
||||||
} from '@bindings/crypt/service';
|
|
||||||
|
|
||||||
// Hash data
|
|
||||||
const hash = await Hash("SHA256", "hello world");
|
|
||||||
|
|
||||||
// RSA encryption
|
|
||||||
const { publicKey, privateKey } = await GenerateRSAKeyPair(2048);
|
|
||||||
const encrypted = await EncryptRSA(publicKey, "secret");
|
|
||||||
const decrypted = await DecryptRSA(privateKey, encrypted);
|
|
||||||
```
|
|
||||||
|
|
@ -1,352 +0,0 @@
|
||||||
# Display API Reference
|
|
||||||
|
|
||||||
Complete API reference for the Display service (`pkg/display`).
|
|
||||||
|
|
||||||
## Service Creation
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewService(c *core.Core) (any, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Window Management
|
|
||||||
|
|
||||||
### CreateWindow
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) CreateWindow(opts CreateWindowOptions) (*WindowInfo, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Creates a new window with the specified options.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type CreateWindowOptions struct {
|
|
||||||
Name string
|
|
||||||
Title string
|
|
||||||
URL string
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### CloseWindow
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) CloseWindow(name string) error
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetWindowInfo
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetWindowInfo(name string) (*WindowInfo, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type WindowInfo struct {
|
|
||||||
Name string
|
|
||||||
Title string
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
IsVisible bool
|
|
||||||
IsFocused bool
|
|
||||||
IsMaximized bool
|
|
||||||
IsMinimized bool
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ListWindowInfos
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) ListWindowInfos() []*WindowInfo
|
|
||||||
```
|
|
||||||
|
|
||||||
### Window Position & Size
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) SetWindowPosition(name string, x, y int) error
|
|
||||||
func (s *Service) SetWindowSize(name string, width, height int) error
|
|
||||||
func (s *Service) SetWindowBounds(name string, x, y, width, height int) error
|
|
||||||
```
|
|
||||||
|
|
||||||
### Window State
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) MaximizeWindow(name string) error
|
|
||||||
func (s *Service) MinimizeWindow(name string) error
|
|
||||||
func (s *Service) RestoreWindow(name string) error
|
|
||||||
func (s *Service) FocusWindow(name string) error
|
|
||||||
func (s *Service) SetWindowFullscreen(name string, fullscreen bool) error
|
|
||||||
func (s *Service) SetWindowAlwaysOnTop(name string, onTop bool) error
|
|
||||||
func (s *Service) SetWindowVisibility(name string, visible bool) error
|
|
||||||
```
|
|
||||||
|
|
||||||
### Window Title
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) SetWindowTitle(name, title string) error
|
|
||||||
func (s *Service) GetWindowTitle(name string) (string, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Window Background
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) SetWindowBackgroundColour(name string, r, g, b, a uint8) error
|
|
||||||
```
|
|
||||||
|
|
||||||
### Focus
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetFocusedWindow() string
|
|
||||||
```
|
|
||||||
|
|
||||||
## Screen Management
|
|
||||||
|
|
||||||
### GetScreens
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetScreens() []*Screen
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Screen struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
ScaleFactor float64
|
|
||||||
IsPrimary bool
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetScreen
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetScreen(id string) (*Screen, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetPrimaryScreen
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetPrimaryScreen() (*Screen, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetScreenAtPoint
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetScreenAtPoint(x, y int) (*Screen, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetScreenForWindow
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetScreenForWindow(name string) (*Screen, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
### GetWorkAreas
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetWorkAreas() []*WorkArea
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns usable screen space (excluding dock/taskbar).
|
|
||||||
|
|
||||||
## Layout Management
|
|
||||||
|
|
||||||
### SaveLayout / RestoreLayout
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) SaveLayout(name string) error
|
|
||||||
func (s *Service) RestoreLayout(name string) error
|
|
||||||
func (s *Service) ListLayouts() []string
|
|
||||||
func (s *Service) DeleteLayout(name string) error
|
|
||||||
func (s *Service) GetLayout(name string) *Layout
|
|
||||||
```
|
|
||||||
|
|
||||||
### TileWindows
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) TileWindows(mode TileMode, windows []string) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Tile modes:
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
TileModeLeft TileMode = "left"
|
|
||||||
TileModeRight TileMode = "right"
|
|
||||||
TileModeGrid TileMode = "grid"
|
|
||||||
TileModeQuadrants TileMode = "quadrants"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### SnapWindow
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) SnapWindow(name string, position SnapPosition) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Snap positions:
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
SnapPositionLeft SnapPosition = "left"
|
|
||||||
SnapPositionRight SnapPosition = "right"
|
|
||||||
SnapPositionTop SnapPosition = "top"
|
|
||||||
SnapPositionBottom SnapPosition = "bottom"
|
|
||||||
SnapPositionTopLeft SnapPosition = "top-left"
|
|
||||||
SnapPositionTopRight SnapPosition = "top-right"
|
|
||||||
SnapPositionBottomLeft SnapPosition = "bottom-left"
|
|
||||||
SnapPositionBottomRight SnapPosition = "bottom-right"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### StackWindows
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) StackWindows(windows []string, offsetX, offsetY int) error
|
|
||||||
```
|
|
||||||
|
|
||||||
### ApplyWorkflowLayout
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) ApplyWorkflowLayout(workflow WorkflowType) error
|
|
||||||
```
|
|
||||||
|
|
||||||
Workflow types:
|
|
||||||
|
|
||||||
```go
|
|
||||||
const (
|
|
||||||
WorkflowCoding WorkflowType = "coding"
|
|
||||||
WorkflowDebugging WorkflowType = "debugging"
|
|
||||||
WorkflowPresenting WorkflowType = "presenting"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dialogs
|
|
||||||
|
|
||||||
### File Dialogs
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) OpenSingleFileDialog(opts OpenFileOptions) (string, error)
|
|
||||||
func (s *Service) OpenFileDialog(opts OpenFileOptions) ([]string, error)
|
|
||||||
func (s *Service) SaveFileDialog(opts SaveFileOptions) (string, error)
|
|
||||||
func (s *Service) OpenDirectoryDialog(opts OpenDirectoryOptions) (string, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type OpenFileOptions struct {
|
|
||||||
Title string
|
|
||||||
DefaultDirectory string
|
|
||||||
AllowMultiple bool
|
|
||||||
Filters []FileFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
type SaveFileOptions struct {
|
|
||||||
Title string
|
|
||||||
DefaultDirectory string
|
|
||||||
DefaultFilename string
|
|
||||||
Filters []FileFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileFilter struct {
|
|
||||||
DisplayName string
|
|
||||||
Pattern string // e.g., "*.png;*.jpg"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ConfirmDialog
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) ConfirmDialog(title, message string) (bool, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
### PromptDialog
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) PromptDialog(title, message string) (string, bool, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
## System Tray
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) SetTrayIcon(icon []byte) error
|
|
||||||
func (s *Service) SetTrayTooltip(tooltip string) error
|
|
||||||
func (s *Service) SetTrayLabel(label string) error
|
|
||||||
func (s *Service) SetTrayMenu(items []TrayMenuItem) error
|
|
||||||
func (s *Service) GetTrayInfo() map[string]any
|
|
||||||
```
|
|
||||||
|
|
||||||
Menu item:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type TrayMenuItem struct {
|
|
||||||
Label string
|
|
||||||
ActionID string
|
|
||||||
IsSeparator bool
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Clipboard
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) ReadClipboard() (string, error)
|
|
||||||
func (s *Service) WriteClipboard(text string) error
|
|
||||||
func (s *Service) HasClipboard() bool
|
|
||||||
func (s *Service) ClearClipboard() error
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notifications
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) ShowNotification(opts NotificationOptions) error
|
|
||||||
func (s *Service) ShowInfoNotification(title, message string) error
|
|
||||||
func (s *Service) ShowWarningNotification(title, message string) error
|
|
||||||
func (s *Service) ShowErrorNotification(title, message string) error
|
|
||||||
func (s *Service) RequestNotificationPermission() (bool, error)
|
|
||||||
func (s *Service) CheckNotificationPermission() (bool, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type NotificationOptions struct {
|
|
||||||
ID string
|
|
||||||
Title string
|
|
||||||
Message string
|
|
||||||
Subtitle string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Theme
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetTheme() *Theme
|
|
||||||
func (s *Service) GetSystemTheme() string
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Theme struct {
|
|
||||||
IsDark bool
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Events
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) GetEventManager() *EventManager
|
|
||||||
```
|
|
||||||
|
|
||||||
The EventManager handles WebSocket connections for real-time events.
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
---
|
|
||||||
title: e
|
|
||||||
---
|
|
||||||
# Service: `e`
|
|
||||||
|
|
||||||
Package e provides a standardized error handling mechanism for the Core library.
|
|
||||||
It allows for wrapping errors with contextual information, making it easier to
|
|
||||||
trace the origin of an error and provide meaningful feedback.
|
|
||||||
|
|
||||||
The design of this package is influenced by the need for a simple, yet powerful
|
|
||||||
way to handle errors that can occur in different layers of the application,
|
|
||||||
from low-level file operations to high-level service interactions.
|
|
||||||
|
|
||||||
The key features of this package are:
|
|
||||||
- Error wrapping: The Op and an optional Msg field provide context about
|
|
||||||
where and why an error occurred.
|
|
||||||
- Stack traces: By wrapping errors, we can build a logical stack trace
|
|
||||||
that is more informative than a raw stack trace.
|
|
||||||
- Consistent error handling: Encourages a uniform approach to error
|
|
||||||
handling across the entire codebase.
|
|
||||||
|
|
||||||
## Types
|
|
||||||
|
|
||||||
### `type Error`
|
|
||||||
|
|
||||||
`Error` represents a standardized error with operational context.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Error struct {
|
|
||||||
// Op is the operation being performed, e.g., "config.Load".
|
|
||||||
Op string
|
|
||||||
// Msg is a human-readable message explaining the error.
|
|
||||||
Msg string
|
|
||||||
// Err is the underlying error that was wrapped.
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Methods
|
|
||||||
|
|
||||||
- `Error() string`: Error returns the string representation of the error.
|
|
||||||
- `Unwrap() error`: Unwrap provides compatibility for Go's errors.Is and errors.As functions.
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
|
|
||||||
- `E(op, msg string, err error) error`: E is a helper function to create a new Error.
|
|
||||||
|
|
||||||
This is the primary way to create errors that will be consumed by the system. For example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
return e.E("config.Load", "failed to load config file", err)
|
|
||||||
```
|
|
||||||
|
|
||||||
The `op` parameter should be in the format of `package.function` or `service.method`. The `msg` parameter should be a human-readable message that can be displayed to the user. The `err` parameter is the underlying error that is being wrapped.
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
# Help Service
|
|
||||||
|
|
||||||
The Help service (`pkg/help`) provides an embeddable documentation system that displays MkDocs-based help content in a dedicated window.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Embedded help content (MkDocs static site)
|
|
||||||
- Context-sensitive help navigation
|
|
||||||
- Works with or without Display service
|
|
||||||
- Multiple content sources (embedded, filesystem, custom)
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/help"
|
|
||||||
|
|
||||||
// Create with default embedded content
|
|
||||||
helpService, err := help.New(help.Options{})
|
|
||||||
|
|
||||||
// Initialize with core dependencies
|
|
||||||
helpService.Init(coreInstance, displayService)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Showing Help
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Show main help window
|
|
||||||
err := helpService.Show()
|
|
||||||
|
|
||||||
// Show specific section
|
|
||||||
err := helpService.ShowAt("getting-started")
|
|
||||||
err := helpService.ShowAt("api/config")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Options struct {
|
|
||||||
Source string // Path to help content directory
|
|
||||||
Assets fs.FS // Custom filesystem for assets
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Default Embedded Content
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Uses embedded MkDocs site
|
|
||||||
helpService, _ := help.New(help.Options{})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Directory
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Use local directory
|
|
||||||
helpService, _ := help.New(help.Options{
|
|
||||||
Source: "/path/to/docs/site",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Filesystem
|
|
||||||
|
|
||||||
```go
|
|
||||||
//go:embed docs/*
|
|
||||||
var docsFS embed.FS
|
|
||||||
|
|
||||||
helpService, _ := help.New(help.Options{
|
|
||||||
Assets: docsFS,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration with Core
|
|
||||||
|
|
||||||
The help service can work standalone or integrated with Core:
|
|
||||||
|
|
||||||
### With Display Service
|
|
||||||
|
|
||||||
When Display service is available, help opens through the IPC action system:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Automatically uses display.open_window action
|
|
||||||
helpService.Init(core, displayService)
|
|
||||||
helpService.Show()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Without Display Service
|
|
||||||
|
|
||||||
Falls back to direct Wails window creation:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Creates window directly via Wails
|
|
||||||
helpService.Init(core, nil)
|
|
||||||
helpService.Show()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lifecycle
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Called on application startup
|
|
||||||
err := helpService.ServiceStartup(ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building Help Content
|
|
||||||
|
|
||||||
Help content is a static MkDocs site. To update:
|
|
||||||
|
|
||||||
1. Edit documentation in `docs/` directory
|
|
||||||
2. Build with MkDocs:
|
|
||||||
```bash
|
|
||||||
mkdocs build
|
|
||||||
```
|
|
||||||
3. The built site goes to `pkg/help/public/`
|
|
||||||
4. Content is embedded at compile time
|
|
||||||
|
|
||||||
## Frontend Usage (TypeScript)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { Show, ShowAt } from '@bindings/help/service';
|
|
||||||
|
|
||||||
// Open help window
|
|
||||||
await Show();
|
|
||||||
|
|
||||||
// Open specific section
|
|
||||||
await ShowAt("configuration");
|
|
||||||
await ShowAt("api/display");
|
|
||||||
```
|
|
||||||
|
|
||||||
## Help Window Options
|
|
||||||
|
|
||||||
The help window opens with default settings:
|
|
||||||
|
|
||||||
| Property | Value |
|
|
||||||
|----------|-------|
|
|
||||||
| Title | "Help" |
|
|
||||||
| Width | 800px |
|
|
||||||
| Height | 600px |
|
|
||||||
|
|
||||||
## IPC Action
|
|
||||||
|
|
||||||
When using Display service, help triggers this action:
|
|
||||||
|
|
||||||
```go
|
|
||||||
{
|
|
||||||
"action": "display.open_window",
|
|
||||||
"name": "help",
|
|
||||||
"options": {
|
|
||||||
"Title": "Help",
|
|
||||||
"Width": 800,
|
|
||||||
"Height": 600,
|
|
||||||
"URL": "/#anchor", // When using ShowAt
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
# I18n Service
|
|
||||||
|
|
||||||
The I18n service (`pkg/i18n`) provides internationalization and localization support with automatic language detection and template-based translations.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- JSON-based locale files
|
|
||||||
- Embedded locale bundles
|
|
||||||
- Automatic language detection from environment
|
|
||||||
- Template variable interpolation
|
|
||||||
- BCP 47 language tag support
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/i18n"
|
|
||||||
|
|
||||||
// Create service (defaults to English)
|
|
||||||
i18n, err := i18n.New()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setting Language
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Set language using BCP 47 tag
|
|
||||||
err := i18n.SetLanguage("fr")
|
|
||||||
err := i18n.SetLanguage("en-US")
|
|
||||||
err := i18n.SetLanguage("zh-Hans")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Translating Messages
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Simple translation
|
|
||||||
msg := i18n.Translate("welcome_message")
|
|
||||||
|
|
||||||
// With template data
|
|
||||||
msg := i18n.Translate("greeting", map[string]string{
|
|
||||||
"Name": "John",
|
|
||||||
})
|
|
||||||
// Template: "Hello, {{.Name}}!"
|
|
||||||
// Result: "Hello, John!"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Languages
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Get list of available language codes
|
|
||||||
langs := i18n.AvailableLanguages()
|
|
||||||
// Returns: ["en", "es", "fr", "de", ...]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Get All Messages
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Get all translations for a language
|
|
||||||
messages, err := i18n.GetAllMessages("en")
|
|
||||||
for key, value := range messages {
|
|
||||||
fmt.Printf("%s: %s\n", key, value)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Locale File Format
|
|
||||||
|
|
||||||
Locale files are JSON stored in `locales/` directory:
|
|
||||||
|
|
||||||
```json
|
|
||||||
// locales/en.json
|
|
||||||
{
|
|
||||||
"welcome": "Welcome to the application",
|
|
||||||
"greeting": "Hello, {{.Name}}!",
|
|
||||||
"items_count": {
|
|
||||||
"one": "{{.Count}} item",
|
|
||||||
"other": "{{.Count}} items"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Adding New Languages
|
|
||||||
|
|
||||||
1. Create a new JSON file in `pkg/i18n/locales/`:
|
|
||||||
```
|
|
||||||
locales/es.json
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add translations:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"welcome": "Bienvenido a la aplicación",
|
|
||||||
"greeting": "¡Hola, {{.Name}}!"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. The service automatically loads embedded locales at startup.
|
|
||||||
|
|
||||||
## Language Detection
|
|
||||||
|
|
||||||
The service can detect system language from the `LANG` environment variable:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Automatic detection happens internally
|
|
||||||
// LANG=fr_FR.UTF-8 -> French
|
|
||||||
// LANG=de_DE.UTF-8 -> German
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Usage (TypeScript)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
SetLanguage,
|
|
||||||
Translate,
|
|
||||||
AvailableLanguages,
|
|
||||||
GetAllMessages
|
|
||||||
} from '@bindings/i18n/service';
|
|
||||||
|
|
||||||
// Set language
|
|
||||||
await SetLanguage("fr");
|
|
||||||
|
|
||||||
// Translate
|
|
||||||
const welcome = await Translate("welcome_message");
|
|
||||||
|
|
||||||
// Get available languages for a selector
|
|
||||||
const languages = await AvailableLanguages();
|
|
||||||
|
|
||||||
// Load all messages for client-side caching
|
|
||||||
const messages = await GetAllMessages("en");
|
|
||||||
```
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
# Installation
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
### Go 1.22+
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# macOS
|
|
||||||
brew install go
|
|
||||||
|
|
||||||
# Linux
|
|
||||||
sudo apt install golang-go
|
|
||||||
|
|
||||||
# Windows - download from https://go.dev/dl/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Wails v3
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go install github.com/wailsapp/wails/v3/cmd/wails3@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
### Task (Build Automation)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# macOS
|
|
||||||
brew install go-task
|
|
||||||
|
|
||||||
# Linux
|
|
||||||
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
|
|
||||||
|
|
||||||
# Windows
|
|
||||||
choco install go-task
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install Core
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/Snider/Core@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verify Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check Go
|
|
||||||
go version
|
|
||||||
|
|
||||||
# Check Wails
|
|
||||||
wails3 version
|
|
||||||
|
|
||||||
# Check Task
|
|
||||||
task --version
|
|
||||||
```
|
|
||||||
|
|
||||||
## IDE Setup
|
|
||||||
|
|
||||||
### VS Code
|
|
||||||
|
|
||||||
Install the Go extension and configure:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"go.useLanguageServer": true,
|
|
||||||
"gopls": {
|
|
||||||
"ui.semanticTokens": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GoLand / IntelliJ
|
|
||||||
|
|
||||||
Go support is built-in. Enable the Wails plugin for additional features.
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
Continue to [Quick Start](quickstart.md) to create your first application.
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
# IO Service
|
|
||||||
|
|
||||||
The IO package (`pkg/io`) provides a unified interface for file operations across different storage backends (local filesystem, S3, SFTP, etc.).
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Abstract `Medium` interface for storage backends
|
|
||||||
- Local filesystem implementation
|
|
||||||
- Copy between different mediums
|
|
||||||
- Mock implementation for testing
|
|
||||||
|
|
||||||
## Medium Interface
|
|
||||||
|
|
||||||
All storage backends implement the `Medium` interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Medium interface {
|
|
||||||
Read(path string) (string, error)
|
|
||||||
Write(path, content string) error
|
|
||||||
EnsureDir(path string) error
|
|
||||||
IsFile(path string) bool
|
|
||||||
FileGet(path string) (string, error)
|
|
||||||
FileSet(path, content string) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Local Filesystem
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"github.com/Snider/Core/pkg/io"
|
|
||||||
"github.com/Snider/Core/pkg/io/local"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pre-initialized global medium (root = "/")
|
|
||||||
content, err := io.Local.Read("/etc/hosts")
|
|
||||||
|
|
||||||
// Create sandboxed medium
|
|
||||||
medium, err := local.New("/app/data")
|
|
||||||
content, err := medium.Read("config.json") // Reads /app/data/config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic Operations
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Read file
|
|
||||||
content, err := medium.Read("path/to/file.txt")
|
|
||||||
|
|
||||||
// Write file
|
|
||||||
err := medium.Write("path/to/file.txt", "content")
|
|
||||||
|
|
||||||
// Check if file exists
|
|
||||||
if medium.IsFile("config.json") {
|
|
||||||
// File exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure directory exists
|
|
||||||
err := medium.EnsureDir("path/to/dir")
|
|
||||||
|
|
||||||
// Convenience methods
|
|
||||||
content, err := medium.FileGet("file.txt")
|
|
||||||
err := medium.FileSet("file.txt", "content")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Helper Functions
|
|
||||||
|
|
||||||
Package-level functions that work with any Medium:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Read from medium
|
|
||||||
content, err := io.Read(medium, "file.txt")
|
|
||||||
|
|
||||||
// Write to medium
|
|
||||||
err := io.Write(medium, "file.txt", "content")
|
|
||||||
|
|
||||||
// Ensure directory
|
|
||||||
err := io.EnsureDir(medium, "path/to/dir")
|
|
||||||
|
|
||||||
// Check if file
|
|
||||||
exists := io.IsFile(medium, "file.txt")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Copy Between Mediums
|
|
||||||
|
|
||||||
```go
|
|
||||||
localMedium, _ := local.New("/local/path")
|
|
||||||
remoteMedium := s3.New(bucket, region) // hypothetical S3 implementation
|
|
||||||
|
|
||||||
// Copy from local to remote
|
|
||||||
err := io.Copy(localMedium, "data.json", remoteMedium, "backup/data.json")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mock Medium for Testing
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/io"
|
|
||||||
|
|
||||||
func TestMyFunction(t *testing.T) {
|
|
||||||
mock := io.NewMockMedium()
|
|
||||||
|
|
||||||
// Pre-populate files
|
|
||||||
mock.Files["config.json"] = `{"key": "value"}`
|
|
||||||
mock.Dirs["data"] = true
|
|
||||||
|
|
||||||
// Use in tests
|
|
||||||
myService := NewService(mock)
|
|
||||||
|
|
||||||
// Verify writes
|
|
||||||
err := myService.SaveData("test")
|
|
||||||
if mock.Files["data/test.json"] != expectedContent {
|
|
||||||
t.Error("unexpected content")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Creating Custom Backends
|
|
||||||
|
|
||||||
Implement the `Medium` interface for custom storage:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type S3Medium struct {
|
|
||||||
bucket string
|
|
||||||
client *s3.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *S3Medium) Read(path string) (string, error) {
|
|
||||||
// Implement S3 read
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *S3Medium) Write(path, content string) error {
|
|
||||||
// Implement S3 write
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... implement remaining methods
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
```go
|
|
||||||
content, err := medium.Read("missing.txt")
|
|
||||||
if err != nil {
|
|
||||||
// File not found or read error
|
|
||||||
log.Printf("Read failed: %v", err)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Usage
|
|
||||||
|
|
||||||
The IO package is primarily used server-side. Frontend file operations should use the Display service dialogs or direct API calls:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { OpenFileDialog, SaveFileDialog } from '@bindings/display/service';
|
|
||||||
|
|
||||||
// Open file picker
|
|
||||||
const path = await OpenFileDialog({
|
|
||||||
title: "Select File",
|
|
||||||
filters: [{ displayName: "Text", pattern: "*.txt" }]
|
|
||||||
});
|
|
||||||
|
|
||||||
// Save file picker
|
|
||||||
const savePath = await SaveFileDialog({
|
|
||||||
title: "Save As",
|
|
||||||
defaultFilename: "document.txt"
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
# IPC & Actions
|
|
||||||
|
|
||||||
Core provides an inter-process communication system for services to communicate without tight coupling.
|
|
||||||
|
|
||||||
## Message Structure
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Message struct {
|
|
||||||
Type string // Message type identifier
|
|
||||||
Data map[string]any // Message payload
|
|
||||||
Source string // Originating service (optional)
|
|
||||||
Timestamp time.Time // When message was created
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sending Messages
|
|
||||||
|
|
||||||
```go
|
|
||||||
c.ACTION(core.Message{
|
|
||||||
Type: "user.created",
|
|
||||||
Data: map[string]any{
|
|
||||||
"id": "123",
|
|
||||||
"email": "user@example.com",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Handling Messages
|
|
||||||
|
|
||||||
Register action handlers during service initialization:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewNotificationService(c *core.Core) (any, error) {
|
|
||||||
svc := &NotificationService{}
|
|
||||||
|
|
||||||
// Register handler
|
|
||||||
c.RegisterAction(func(c *core.Core, msg core.Message) error {
|
|
||||||
return svc.handleAction(msg)
|
|
||||||
})
|
|
||||||
|
|
||||||
return svc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NotificationService) handleAction(msg core.Message) error {
|
|
||||||
switch msg.Type {
|
|
||||||
case "user.created":
|
|
||||||
email := msg.Data["email"].(string)
|
|
||||||
return s.sendWelcomeEmail(email)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Auto-Discovery
|
|
||||||
|
|
||||||
Services implementing `HandleIPCEvents` are automatically registered:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyService struct{}
|
|
||||||
|
|
||||||
// Automatically registered when using WithService
|
|
||||||
func (s *MyService) HandleIPCEvents(c *core.Core, msg core.Message) error {
|
|
||||||
// Handle messages
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
### Request/Response
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Sender
|
|
||||||
responseChan := make(chan any)
|
|
||||||
c.ACTION(core.Message{
|
|
||||||
Type: "data.request",
|
|
||||||
Data: map[string]any{
|
|
||||||
"query": "SELECT * FROM users",
|
|
||||||
"response": responseChan,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
result := <-responseChan
|
|
||||||
|
|
||||||
// Handler
|
|
||||||
func (s *DataService) handleAction(msg core.Message) error {
|
|
||||||
if msg.Type == "data.request" {
|
|
||||||
query := msg.Data["query"].(string)
|
|
||||||
respChan := msg.Data["response"].(chan any)
|
|
||||||
|
|
||||||
result, err := s.execute(query)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
respChan <- result
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Event Broadcasting
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Broadcast to all listeners
|
|
||||||
c.ACTION(core.Message{
|
|
||||||
Type: "system.config.changed",
|
|
||||||
Data: map[string]any{
|
|
||||||
"key": "theme",
|
|
||||||
"value": "dark",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Use namespaced types** - `service.action` format
|
|
||||||
2. **Keep payloads simple** - Use primitive types when possible
|
|
||||||
3. **Handle errors** - Return errors from handlers
|
|
||||||
4. **Document message types** - Create constants for message types
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
# Service Lifecycle
|
|
||||||
|
|
||||||
Core provides lifecycle hooks for services to initialize and clean up resources.
|
|
||||||
|
|
||||||
## Lifecycle Interfaces
|
|
||||||
|
|
||||||
### Startable
|
|
||||||
|
|
||||||
Called when the application starts:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Startable interface {
|
|
||||||
OnStartup(ctx context.Context) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stoppable
|
|
||||||
|
|
||||||
Called when the application shuts down:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Stoppable interface {
|
|
||||||
OnShutdown(ctx context.Context) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Implementation Example
|
|
||||||
|
|
||||||
```go
|
|
||||||
type DatabaseService struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DatabaseService) OnStartup(ctx context.Context) error {
|
|
||||||
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify connection
|
|
||||||
if err := db.PingContext(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.db = db
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DatabaseService) OnShutdown(ctx context.Context) error {
|
|
||||||
if s.db != nil {
|
|
||||||
return s.db.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lifecycle Order
|
|
||||||
|
|
||||||
1. **Registration**: Services registered via `core.New()`
|
|
||||||
2. **Wails Binding**: Services bound to Wails app
|
|
||||||
3. **Startup**: `OnStartup()` called for each Startable service
|
|
||||||
4. **Running**: Application runs
|
|
||||||
5. **Shutdown**: `OnShutdown()` called for each Stoppable service
|
|
||||||
|
|
||||||
## Context Usage
|
|
||||||
|
|
||||||
The context passed to lifecycle methods includes:
|
|
||||||
|
|
||||||
- Cancellation signal for graceful shutdown
|
|
||||||
- Deadline for timeout handling
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) OnStartup(ctx context.Context) error {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-s.initialize():
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
If `OnStartup` returns an error, the application will fail to start:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (s *Service) OnStartup(ctx context.Context) error {
|
|
||||||
if err := s.validate(); err != nil {
|
|
||||||
return fmt.Errorf("validation failed: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Keep startup fast** - Defer heavy initialization
|
|
||||||
2. **Handle context cancellation** - Support graceful shutdown
|
|
||||||
3. **Clean up resources** - Always implement OnShutdown for services with resources
|
|
||||||
4. **Log lifecycle events** - Helps with debugging
|
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
# MCP Bridge
|
|
||||||
|
|
||||||
The MCP Bridge (`cmd/core-gui/mcp_bridge.go`) connects the Model Context Protocol server with Display, WebView, and WebSocket services.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The MCP Bridge provides an HTTP API for AI assistants to interact with the desktop application, enabling:
|
|
||||||
|
|
||||||
- Window and screen management
|
|
||||||
- JavaScript execution in webviews
|
|
||||||
- DOM interaction (click, type, select)
|
|
||||||
- Screenshot capture
|
|
||||||
- File and process management
|
|
||||||
- Real-time events via WebSocket
|
|
||||||
|
|
||||||
## HTTP Endpoints
|
|
||||||
|
|
||||||
| Endpoint | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `GET /health` | Health check |
|
|
||||||
| `GET /mcp` | Server capabilities |
|
|
||||||
| `GET /mcp/tools` | List available tools |
|
|
||||||
| `POST /mcp/call` | Execute a tool |
|
|
||||||
| `WS /ws` | WebSocket for GUI clients |
|
|
||||||
| `WS /events` | WebSocket for display events |
|
|
||||||
|
|
||||||
## Server Capabilities
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:9877/mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "core",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"capabilities": {
|
|
||||||
"webview": true,
|
|
||||||
"display": true,
|
|
||||||
"windowControl": true,
|
|
||||||
"screenControl": true,
|
|
||||||
"websocket": "ws://localhost:9877/ws",
|
|
||||||
"events": "ws://localhost:9877/events"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tool Categories
|
|
||||||
|
|
||||||
### File Operations
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `file_read` | Read file contents |
|
|
||||||
| `file_write` | Write content to file |
|
|
||||||
| `file_edit` | Edit file by replacing text |
|
|
||||||
| `file_delete` | Delete a file |
|
|
||||||
| `file_exists` | Check if file exists |
|
|
||||||
| `dir_list` | List directory contents |
|
|
||||||
| `dir_create` | Create directory |
|
|
||||||
|
|
||||||
### Window Control
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `window_list` | List all windows |
|
|
||||||
| `window_create` | Create new window |
|
|
||||||
| `window_close` | Close window |
|
|
||||||
| `window_position` | Move window |
|
|
||||||
| `window_size` | Resize window |
|
|
||||||
| `window_maximize` | Maximize window |
|
|
||||||
| `window_minimize` | Minimize window |
|
|
||||||
| `window_focus` | Bring window to front |
|
|
||||||
|
|
||||||
### WebView Interaction
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `webview_eval` | Execute JavaScript |
|
|
||||||
| `webview_click` | Click element |
|
|
||||||
| `webview_type` | Type into element |
|
|
||||||
| `webview_screenshot` | Capture page |
|
|
||||||
| `webview_navigate` | Navigate to URL |
|
|
||||||
| `webview_console` | Get console messages |
|
|
||||||
|
|
||||||
### Screen Management
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `screen_list` | List all monitors |
|
|
||||||
| `screen_primary` | Get primary screen |
|
|
||||||
| `screen_at_point` | Get screen at coordinates |
|
|
||||||
| `screen_work_areas` | Get usable screen space |
|
|
||||||
|
|
||||||
### Layout Management
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `layout_save` | Save window arrangement |
|
|
||||||
| `layout_restore` | Restore saved layout |
|
|
||||||
| `layout_tile` | Auto-tile windows |
|
|
||||||
| `layout_snap` | Snap window to edge |
|
|
||||||
|
|
||||||
## Calling Tools
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# List windows
|
|
||||||
curl -X POST http://localhost:9877/mcp/call \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"tool": "window_list", "params": {}}'
|
|
||||||
|
|
||||||
# Move window
|
|
||||||
curl -X POST http://localhost:9877/mcp/call \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"tool": "window_position",
|
|
||||||
"params": {"name": "main", "x": 100, "y": 100}
|
|
||||||
}'
|
|
||||||
|
|
||||||
# Execute JavaScript
|
|
||||||
curl -X POST http://localhost:9877/mcp/call \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"tool": "webview_eval",
|
|
||||||
"params": {
|
|
||||||
"window": "main",
|
|
||||||
"code": "document.title"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
|
|
||||||
# Click element
|
|
||||||
curl -X POST http://localhost:9877/mcp/call \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"tool": "webview_click",
|
|
||||||
"params": {
|
|
||||||
"window": "main",
|
|
||||||
"selector": "#submit-button"
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
|
|
||||||
# Take screenshot
|
|
||||||
curl -X POST http://localhost:9877/mcp/call \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{
|
|
||||||
"tool": "webview_screenshot",
|
|
||||||
"params": {"window": "main"}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebSocket Events
|
|
||||||
|
|
||||||
Connect to `/events` for real-time display events:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ws = new WebSocket('ws://localhost:9877/events');
|
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
switch (data.type) {
|
|
||||||
case 'window.focus':
|
|
||||||
console.log('Window focused:', data.name);
|
|
||||||
break;
|
|
||||||
case 'window.move':
|
|
||||||
console.log('Window moved:', data.name, data.x, data.y);
|
|
||||||
break;
|
|
||||||
case 'theme.change':
|
|
||||||
console.log('Theme changed:', data.isDark);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Event types:
|
|
||||||
|
|
||||||
- `window.focus` - Window received focus
|
|
||||||
- `window.blur` - Window lost focus
|
|
||||||
- `window.move` - Window position changed
|
|
||||||
- `window.resize` - Window size changed
|
|
||||||
- `window.close` - Window was closed
|
|
||||||
- `window.create` - New window created
|
|
||||||
- `theme.change` - System theme changed
|
|
||||||
- `screen.change` - Screen configuration changed
|
|
||||||
|
|
||||||
## Go Integration
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/cmd/core-gui"
|
|
||||||
|
|
||||||
// Create bridge
|
|
||||||
bridge := NewMCPBridge(9877, displayService)
|
|
||||||
|
|
||||||
// Access services
|
|
||||||
mcpSvc := bridge.GetMCPService()
|
|
||||||
webview := bridge.GetWebView()
|
|
||||||
display := bridge.GetDisplay()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The bridge starts automatically on Wails app startup via the `ServiceStartup` lifecycle hook:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func (b *MCPBridge) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
|
||||||
b.app = application.Get()
|
|
||||||
b.webview.SetApp(b.app)
|
|
||||||
go b.startHTTPServer()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
The MCP server binds to localhost only by default. For production:
|
|
||||||
|
|
||||||
- Consider firewall rules
|
|
||||||
- Add authentication if needed
|
|
||||||
- Limit exposed tools
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
# MCP Service
|
|
||||||
|
|
||||||
The MCP service (`pkg/mcp`) implements the [Model Context Protocol](https://modelcontextprotocol.io/), enabling AI assistants like Claude to interact with your application.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
MCP provides a standardized way for AI tools to:
|
|
||||||
|
|
||||||
- Execute operations in your application
|
|
||||||
- Query application state
|
|
||||||
- Interact with the UI
|
|
||||||
- Manage files and processes
|
|
||||||
|
|
||||||
## Basic Setup
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/mcp"
|
|
||||||
|
|
||||||
// Create standalone MCP server
|
|
||||||
mcpService := mcp.NewStandaloneWithPort(9877)
|
|
||||||
|
|
||||||
// Or integrate with Core
|
|
||||||
c, _ := core.New(
|
|
||||||
core.WithService(mcp.NewService),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Tools
|
|
||||||
|
|
||||||
The MCP service exposes numerous tools organized by category:
|
|
||||||
|
|
||||||
### File Operations
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `file_read` | Read file contents |
|
|
||||||
| `file_write` | Write content to file |
|
|
||||||
| `file_edit` | Replace text in file |
|
|
||||||
| `file_delete` | Delete a file |
|
|
||||||
| `file_exists` | Check if file exists |
|
|
||||||
| `dir_list` | List directory contents |
|
|
||||||
| `dir_create` | Create directory |
|
|
||||||
|
|
||||||
### Window Control
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `window_list` | List all windows |
|
|
||||||
| `window_create` | Create new window |
|
|
||||||
| `window_close` | Close window |
|
|
||||||
| `window_position` | Move window |
|
|
||||||
| `window_size` | Resize window |
|
|
||||||
| `window_maximize` | Maximize window |
|
|
||||||
| `window_minimize` | Minimize window |
|
|
||||||
| `window_focus` | Bring to front |
|
|
||||||
|
|
||||||
### WebView Interaction
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `webview_eval` | Execute JavaScript |
|
|
||||||
| `webview_click` | Click element |
|
|
||||||
| `webview_type` | Type into element |
|
|
||||||
| `webview_screenshot` | Capture page |
|
|
||||||
| `webview_navigate` | Navigate to URL |
|
|
||||||
| `webview_console` | Get console logs |
|
|
||||||
|
|
||||||
### Process Management
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `process_start` | Start a process |
|
|
||||||
| `process_stop` | Stop a process |
|
|
||||||
| `process_list` | List running processes |
|
|
||||||
| `process_output` | Get process output |
|
|
||||||
|
|
||||||
## HTTP API
|
|
||||||
|
|
||||||
The MCP service exposes an HTTP API:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Health check
|
|
||||||
curl http://localhost:9877/health
|
|
||||||
|
|
||||||
# List available tools
|
|
||||||
curl http://localhost:9877/mcp/tools
|
|
||||||
|
|
||||||
# Call a tool
|
|
||||||
curl -X POST http://localhost:9877/mcp/call \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"tool": "window_list", "params": {}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebSocket Events
|
|
||||||
|
|
||||||
Connect to `/events` for real-time updates:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ws = new WebSocket('ws://localhost:9877/events');
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
console.log('Event:', data.type, data.data);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration with Display Service
|
|
||||||
|
|
||||||
```go
|
|
||||||
mcpService := mcp.NewStandaloneWithPort(9877)
|
|
||||||
mcpService.SetDisplay(displayService)
|
|
||||||
mcpService.SetWebView(webviewService)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example: Claude Integration
|
|
||||||
|
|
||||||
When Claude connects via MCP, it can:
|
|
||||||
|
|
||||||
```
|
|
||||||
User: "Move the settings window to the left side of the screen"
|
|
||||||
|
|
||||||
Claude uses: window_position("settings", 0, 100)
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
User: "Take a screenshot of the app"
|
|
||||||
|
|
||||||
Claude uses: webview_screenshot("main")
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
User: "Click the submit button"
|
|
||||||
|
|
||||||
Claude uses: webview_click("main", "#submit-btn")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
- MCP server binds to localhost by default
|
|
||||||
- No authentication (designed for local AI assistants)
|
|
||||||
- Consider firewall rules for production
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Custom port
|
|
||||||
mcp.NewStandaloneWithPort(8080)
|
|
||||||
|
|
||||||
// With all services
|
|
||||||
bridge := NewMCPBridge(9877, displayService)
|
|
||||||
bridge.SetWebView(webviewService)
|
|
||||||
```
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
# Module System
|
|
||||||
|
|
||||||
The Module system (`pkg/module`) provides a declarative way to register UI menus, routes, and API endpoints using the `.itw3.json` configuration format.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Declarative module configuration
|
|
||||||
- UI menu contributions
|
|
||||||
- Frontend route registration
|
|
||||||
- API endpoint declarations
|
|
||||||
- Multi-context support (developer, retail, miner)
|
|
||||||
- Binary/daemon management
|
|
||||||
- Module dependencies
|
|
||||||
|
|
||||||
## Module Config Format
|
|
||||||
|
|
||||||
Modules are defined using `.itw3.json` files:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": "wallet",
|
|
||||||
"type": "core",
|
|
||||||
"name": "Wallet Manager",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"namespace": "finance",
|
|
||||||
"description": "Cryptocurrency wallet management",
|
|
||||||
"author": "Your Name",
|
|
||||||
"contexts": ["default", "retail"],
|
|
||||||
"menu": [...],
|
|
||||||
"routes": [...],
|
|
||||||
"api": [...],
|
|
||||||
"config": {...}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Module Types
|
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `core` | Built-in core functionality |
|
|
||||||
| `app` | External web application |
|
|
||||||
| `bin` | Binary/daemon wrapper |
|
|
||||||
|
|
||||||
## UI Contexts
|
|
||||||
|
|
||||||
Modules can target specific UI contexts:
|
|
||||||
|
|
||||||
| Context | Description |
|
|
||||||
|---------|-------------|
|
|
||||||
| `default` | Standard user interface |
|
|
||||||
| `developer` | Developer tools and debugging |
|
|
||||||
| `retail` | Point-of-sale interface |
|
|
||||||
| `miner` | Mining operations interface |
|
|
||||||
|
|
||||||
## Menu Contributions
|
|
||||||
|
|
||||||
Add items to the application menu:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"menu": [
|
|
||||||
{
|
|
||||||
"id": "wallet-send",
|
|
||||||
"label": "Send Funds",
|
|
||||||
"icon": "send",
|
|
||||||
"route": "/wallet/send",
|
|
||||||
"accelerator": "CmdOrCtrl+Shift+S",
|
|
||||||
"contexts": ["default", "retail"],
|
|
||||||
"order": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "wallet-receive",
|
|
||||||
"label": "Receive",
|
|
||||||
"icon": "receive",
|
|
||||||
"route": "/wallet/receive",
|
|
||||||
"order": 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"separator": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "wallet-settings",
|
|
||||||
"label": "Settings",
|
|
||||||
"action": "wallet.open_settings",
|
|
||||||
"children": [
|
|
||||||
{"id": "wallet-backup", "label": "Backup", "action": "wallet.backup"},
|
|
||||||
{"id": "wallet-restore", "label": "Restore", "action": "wallet.restore"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Route Contributions
|
|
||||||
|
|
||||||
Register frontend routes:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"path": "/wallet",
|
|
||||||
"component": "wallet-dashboard",
|
|
||||||
"title": "Wallet",
|
|
||||||
"icon": "wallet",
|
|
||||||
"contexts": ["default"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/wallet/send",
|
|
||||||
"component": "wallet-send-form",
|
|
||||||
"title": "Send Funds"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Declarations
|
|
||||||
|
|
||||||
Declare API endpoints the module provides:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"api": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"path": "/balance",
|
|
||||||
"description": "Get wallet balance"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"path": "/send",
|
|
||||||
"description": "Send transaction"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Binary Downloads
|
|
||||||
|
|
||||||
For `bin` type modules, specify platform binaries:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"downloads": {
|
|
||||||
"app": "https://example.com/wallet-ui.tar.gz",
|
|
||||||
"x86_64": {
|
|
||||||
"darwin": {
|
|
||||||
"url": "https://example.com/wallet-darwin-x64",
|
|
||||||
"checksum": "sha256:abc123..."
|
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"url": "https://example.com/wallet-linux-x64",
|
|
||||||
"checksum": "sha256:def456..."
|
|
||||||
},
|
|
||||||
"windows": {
|
|
||||||
"url": "https://example.com/wallet-win-x64.exe",
|
|
||||||
"checksum": "sha256:ghi789..."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aarch64": {
|
|
||||||
"darwin": {
|
|
||||||
"url": "https://example.com/wallet-darwin-arm64"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Web App Configuration
|
|
||||||
|
|
||||||
For `app` type modules:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"app": {
|
|
||||||
"url": "https://example.com/wallet-app.tar.gz",
|
|
||||||
"type": "spa",
|
|
||||||
"hooks": [
|
|
||||||
{
|
|
||||||
"type": "rename",
|
|
||||||
"from": "dist",
|
|
||||||
"to": "wallet"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
Declare module dependencies:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"depends": ["core", "crypto"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using in Go
|
|
||||||
|
|
||||||
### Module Registration
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/module"
|
|
||||||
|
|
||||||
// Create from config
|
|
||||||
cfg := module.Config{
|
|
||||||
Code: "wallet",
|
|
||||||
Type: module.TypeCore,
|
|
||||||
Name: "Wallet",
|
|
||||||
Namespace: "finance",
|
|
||||||
}
|
|
||||||
|
|
||||||
mod := module.Module{
|
|
||||||
Config: cfg,
|
|
||||||
Handler: myHandler,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Gin Router Integration
|
|
||||||
|
|
||||||
```go
|
|
||||||
type WalletModule struct{}
|
|
||||||
|
|
||||||
func (m *WalletModule) RegisterRoutes(group *gin.RouterGroup) {
|
|
||||||
group.GET("/balance", m.getBalance)
|
|
||||||
group.POST("/send", m.sendTransaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register with Gin
|
|
||||||
router := gin.Default()
|
|
||||||
apiGroup := router.Group("/api/finance/wallet")
|
|
||||||
walletModule.RegisterRoutes(apiGroup)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Registry Service
|
|
||||||
|
|
||||||
The registry manages all modules:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/module"
|
|
||||||
|
|
||||||
registry := module.NewRegistry()
|
|
||||||
|
|
||||||
// Register module
|
|
||||||
registry.Register(walletModule)
|
|
||||||
|
|
||||||
// Get module by code
|
|
||||||
mod := registry.Get("wallet")
|
|
||||||
|
|
||||||
// List all modules
|
|
||||||
modules := registry.List()
|
|
||||||
|
|
||||||
// Get modules for context
|
|
||||||
devModules := registry.ForContext(module.ContextDeveloper)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Built-in Modules
|
|
||||||
|
|
||||||
Core provides several built-in modules:
|
|
||||||
|
|
||||||
- System information
|
|
||||||
- Configuration management
|
|
||||||
- Process management
|
|
||||||
- File operations
|
|
||||||
|
|
||||||
Access via:
|
|
||||||
|
|
||||||
```go
|
|
||||||
builtins := module.BuiltinModules()
|
|
||||||
```
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
# GUI Application
|
|
||||||
|
|
||||||
The Core GUI (`cmd/core-gui`) is a Wails v3 desktop application that demonstrates the Core framework capabilities with integrated MCP support.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Angular frontend with Wails bindings
|
|
||||||
- MCP HTTP server for AI tool integration
|
|
||||||
- WebView automation capabilities
|
|
||||||
- Real-time WebSocket communication
|
|
||||||
- System tray support
|
|
||||||
- Multi-window management
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ Angular Frontend │
|
|
||||||
│ (TypeScript + Wails Bindings) │
|
|
||||||
└─────────────────┬───────────────────────┘
|
|
||||||
│ IPC
|
|
||||||
┌─────────────────┴───────────────────────┐
|
|
||||||
│ Wails Runtime │
|
|
||||||
│ (Window, Events, Bindings) │
|
|
||||||
└─────────────────┬───────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────┴───────────────────────┐
|
|
||||||
│ MCP Bridge │
|
|
||||||
│ ┌─────────┬──────────┬─────────────┐ │
|
|
||||||
│ │ Display │ WebView │ WebSocket │ │
|
|
||||||
│ │ Service │ Service │ Hub │ │
|
|
||||||
│ └─────────┴──────────┴─────────────┘ │
|
|
||||||
└─────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running the GUI
|
|
||||||
|
|
||||||
### Development Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From project root
|
|
||||||
task gui:dev
|
|
||||||
|
|
||||||
# Or directly
|
|
||||||
cd cmd/core-gui
|
|
||||||
wails3 dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Production Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
task gui:build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
cmd/core-gui/
|
|
||||||
├── main.go # Application entry point
|
|
||||||
├── mcp_bridge.go # MCP HTTP server and tool handler
|
|
||||||
├── claude_bridge.go # Claude MCP client (optional)
|
|
||||||
├── frontend/ # Angular application
|
|
||||||
│ ├── src/
|
|
||||||
│ │ ├── app/ # Angular components
|
|
||||||
│ │ └── lib/ # Shared utilities
|
|
||||||
│ └── bindings/ # Generated Wails bindings
|
|
||||||
└── public/ # Static assets
|
|
||||||
```
|
|
||||||
|
|
||||||
## Services Integrated
|
|
||||||
|
|
||||||
The GUI integrates several Core services:
|
|
||||||
|
|
||||||
| Service | Purpose |
|
|
||||||
|---------|---------|
|
|
||||||
| Display | Window management, dialogs, tray |
|
|
||||||
| WebView | JavaScript execution, DOM interaction |
|
|
||||||
| MCP | AI tool protocol server |
|
|
||||||
| WebSocket | Real-time communication |
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The application uses the Config service for settings:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Default settings
|
|
||||||
DefaultRoute: "/"
|
|
||||||
Language: "en"
|
|
||||||
Features: []
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Bindings
|
|
||||||
|
|
||||||
Wails generates TypeScript bindings for Go services:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { CreateWindow, ShowNotification } from '@bindings/display/service';
|
|
||||||
import { Translate, SetLanguage } from '@bindings/i18n/service';
|
|
||||||
|
|
||||||
// Create a new window
|
|
||||||
await CreateWindow({
|
|
||||||
name: "settings",
|
|
||||||
title: "Settings",
|
|
||||||
width: 800,
|
|
||||||
height: 600
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show notification
|
|
||||||
await ShowNotification({
|
|
||||||
title: "Success",
|
|
||||||
message: "Operation completed!"
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebSocket Communication
|
|
||||||
|
|
||||||
Connect to the WebSocket endpoint for real-time updates:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const ws = new WebSocket('ws://localhost:9877/ws');
|
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
console.log('Received:', data);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'ping',
|
|
||||||
data: {}
|
|
||||||
}));
|
|
||||||
```
|
|
||||||
|
|
||||||
## System Tray
|
|
||||||
|
|
||||||
The application includes system tray support:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Set tray menu
|
|
||||||
display.SetTrayMenu([]display.TrayMenuItem{
|
|
||||||
{Label: "Open", ActionID: "open"},
|
|
||||||
{Label: "Settings", ActionID: "settings"},
|
|
||||||
{IsSeparator: true},
|
|
||||||
{Label: "Quit", ActionID: "quit"},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building for Distribution
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
task gui:build
|
|
||||||
# Creates: build/bin/core-gui.app
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
```bash
|
|
||||||
task gui:build
|
|
||||||
# Creates: build/bin/core-gui.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linux
|
|
||||||
|
|
||||||
```bash
|
|
||||||
task gui:build
|
|
||||||
# Creates: build/bin/core-gui
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
| Variable | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `MCP_PORT` | MCP server port (default: 9877) |
|
|
||||||
| `DEBUG` | Enable debug logging |
|
|
||||||
|
|
@ -1,172 +0,0 @@
|
||||||
# Plugin System
|
|
||||||
|
|
||||||
The Plugin system (`pkg/plugin`) allows you to extend Core applications with HTTP-based plugins that register routes under `/api/{namespace}/{name}/`.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Namespace-based organization
|
|
||||||
- HTTP handler registration
|
|
||||||
- Lifecycle hooks (OnRegister, OnUnregister)
|
|
||||||
- Wails service integration
|
|
||||||
|
|
||||||
## Plugin Interface
|
|
||||||
|
|
||||||
All plugins implement the `Plugin` interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Plugin interface {
|
|
||||||
// Name returns the unique identifier for this plugin
|
|
||||||
Name() string
|
|
||||||
|
|
||||||
// Namespace returns the plugin's namespace (e.g., "core", "mining")
|
|
||||||
Namespace() string
|
|
||||||
|
|
||||||
// ServeHTTP handles HTTP requests routed to this plugin
|
|
||||||
http.Handler
|
|
||||||
|
|
||||||
// OnRegister is called when the plugin is registered
|
|
||||||
OnRegister(ctx context.Context) error
|
|
||||||
|
|
||||||
// OnUnregister is called when the plugin is being removed
|
|
||||||
OnUnregister(ctx context.Context) error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using BasePlugin
|
|
||||||
|
|
||||||
For simple plugins, embed `BasePlugin`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/plugin"
|
|
||||||
|
|
||||||
func NewMyPlugin() *plugin.BasePlugin {
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Write([]byte("Hello from plugin!"))
|
|
||||||
})
|
|
||||||
|
|
||||||
return plugin.NewBasePlugin("myapp", "greeting", handler).
|
|
||||||
WithDescription("A simple greeting plugin").
|
|
||||||
WithVersion("1.0.0")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom Plugin Implementation
|
|
||||||
|
|
||||||
For more control, implement the full interface:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type DataPlugin struct {
|
|
||||||
db *sql.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *DataPlugin) Name() string { return "data" }
|
|
||||||
func (p *DataPlugin) Namespace() string { return "myapp" }
|
|
||||||
|
|
||||||
func (p *DataPlugin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
switch r.URL.Path {
|
|
||||||
case "/users":
|
|
||||||
p.handleUsers(w, r)
|
|
||||||
case "/items":
|
|
||||||
p.handleItems(w, r)
|
|
||||||
default:
|
|
||||||
http.NotFound(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *DataPlugin) OnRegister(ctx context.Context) error {
|
|
||||||
// Initialize database connection
|
|
||||||
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.db = db
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *DataPlugin) OnUnregister(ctx context.Context) error {
|
|
||||||
if p.db != nil {
|
|
||||||
return p.db.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin Info
|
|
||||||
|
|
||||||
Access plugin metadata:
|
|
||||||
|
|
||||||
```go
|
|
||||||
info := myPlugin.Info()
|
|
||||||
fmt.Println(info.Name) // "greeting"
|
|
||||||
fmt.Println(info.Namespace) // "myapp"
|
|
||||||
fmt.Println(info.Description) // "A simple greeting plugin"
|
|
||||||
fmt.Println(info.Version) // "1.0.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Wails Integration
|
|
||||||
|
|
||||||
Register plugins as Wails services:
|
|
||||||
|
|
||||||
```go
|
|
||||||
app := application.New(application.Options{
|
|
||||||
Services: []application.Service{
|
|
||||||
application.NewServiceWithOptions(
|
|
||||||
myPlugin,
|
|
||||||
plugin.ServiceOptionsForPlugin(myPlugin),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## URL Routing
|
|
||||||
|
|
||||||
Plugins receive requests at:
|
|
||||||
|
|
||||||
```
|
|
||||||
/api/{namespace}/{name}/{path}
|
|
||||||
```
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
- `/api/myapp/greeting/` → GreetingPlugin
|
|
||||||
- `/api/myapp/data/users` → DataPlugin (path: "/users")
|
|
||||||
- `/api/core/system/health` → SystemPlugin (path: "/health")
|
|
||||||
|
|
||||||
## Built-in Plugins
|
|
||||||
|
|
||||||
### System Plugin
|
|
||||||
|
|
||||||
Located at `pkg/plugin/builtin/system`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Provides system information endpoints
|
|
||||||
/api/core/system/info - Application info
|
|
||||||
/api/core/system/health - Health check
|
|
||||||
```
|
|
||||||
|
|
||||||
## Plugin Router
|
|
||||||
|
|
||||||
The Router manages plugin registration:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/plugin"
|
|
||||||
|
|
||||||
router := plugin.NewRouter()
|
|
||||||
|
|
||||||
// Register plugins
|
|
||||||
router.Register(ctx, myPlugin)
|
|
||||||
router.Register(ctx, dataPlugin)
|
|
||||||
|
|
||||||
// Get all registered plugins
|
|
||||||
plugins := router.List()
|
|
||||||
|
|
||||||
// Unregister a plugin
|
|
||||||
router.Unregister(ctx, "myapp", "greeting")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Use namespaces** to group related plugins
|
|
||||||
2. **Implement OnRegister** for initialization that can fail
|
|
||||||
3. **Implement OnUnregister** to clean up resources
|
|
||||||
4. **Return meaningful errors** from lifecycle hooks
|
|
||||||
5. **Use standard HTTP patterns** in ServeHTTP
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
# Quick Start
|
|
||||||
|
|
||||||
Build a simple Core application in 5 minutes.
|
|
||||||
|
|
||||||
## Create Project
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir myapp && cd myapp
|
|
||||||
go mod init myapp
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install Dependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/Snider/Core@latest
|
|
||||||
go get github.com/wailsapp/wails/v3@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Create Main File
|
|
||||||
|
|
||||||
Create `main.go`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"embed"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/Snider/Core/pkg/core"
|
|
||||||
"github.com/Snider/Core/pkg/display"
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed all:frontend/dist
|
|
||||||
var assets embed.FS
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Initialize Core with display service
|
|
||||||
c, err := core.New(
|
|
||||||
core.WithAssets(assets),
|
|
||||||
core.WithService(display.NewService),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get display service for window creation
|
|
||||||
displaySvc := core.MustServiceFor[*display.Service](c, "display")
|
|
||||||
|
|
||||||
// Create Wails application
|
|
||||||
app := application.New(application.Options{
|
|
||||||
Name: "My App",
|
|
||||||
Assets: application.AssetOptions{
|
|
||||||
FS: assets,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create main window
|
|
||||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
|
||||||
Title: "My App",
|
|
||||||
Width: 1200,
|
|
||||||
Height: 800,
|
|
||||||
URL: "/",
|
|
||||||
})
|
|
||||||
|
|
||||||
// Register display service with Wails
|
|
||||||
app.RegisterService(displaySvc)
|
|
||||||
|
|
||||||
// Run application
|
|
||||||
if err := app.Run(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Create Frontend
|
|
||||||
|
|
||||||
Create a minimal frontend:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p frontend/dist
|
|
||||||
```
|
|
||||||
|
|
||||||
Create `frontend/dist/index.html`:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>My App</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: system-ui, sans-serif;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
background: #1a1a2e;
|
|
||||||
color: #eee;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Hello from Core!</h1>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Run Development Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wails3 dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build for Production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wails3 build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
- [Architecture](architecture.md) - Understand how Core works
|
|
||||||
- [Display Service](../services/display.md) - Window and dialog management
|
|
||||||
- [MCP Integration](../services/mcp.md) - AI tool support
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
---
|
|
||||||
title: runtime
|
|
||||||
---
|
|
||||||
# Service: `runtime`
|
|
||||||
|
|
||||||
The `runtime` service provides the main entry point for the application and a helper structure for services to interact with the `Core`.
|
|
||||||
|
|
||||||
## Types
|
|
||||||
|
|
||||||
### `type Runtime`
|
|
||||||
|
|
||||||
`Runtime` is the top-level container that holds the `Core` instance and the Wails application. It serves as the bridge between Wails and the Core framework.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Runtime struct {
|
|
||||||
// Core is the central service manager
|
|
||||||
Core *Core
|
|
||||||
// app is the Wails application instance
|
|
||||||
app *application.App
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `type ServiceRuntime[T any]`
|
|
||||||
|
|
||||||
`ServiceRuntime` is a generic helper struct designed to be embedded in service implementations. It provides easy access to the `Core` and service-specific options.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ServiceRuntime[T any] struct {
|
|
||||||
core *Core
|
|
||||||
opts T
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `type ServiceFactory`
|
|
||||||
|
|
||||||
`ServiceFactory` is a function type that creates a service instance.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type ServiceFactory func() (any, error)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
|
|
||||||
### `func NewRuntime(app *application.App) (*Runtime, error)`
|
|
||||||
|
|
||||||
`NewRuntime` creates and wires together all application services using default settings. It is the standard way to initialize the runtime.
|
|
||||||
|
|
||||||
### `func NewWithFactories(app *application.App, factories map[string]ServiceFactory) (*Runtime, error)`
|
|
||||||
|
|
||||||
`NewWithFactories` creates a new `Runtime` instance using a provided map of service factories. This allows for flexible, dynamic service registration.
|
|
||||||
|
|
||||||
### `func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T]`
|
|
||||||
|
|
||||||
`NewServiceRuntime` creates a new `ServiceRuntime` instance. This is typically used in a service's factory or constructor.
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### `func (r *Runtime) ServiceName() string`
|
|
||||||
|
|
||||||
`ServiceName` returns the name of the service ("Core"). This is used by Wails for service identification.
|
|
||||||
|
|
||||||
### `func (r *Runtime) ServiceStartup(ctx context.Context, options application.ServiceOptions)`
|
|
||||||
|
|
||||||
`ServiceStartup` delegates the startup lifecycle event to the underlying `Core`, which in turn initializes all registered services.
|
|
||||||
|
|
||||||
### `func (r *Runtime) ServiceShutdown(ctx context.Context)`
|
|
||||||
|
|
||||||
`ServiceShutdown` delegates the shutdown lifecycle event to the underlying `Core`.
|
|
||||||
|
|
||||||
### `func (r *ServiceRuntime[T]) Core() *Core`
|
|
||||||
|
|
||||||
`Core` returns the central `Core` instance, giving the service access to other services and features.
|
|
||||||
|
|
||||||
### `func (r *ServiceRuntime[T]) Config() Config`
|
|
||||||
|
|
||||||
`Config` returns the registered `Config` service from the `Core`. It is a convenience method for accessing configuration.
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
# Services
|
|
||||||
|
|
||||||
Services are the building blocks of a Core application. Each service encapsulates a specific domain of functionality.
|
|
||||||
|
|
||||||
## Creating a Service
|
|
||||||
|
|
||||||
```go
|
|
||||||
package myservice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/Snider/Core/pkg/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
core *core.Core
|
|
||||||
}
|
|
||||||
|
|
||||||
// Factory function for registration
|
|
||||||
func NewService(c *core.Core) (any, error) {
|
|
||||||
return &Service{core: c}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement Startable for startup logic
|
|
||||||
func (s *Service) OnStartup(ctx context.Context) error {
|
|
||||||
// Initialize resources
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement Stoppable for cleanup
|
|
||||||
func (s *Service) OnShutdown(ctx context.Context) error {
|
|
||||||
// Release resources
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Registering Services
|
|
||||||
|
|
||||||
```go
|
|
||||||
c, err := core.New(
|
|
||||||
// Auto-discover name from package path
|
|
||||||
core.WithService(myservice.NewService),
|
|
||||||
|
|
||||||
// Explicit name
|
|
||||||
core.WithName("custom", func(c *core.Core) (any, error) {
|
|
||||||
return &CustomService{}, nil
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Retrieving Services
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Safe retrieval with error
|
|
||||||
svc, err := core.ServiceFor[*myservice.Service](c, "myservice")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Service not found: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must retrieval (panics if not found)
|
|
||||||
svc := core.MustServiceFor[*myservice.Service](c, "myservice")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Service Dependencies
|
|
||||||
|
|
||||||
Services can depend on other services:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewOrderService(c *core.Core) (any, error) {
|
|
||||||
// Get required dependencies
|
|
||||||
userSvc := core.MustServiceFor[*user.Service](c, "user")
|
|
||||||
paymentSvc := core.MustServiceFor[*payment.Service](c, "payment")
|
|
||||||
|
|
||||||
return &OrderService{
|
|
||||||
users: userSvc,
|
|
||||||
payments: paymentSvc,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Dependency Order"
|
|
||||||
Register dependencies before services that use them. Core does not automatically resolve dependency order.
|
|
||||||
|
|
||||||
## Exposing to Frontend
|
|
||||||
|
|
||||||
Services are automatically exposed to the frontend via Wails bindings:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Go service method
|
|
||||||
func (s *Service) GetUser(id string) (*User, error) {
|
|
||||||
return s.db.FindUser(id)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// TypeScript (auto-generated)
|
|
||||||
import { GetUser } from '@bindings/myservice/service';
|
|
||||||
|
|
||||||
const user = await GetUser("123");
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Services
|
|
||||||
|
|
||||||
```go
|
|
||||||
func TestMyService(t *testing.T) {
|
|
||||||
// Create mock core
|
|
||||||
c, _ := core.New()
|
|
||||||
|
|
||||||
// Create service
|
|
||||||
svc, err := NewService(c)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test methods
|
|
||||||
result := svc.(*Service).DoSomething()
|
|
||||||
assert.Equal(t, expected, result)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
# WebView Service
|
|
||||||
|
|
||||||
The WebView service (`pkg/webview`) provides programmatic interaction with web content in your application windows.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- JavaScript execution
|
|
||||||
- DOM manipulation
|
|
||||||
- Element interaction (click, type, select)
|
|
||||||
- Console message capture
|
|
||||||
- Screenshots
|
|
||||||
- Network request monitoring
|
|
||||||
- Performance metrics
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/webview"
|
|
||||||
|
|
||||||
// Create service
|
|
||||||
wv := webview.New()
|
|
||||||
|
|
||||||
// Set Wails app reference
|
|
||||||
wv.SetApp(app)
|
|
||||||
```
|
|
||||||
|
|
||||||
## JavaScript Execution
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Execute JavaScript and get result
|
|
||||||
result, err := wv.ExecJS("main", `
|
|
||||||
document.title
|
|
||||||
`)
|
|
||||||
|
|
||||||
// Execute complex scripts
|
|
||||||
result, err := wv.ExecJS("main", `
|
|
||||||
const items = document.querySelectorAll('.item');
|
|
||||||
Array.from(items).map(el => el.textContent);
|
|
||||||
`)
|
|
||||||
```
|
|
||||||
|
|
||||||
## DOM Interaction
|
|
||||||
|
|
||||||
### Click Element
|
|
||||||
|
|
||||||
```go
|
|
||||||
err := wv.Click("main", "#submit-button")
|
|
||||||
err := wv.Click("main", ".nav-link:first-child")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Type Text
|
|
||||||
|
|
||||||
```go
|
|
||||||
err := wv.Type("main", "#search-input", "hello world")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Select Option
|
|
||||||
|
|
||||||
```go
|
|
||||||
err := wv.Select("main", "#country-select", "US")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check/Uncheck
|
|
||||||
|
|
||||||
```go
|
|
||||||
err := wv.Check("main", "#agree-checkbox", true)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Hover
|
|
||||||
|
|
||||||
```go
|
|
||||||
err := wv.Hover("main", ".dropdown-trigger")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scroll
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Scroll to element
|
|
||||||
err := wv.Scroll("main", "#section-3", 0, 0)
|
|
||||||
|
|
||||||
// Scroll by coordinates
|
|
||||||
err := wv.Scroll("main", "", 0, 500)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Element Information
|
|
||||||
|
|
||||||
### Query Selector
|
|
||||||
|
|
||||||
```go
|
|
||||||
elements, err := wv.QuerySelector("main", ".list-item")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Element Info
|
|
||||||
|
|
||||||
```go
|
|
||||||
info, err := wv.GetElementInfo("main", "#user-card")
|
|
||||||
// Returns: tag, id, classes, text, attributes, bounds
|
|
||||||
```
|
|
||||||
|
|
||||||
### Computed Styles
|
|
||||||
|
|
||||||
```go
|
|
||||||
styles, err := wv.GetComputedStyle("main", ".button",
|
|
||||||
[]string{"color", "background-color", "font-size"})
|
|
||||||
```
|
|
||||||
|
|
||||||
### DOM Tree
|
|
||||||
|
|
||||||
```go
|
|
||||||
tree, err := wv.GetDOMTree("main", 5) // max depth 5
|
|
||||||
```
|
|
||||||
|
|
||||||
## Console Messages
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Setup console listener
|
|
||||||
wv.SetupConsoleListener()
|
|
||||||
|
|
||||||
// Inject capture script
|
|
||||||
wv.InjectConsoleCapture("main")
|
|
||||||
|
|
||||||
// Get messages
|
|
||||||
messages := wv.GetConsoleMessages("all", 100)
|
|
||||||
messages := wv.GetConsoleMessages("error", 50)
|
|
||||||
|
|
||||||
// Clear buffer
|
|
||||||
wv.ClearConsole()
|
|
||||||
|
|
||||||
// Get errors only
|
|
||||||
errors := wv.GetErrors(50)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Full page screenshot (base64 PNG)
|
|
||||||
data, err := wv.Screenshot("main")
|
|
||||||
|
|
||||||
// Element screenshot
|
|
||||||
data, err := wv.ScreenshotElement("main", "#chart")
|
|
||||||
|
|
||||||
// Export as PDF
|
|
||||||
pdfData, err := wv.ExportToPDF("main", map[string]any{
|
|
||||||
"margin": 20,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Page Information
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Get current URL
|
|
||||||
url, err := wv.GetURL("main")
|
|
||||||
|
|
||||||
// Get page title
|
|
||||||
title, err := wv.GetTitle("main")
|
|
||||||
|
|
||||||
// Get page source
|
|
||||||
source, err := wv.GetPageSource("main")
|
|
||||||
|
|
||||||
// Navigate
|
|
||||||
err := wv.Navigate("main", "https://example.com")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network Monitoring
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Inject network interceptor
|
|
||||||
wv.InjectNetworkInterceptor("main")
|
|
||||||
|
|
||||||
// Get captured requests
|
|
||||||
requests, err := wv.GetNetworkRequests("main", 100)
|
|
||||||
|
|
||||||
// Clear request log
|
|
||||||
wv.ClearNetworkRequests("main")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Metrics
|
|
||||||
|
|
||||||
```go
|
|
||||||
metrics, err := wv.GetPerformance("main")
|
|
||||||
// Returns: loadTime, domContentLoaded, firstPaint, etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Resource Listing
|
|
||||||
|
|
||||||
```go
|
|
||||||
resources, err := wv.GetResources("main")
|
|
||||||
// Returns: scripts, stylesheets, images, fonts, etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Visual Debugging
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Highlight element temporarily
|
|
||||||
err := wv.Highlight("main", "#target-element", 2000) // 2 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
## Window Listing
|
|
||||||
|
|
||||||
```go
|
|
||||||
windows := wv.ListWindows()
|
|
||||||
for _, w := range windows {
|
|
||||||
fmt.Println(w.Name)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Usage
|
|
||||||
|
|
||||||
The WebView service is primarily used server-side for:
|
|
||||||
|
|
||||||
- Automated testing
|
|
||||||
- AI assistant interactions (via MCP)
|
|
||||||
- Scripted UI interactions
|
|
||||||
|
|
||||||
For normal frontend development, use standard DOM APIs directly.
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
# Workspace Service
|
|
||||||
|
|
||||||
The Workspace service (`pkg/workspace`) manages isolated user workspaces with encrypted storage and PGP key pairs.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Isolated workspace environments
|
|
||||||
- PGP key pair generation per workspace
|
|
||||||
- Encrypted workspace identification
|
|
||||||
- File operations within workspace context
|
|
||||||
- Multiple workspace support
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/Snider/Core/pkg/workspace"
|
|
||||||
|
|
||||||
// With IO medium (standalone)
|
|
||||||
medium, _ := local.New("/app/workspaces")
|
|
||||||
ws, err := workspace.New(medium)
|
|
||||||
|
|
||||||
// With Core framework (recommended)
|
|
||||||
c, _ := core.New(
|
|
||||||
core.WithService(workspace.Register),
|
|
||||||
)
|
|
||||||
ws := core.MustServiceFor[*workspace.Service](c, "workspace")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Creating Workspaces
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create a new encrypted workspace
|
|
||||||
workspaceID, err := ws.CreateWorkspace("my-project", "secure-password")
|
|
||||||
// Returns obfuscated workspace ID
|
|
||||||
|
|
||||||
// Workspace structure created:
|
|
||||||
// workspaces/
|
|
||||||
// <workspace-id>/
|
|
||||||
// config/
|
|
||||||
// log/
|
|
||||||
// data/
|
|
||||||
// files/
|
|
||||||
// keys/
|
|
||||||
// key.pub (PGP public key)
|
|
||||||
// key.priv (PGP private key)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Switching Workspaces
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Switch to a workspace
|
|
||||||
err := ws.SwitchWorkspace(workspaceID)
|
|
||||||
|
|
||||||
// Switch to default workspace
|
|
||||||
err := ws.SwitchWorkspace("default")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Workspace File Operations
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Write file to active workspace
|
|
||||||
err := ws.WorkspaceFileSet("config/settings.json", jsonData)
|
|
||||||
|
|
||||||
// Read file from active workspace
|
|
||||||
content, err := ws.WorkspaceFileGet("config/settings.json")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Listing Workspaces
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Get all workspace IDs
|
|
||||||
workspaces := ws.ListWorkspaces()
|
|
||||||
for _, id := range workspaces {
|
|
||||||
fmt.Println(id)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Active Workspace
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Get current workspace info
|
|
||||||
active := ws.ActiveWorkspace()
|
|
||||||
if active != nil {
|
|
||||||
fmt.Println("Name:", active.Name)
|
|
||||||
fmt.Println("Path:", active.Path)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Workspace Structure
|
|
||||||
|
|
||||||
Each workspace contains:
|
|
||||||
|
|
||||||
| Directory | Purpose |
|
|
||||||
|-----------|---------|
|
|
||||||
| `config/` | Workspace configuration files |
|
|
||||||
| `log/` | Workspace logs |
|
|
||||||
| `data/` | Application data |
|
|
||||||
| `files/` | User files |
|
|
||||||
| `keys/` | PGP key pair |
|
|
||||||
|
|
||||||
## Security Model
|
|
||||||
|
|
||||||
Workspaces use a two-level hashing scheme:
|
|
||||||
|
|
||||||
1. **Real Name**: Hash of the identifier
|
|
||||||
2. **Workspace ID**: Hash of `workspace/{real_name}`
|
|
||||||
|
|
||||||
This prevents workspace enumeration while allowing consistent access.
|
|
||||||
|
|
||||||
## IPC Events
|
|
||||||
|
|
||||||
The workspace service responds to IPC messages:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Switch workspace via IPC
|
|
||||||
c.ACTION(core.Message{
|
|
||||||
Type: "workspace.switch_workspace",
|
|
||||||
Data: map[string]any{
|
|
||||||
"name": workspaceID,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend Usage (TypeScript)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import {
|
|
||||||
CreateWorkspace,
|
|
||||||
SwitchWorkspace,
|
|
||||||
WorkspaceFileGet,
|
|
||||||
WorkspaceFileSet,
|
|
||||||
ListWorkspaces,
|
|
||||||
ActiveWorkspace
|
|
||||||
} from '@bindings/workspace/service';
|
|
||||||
|
|
||||||
// Create workspace
|
|
||||||
const wsId = await CreateWorkspace("my-project", "password");
|
|
||||||
|
|
||||||
// Switch workspace
|
|
||||||
await SwitchWorkspace(wsId);
|
|
||||||
|
|
||||||
// Read/write files
|
|
||||||
const config = await WorkspaceFileGet("config/app.json");
|
|
||||||
await WorkspaceFileSet("config/app.json", JSON.stringify(newConfig));
|
|
||||||
|
|
||||||
// List all workspaces
|
|
||||||
const workspaces = await ListWorkspaces();
|
|
||||||
|
|
||||||
// Get active workspace
|
|
||||||
const active = await ActiveWorkspace();
|
|
||||||
console.log(`Current: ${active.Name} at ${active.Path}`);
|
|
||||||
```
|
|
||||||
Loading…
Add table
Reference in a new issue