refactor: Remove pkg/help (#25)
The help package has been moved to its own repository at Snider/help. This commit removes the local copy of the package from this repository and updates the dependencies. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
|
|
@ -1,22 +0,0 @@
|
||||||
:root {
|
|
||||||
--md-primary-fg-color: #00aaff;
|
|
||||||
--md-primary-fg-color--light: #00aaff;
|
|
||||||
--md-primary-fg-color--dark: #00aaff;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] {
|
|
||||||
--md-default-fg-color: hsla(0, 0%, 100%, 0.7);
|
|
||||||
--md-default-fg-color--light: hsla(0, 0%, 100%, 0.54);
|
|
||||||
--md-default-fg-color--lighter: hsla(0, 0%, 100%, 0.32);
|
|
||||||
--md-default-fg-color--lightest: hsla(0, 0%, 100%, 0.12);
|
|
||||||
|
|
||||||
--md-default-bg-color: #0d0d0d;
|
|
||||||
--md-default-bg-color--light: #1a1a1a;
|
|
||||||
--md-default-bg-color--lighter: #333333;
|
|
||||||
--md-default-bg-color--lightest: #4f4f4f;
|
|
||||||
|
|
||||||
--md-code-fg-color: hsla(0, 0%, 100%, 0.7);
|
|
||||||
--md-code-bg-color: #1a1a1a;
|
|
||||||
|
|
||||||
--md-accent-fg-color: #00aaff;
|
|
||||||
}
|
|
||||||
116
pkg/help/help.go
|
|
@ -1,116 +0,0 @@
|
||||||
package help
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"embed"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Snider/Core/pkg/core"
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed all:public/*
|
|
||||||
var helpStatic embed.FS
|
|
||||||
|
|
||||||
// Options holds configuration for the help service.
|
|
||||||
type Options struct{}
|
|
||||||
|
|
||||||
// Service manages the in-app help system.
|
|
||||||
type Service struct {
|
|
||||||
*core.Runtime[Options]
|
|
||||||
config core.Config
|
|
||||||
display core.Display
|
|
||||||
assets embed.FS
|
|
||||||
}
|
|
||||||
|
|
||||||
// newHelpService contains the common logic for initialising a Service struct.
|
|
||||||
func newHelpService() (*Service, error) {
|
|
||||||
return &Service{
|
|
||||||
assets: helpStatic,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// New is the constructor for static dependency injection.
|
|
||||||
// It creates a Service instance without initialising the core.Runtime field.
|
|
||||||
// Dependencies are passed directly here.
|
|
||||||
func New() (*Service, error) {
|
|
||||||
s, err := newHelpService()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register is the constructor for dynamic dependency injection (used with core.WithService).
|
|
||||||
// It creates a Service instance and initialises its core.Runtime field.
|
|
||||||
// Dependencies are injected during ServiceStartup.
|
|
||||||
func Register(c *core.Core) (any, error) {
|
|
||||||
s, err := newHelpService()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s.Runtime = core.NewRuntime(c, Options{})
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleIPCEvents processes IPC messages, including injecting dependencies on startup.
|
|
||||||
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
|
|
||||||
switch m := msg.(type) {
|
|
||||||
case core.ActionServiceStartup:
|
|
||||||
return s.ServiceStartup(context.Background(), application.ServiceOptions{})
|
|
||||||
default:
|
|
||||||
c.App.Logger.Error("Help: Unknown message type", "type", fmt.Sprintf("%T", m))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServiceStartup is called when the app starts, after dependencies are injected.
|
|
||||||
func (s *Service) ServiceStartup(context.Context, application.ServiceOptions) error {
|
|
||||||
s.Core().App.Logger.Info("Help service started")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show displays the help window.
|
|
||||||
func (s *Service) Show() error {
|
|
||||||
if s.display == nil {
|
|
||||||
return fmt.Errorf("display service not initialized")
|
|
||||||
}
|
|
||||||
if s.Core() == nil {
|
|
||||||
return fmt.Errorf("core runtime not initialized")
|
|
||||||
}
|
|
||||||
msg := map[string]any{
|
|
||||||
"action": "display.open_window",
|
|
||||||
"name": "help",
|
|
||||||
"options": map[string]any{
|
|
||||||
"Title": "Help",
|
|
||||||
"Width": 800,
|
|
||||||
"Height": 600,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Core().ACTION(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShowAt displays a specific section of the help documentation.
|
|
||||||
func (s *Service) ShowAt(anchor string) error {
|
|
||||||
if s.display == nil {
|
|
||||||
return fmt.Errorf("display service not initialized")
|
|
||||||
}
|
|
||||||
if s.Core() == nil {
|
|
||||||
return fmt.Errorf("core runtime not initialized")
|
|
||||||
}
|
|
||||||
msg := map[string]any{
|
|
||||||
"action": "display.open_window",
|
|
||||||
"name": "help",
|
|
||||||
"options": map[string]any{
|
|
||||||
"Title": "Help",
|
|
||||||
"Width": 800,
|
|
||||||
"Height": 600,
|
|
||||||
"URL": fmt.Sprintf("/#%s", anchor),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return s.Core().ACTION(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure Service implements the core.Help interface.
|
|
||||||
var _ core.Help = (*Service)(nil)
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
package help
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/Snider/Core/pkg/core"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockDisplay is a mock implementation of the core.Display interface.
|
|
||||||
type MockDisplay struct {
|
|
||||||
ShowCalled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockDisplay) Show() error {
|
|
||||||
m.ShowCalled = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockDisplay) ShowAt(anchor string) error {
|
|
||||||
m.ShowCalled = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockDisplay) Hide() error { return nil }
|
|
||||||
func (m *MockDisplay) HideAt(anchor string) error { return nil }
|
|
||||||
func (m *MockDisplay) OpenWindow(opts ...core.WindowOption) error { return nil }
|
|
||||||
|
|
||||||
// MockCore is a mock implementation of the *core.Core type.
|
|
||||||
type MockCore struct {
|
|
||||||
Core *core.Core
|
|
||||||
ActionCalled bool
|
|
||||||
ActionMsg core.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
// ACTION matches the signature required by RegisterAction.
|
|
||||||
func (m *MockCore) ACTION(c *core.Core, msg core.Message) error {
|
|
||||||
m.ActionCalled = true
|
|
||||||
m.ActionMsg = msg
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupService(t *testing.T) (*Service, *MockCore, *MockDisplay) {
|
|
||||||
s, err := New()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
app := application.New(application.Options{})
|
|
||||||
c, err := core.New(core.WithWails(app))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
mockCore := &MockCore{Core: c}
|
|
||||||
mockDisplay := &MockDisplay{}
|
|
||||||
|
|
||||||
s.Runtime = core.NewRuntime(c, Options{})
|
|
||||||
s.display = mockDisplay
|
|
||||||
// Register our mock handler. When the real s.Core().ACTION is called,
|
|
||||||
// our mock handler will be executed.
|
|
||||||
c.RegisterAction(mockCore.ACTION)
|
|
||||||
|
|
||||||
return s, mockCore, mockDisplay
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
s, err := New()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.NotNil(t, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShow(t *testing.T) {
|
|
||||||
s, mockCore, _ := setupService(t)
|
|
||||||
|
|
||||||
err := s.Show()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, mockCore.ActionCalled)
|
|
||||||
|
|
||||||
msg, ok := mockCore.ActionMsg.(map[string]any)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.Equal(t, "display.open_window", msg["action"])
|
|
||||||
assert.Equal(t, "help", msg["name"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShowAt(t *testing.T) {
|
|
||||||
s, mockCore, _ := setupService(t)
|
|
||||||
|
|
||||||
err := s.ShowAt("test-anchor")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, mockCore.ActionCalled)
|
|
||||||
|
|
||||||
msg, ok := mockCore.ActionMsg.(map[string]any)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.Equal(t, "display.open_window", msg["action"])
|
|
||||||
assert.Equal(t, "help", msg["name"])
|
|
||||||
|
|
||||||
opts, ok := msg["options"].(map[string]any)
|
|
||||||
assert.True(t, ok)
|
|
||||||
assert.Equal(t, "/#test-anchor", opts["URL"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandleIPCEvents_ServiceStartup(t *testing.T) {
|
|
||||||
s, _, _ := setupService(t)
|
|
||||||
err := s.HandleIPCEvents(s.Core(), core.ActionServiceStartup{})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
site_name: Core.Help
|
|
||||||
site_url: https://dappco.re
|
|
||||||
repo_url: https://github.com/Snider/Core
|
|
||||||
edit_uri: edit/main/docs/src/
|
|
||||||
docs_dir: src
|
|
||||||
nav:
|
|
||||||
- Overview: index.md
|
|
||||||
- Core: core/index.md
|
|
||||||
- Config: core/config.md
|
|
||||||
- Crypt: core/crypt.md
|
|
||||||
- Display: core/display.md
|
|
||||||
- Help: core/help.md
|
|
||||||
- IO: core/io.md
|
|
||||||
- Workspace: core/workspace.md
|
|
||||||
|
|
||||||
copyright: Core © EUPL-1.2 — 2024 to ∞ and beyond
|
|
||||||
|
|
||||||
theme:
|
|
||||||
name: material
|
|
||||||
palette:
|
|
||||||
# Palette toggle for dark mode
|
|
||||||
- scheme: slate
|
|
||||||
primary: blue
|
|
||||||
accent: blue
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-4
|
|
||||||
name: Switch to light mode
|
|
||||||
|
|
||||||
# Palette toggle for light mode
|
|
||||||
- scheme: default
|
|
||||||
primary: blue
|
|
||||||
accent: blue
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-7
|
|
||||||
name: Switch to dark mode
|
|
||||||
|
|
||||||
font:
|
|
||||||
text: "Roboto"
|
|
||||||
code: "Roboto Mono"
|
|
||||||
language: en
|
|
||||||
icon:
|
|
||||||
repo: fontawesome/brands/git-alt
|
|
||||||
edit: material/pencil
|
|
||||||
view: material/eye
|
|
||||||
|
|
||||||
features:
|
|
||||||
- navigation.instant
|
|
||||||
- navigation.tracking
|
|
||||||
- navigation.tabs
|
|
||||||
- navigation.indexes
|
|
||||||
- navigation.expand
|
|
||||||
- navigation.sections
|
|
||||||
- navigation.path
|
|
||||||
- navigation.top
|
|
||||||
- search.suggest
|
|
||||||
- search.highlight
|
|
||||||
- search.share
|
|
||||||
- content.tabs.link
|
|
||||||
- content.code.copy
|
|
||||||
|
|
||||||
extra:
|
|
||||||
generator: false
|
|
||||||
social:
|
|
||||||
- icon: fontawesome/brands/github
|
|
||||||
link: https://github.com/Snider/Core
|
|
||||||
- icon: fontawesome/solid/globe
|
|
||||||
link: https://dappco.re
|
|
||||||
|
|
||||||
extra_css:
|
|
||||||
- assets/stylesheets/extra.css
|
|
||||||
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- offline:
|
|
||||||
- privacy:
|
|
||||||
- group:
|
|
||||||
enabled: !ENV CI
|
|
||||||
plugins:
|
|
||||||
- git-revision-date-localized:
|
|
||||||
type: timeago
|
|
||||||
enable_creation_date: true
|
|
||||||
fallback_to_build_date: true
|
|
||||||
tz: Europe/London
|
|
||||||
- git-committers:
|
|
||||||
- group:
|
|
||||||
enabled: !ENV INSIDERS
|
|
||||||
plugins:
|
|
||||||
- optimise
|
|
||||||
- search:
|
|
||||||
lang:
|
|
||||||
- en
|
|
||||||
|
|
||||||
markdown_extensions:
|
|
||||||
- abbr
|
|
||||||
- admonition
|
|
||||||
- attr_list
|
|
||||||
- def_list
|
|
||||||
- footnotes
|
|
||||||
- meta
|
|
||||||
- toc:
|
|
||||||
permalink: true
|
|
||||||
- tables
|
|
||||||
- md_in_html
|
|
||||||
- pymdownx.highlight:
|
|
||||||
anchor_linenums: true
|
|
||||||
line_spans: __span
|
|
||||||
pygments_lang_class: true
|
|
||||||
- pymdownx.inlinehilite
|
|
||||||
- pymdownx.snippets
|
|
||||||
- pymdownx.details
|
|
||||||
- pymdownx.superfences:
|
|
||||||
custom_fences:
|
|
||||||
- name: mermaid
|
|
||||||
class: mermaid
|
|
||||||
format: !!python/name:pymdownx.superfences.fence_code_format
|
|
||||||
- pymdownx.betterem
|
|
||||||
- pymdownx.tabbed:
|
|
||||||
alternate_style: true
|
|
||||||
- pymdownx.emoji:
|
|
||||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
|
||||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
mkdocs-material
|
|
||||||
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 695 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 756 B |
|
Before Width: | Height: | Size: 582 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
|
|
@ -1,78 +0,0 @@
|
||||||
---
|
|
||||||
title: Core.Help
|
|
||||||
---
|
|
||||||
|
|
||||||
# Overview
|
|
||||||
|
|
||||||
Core is an opinionated framework for building Go desktop apps with Wails, providing a small set of focused modules you can mix into your app. It ships with sensible defaults and a demo app that doubles as in‑app help.
|
|
||||||
|
|
||||||
- Site: [https://dappco.re](https://dappco.re)
|
|
||||||
- Repo: [https://github.com/Snider/Core](https://github.com/Snider/Core)
|
|
||||||
|
|
||||||
## Modules
|
|
||||||
|
|
||||||
- Core — framework bootstrap and service container
|
|
||||||
- Core.Config — app and UI state persistence
|
|
||||||
- Core.Crypt — keys, encrypt/decrypt, sign/verify
|
|
||||||
- Core.Display — windows, tray, window state
|
|
||||||
- Core.Help — in‑app help and deep‑links
|
|
||||||
- Core.IO — local/remote filesystem helpers
|
|
||||||
- Core.Workspace — projects and paths
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
|
||||||
core "github.com/Snider/Core"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := core.New(
|
|
||||||
core.WithServiceLock(),
|
|
||||||
)
|
|
||||||
wailsApp := application.NewWithOptions(&application.Options{
|
|
||||||
Bind: []interface{}{app},
|
|
||||||
})
|
|
||||||
wailsApp.Run()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Services
|
|
||||||
```go
|
|
||||||
package demo
|
|
||||||
|
|
||||||
import (
|
|
||||||
core "github.com/Snider/Core"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register your service
|
|
||||||
func Register(c *core.Core) error {
|
|
||||||
return c.RegisterService("demo", &Demo{core: c})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Display example
|
|
||||||
```go
|
|
||||||
package display
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Open a window on startup
|
|
||||||
func (d *API) ServiceStartup(ctx context.Context, _ application.ServiceOptions) error {
|
|
||||||
d.OpenWindow(
|
|
||||||
OptName("main"),
|
|
||||||
OptHeight(900),
|
|
||||||
OptWidth(1280),
|
|
||||||
OptURL("/"),
|
|
||||||
OptTitle("Core"),
|
|
||||||
)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See the left nav for detailed pages on each module.
|
|
||||||
|
|
@ -1,367 +0,0 @@
|
||||||
[data-md-color-scheme="lethean"] {
|
|
||||||
--md-primary-fg-color: #0F131C;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-section {
|
|
||||||
background: linear-gradient(135deg, #0F131C 0%, #1a237e 100%);
|
|
||||||
color: white;
|
|
||||||
padding: 4rem 2rem;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-content {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-content h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
color: white;
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-subtitle {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-badges {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 20px;
|
|
||||||
margin: 0 0.5rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-button {
|
|
||||||
display: inline-block;
|
|
||||||
background: #4A90E2;
|
|
||||||
color: white;
|
|
||||||
padding: 0.8rem 2rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: background 0.3s, transform 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-button:hover {
|
|
||||||
background: #357ABD;
|
|
||||||
color: white;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-button.secondary {
|
|
||||||
background: transparent;
|
|
||||||
border: 2px solid #4A90E2;
|
|
||||||
color: #4A90E2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.features-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
||||||
gap: 0.2rem;
|
|
||||||
padding: 0.2rem;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1.0rem;
|
|
||||||
border: 2px solid #e2e8f0;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .feature-card {
|
|
||||||
background: #2d3748;
|
|
||||||
border-color: #4a5568;
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card img {
|
|
||||||
width: 100%;
|
|
||||||
height: 150px;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-card h3 {
|
|
||||||
margin: 1rem 0;
|
|
||||||
color: #0F131C;
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .feature-card h3 {
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.get-started {
|
|
||||||
color: #4A90E2;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.benefits-section {
|
|
||||||
background: #f5f5f5;
|
|
||||||
padding: 0.4rem 0.2rem;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.benefits-section h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
margin-top: 0.8rem;
|
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .benefits-section {
|
|
||||||
background: #1a202c;
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.benefits-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 0.2rem;
|
|
||||||
padding: 0.2rem;
|
|
||||||
margin: 0.2rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.benefit-card {
|
|
||||||
background: white;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid #e2e8f0;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .benefit-card {
|
|
||||||
background: #2d3748;
|
|
||||||
border-color: #4a5568;
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.roadmap-section {
|
|
||||||
padding: 0.4rem 0.2rem;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline {
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 2rem;
|
|
||||||
margin: 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item {
|
|
||||||
background: white;
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 2px solid #e2e8f0;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
position: relative;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item.completed {
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .timeline-item {
|
|
||||||
background: #2d3748;
|
|
||||||
border-color: #4a5568;
|
|
||||||
color: #e2e8f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-marker {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: absolute;
|
|
||||||
top: -10px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item.planning .timeline-marker {
|
|
||||||
background: #718096;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item.in-progress .timeline-marker {
|
|
||||||
background: #4A90E2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item.completed .timeline-marker {
|
|
||||||
background: #48BB78;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item ul {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
padding-left: 24px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li::before {
|
|
||||||
content: "";
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li.planned::before {
|
|
||||||
background: #718096;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li.active::before {
|
|
||||||
background: #4A90E2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li.completed::before {
|
|
||||||
background: #48BB78;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li ul {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li ul li {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
margin: 0.25rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li ul li::before {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
background: #a0aec0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li ul li a {
|
|
||||||
color: #4A90E2;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item li ul li a:hover {
|
|
||||||
color: #357ABD;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .timeline-item li ul li a {
|
|
||||||
color: #63b3ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .timeline-item li ul li a:hover {
|
|
||||||
color: #90cdf4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: #718096;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-md-color-scheme="slate"] .date {
|
|
||||||
color: #a0aec0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-section {
|
|
||||||
background: #0F131C;
|
|
||||||
color: white;
|
|
||||||
padding: 4rem 2rem;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.community-section {
|
|
||||||
padding: 4rem 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.community-links {
|
|
||||||
display: flex;
|
|
||||||
gap: 2rem;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.community-link {
|
|
||||||
color: #4A90E2;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.community-link:hover {
|
|
||||||
color: #357ABD;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.hero-content h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item.completed {
|
|
||||||
grid-column: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.features-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-buttons {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.community-links {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
version: '3'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
default:
|
|
||||||
cmds:
|
|
||||||
- task: dev
|
|
||||||
desc: "Start the development server."
|
|
||||||
|
|
||||||
install:
|
|
||||||
cmds:
|
|
||||||
- pip install -r requirements.txt
|
|
||||||
desc: "Install documentation dependencies."
|
|
||||||
status:
|
|
||||||
- test -f requirements.txt
|
|
||||||
|
|
||||||
dev:
|
|
||||||
run: always
|
|
||||||
cmds:
|
|
||||||
- mkdocs serve -o -c -w src -w mkdocs.yml
|
|
||||||
desc: "Start the live-reloading documentation server."
|
|
||||||
sources:
|
|
||||||
- mkdocs.yml
|
|
||||||
- "src/**/*"
|
|
||||||
- "src/**/*.css"
|
|
||||||
generates:
|
|
||||||
- "public/**/*"
|
|
||||||
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- mkdocs build --clean -d public
|
|
||||||
desc: "Build the static documentation site."
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
desc: "Deploy the documentation to GitHub Pages."
|
|
||||||
cmds:
|
|
||||||
- mkdocs gh-deploy --force
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/Snider/Core/pkg/core"
|
"github.com/Snider/Core/pkg/core"
|
||||||
"github.com/Snider/Core/pkg/crypt"
|
"github.com/Snider/Core/pkg/crypt"
|
||||||
"github.com/Snider/Core/pkg/display"
|
"github.com/Snider/Core/pkg/display"
|
||||||
"github.com/Snider/Core/pkg/help"
|
|
||||||
"github.com/Snider/Core/pkg/i18n"
|
"github.com/Snider/Core/pkg/i18n"
|
||||||
"github.com/Snider/Core/pkg/workspace"
|
"github.com/Snider/Core/pkg/workspace"
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
|
@ -21,7 +20,6 @@ type Runtime struct {
|
||||||
Core *core.Core
|
Core *core.Core
|
||||||
Config *config.Service
|
Config *config.Service
|
||||||
Display *display.Service
|
Display *display.Service
|
||||||
Help *help.Service
|
|
||||||
Crypt *crypt.Service
|
Crypt *crypt.Service
|
||||||
I18n *i18n.Service
|
I18n *i18n.Service
|
||||||
Workspace *workspace.Service
|
Workspace *workspace.Service
|
||||||
|
|
@ -37,7 +35,7 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
||||||
core.WithWails(app),
|
core.WithWails(app),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, name := range []string{"config", "display", "help", "crypt", "i18n", "workspace"} {
|
for _, name := range []string{"config", "display", "crypt", "i18n", "workspace"} {
|
||||||
factory, ok := factories[name]
|
factory, ok := factories[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("service %s factory not provided", name)
|
return nil, fmt.Errorf("service %s factory not provided", name)
|
||||||
|
|
@ -65,10 +63,6 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("display service has unexpected type")
|
return nil, fmt.Errorf("display service has unexpected type")
|
||||||
}
|
}
|
||||||
helpSvc, ok := services["help"].(*help.Service)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("help service has unexpected type")
|
|
||||||
}
|
|
||||||
cryptSvc, ok := services["crypt"].(*crypt.Service)
|
cryptSvc, ok := services["crypt"].(*crypt.Service)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("crypt service has unexpected type")
|
return nil, fmt.Errorf("crypt service has unexpected type")
|
||||||
|
|
@ -87,7 +81,6 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory)
|
||||||
Core: coreInstance,
|
Core: coreInstance,
|
||||||
Config: configSvc,
|
Config: configSvc,
|
||||||
Display: displaySvc,
|
Display: displaySvc,
|
||||||
Help: helpSvc,
|
|
||||||
Crypt: cryptSvc,
|
Crypt: cryptSvc,
|
||||||
I18n: i18nSvc,
|
I18n: i18nSvc,
|
||||||
Workspace: workspaceSvc,
|
Workspace: workspaceSvc,
|
||||||
|
|
@ -101,7 +94,6 @@ func New(app *application.App) (*Runtime, error) {
|
||||||
return NewWithFactories(app, map[string]ServiceFactory{
|
return NewWithFactories(app, map[string]ServiceFactory{
|
||||||
"config": func() (any, error) { return config.New() },
|
"config": func() (any, error) { return config.New() },
|
||||||
"display": func() (any, error) { return display.New() },
|
"display": func() (any, error) { return display.New() },
|
||||||
"help": func() (any, error) { return help.New() },
|
|
||||||
"crypt": func() (any, error) { return crypt.New() },
|
"crypt": func() (any, error) { return crypt.New() },
|
||||||
"i18n": func() (any, error) { return i18n.New() },
|
"i18n": func() (any, error) { return i18n.New() },
|
||||||
"workspace": func() (any, error) { return workspace.New() },
|
"workspace": func() (any, error) { return workspace.New() },
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/Snider/Core/pkg/config"
|
"github.com/Snider/Core/pkg/config"
|
||||||
"github.com/Snider/Core/pkg/crypt"
|
"github.com/Snider/Core/pkg/crypt"
|
||||||
"github.com/Snider/Core/pkg/display"
|
"github.com/Snider/Core/pkg/display"
|
||||||
"github.com/Snider/Core/pkg/help"
|
|
||||||
"github.com/Snider/Core/pkg/i18n"
|
"github.com/Snider/Core/pkg/i18n"
|
||||||
"github.com/Snider/Core/pkg/runtime"
|
"github.com/Snider/Core/pkg/runtime"
|
||||||
"github.com/Snider/Core/pkg/workspace"
|
"github.com/Snider/Core/pkg/workspace"
|
||||||
|
|
@ -30,7 +29,6 @@ func TestNew(t *testing.T) {
|
||||||
factories: map[string]runtime.ServiceFactory{
|
factories: map[string]runtime.ServiceFactory{
|
||||||
"config": func() (any, error) { return &config.Service{}, nil },
|
"config": func() (any, error) { return &config.Service{}, nil },
|
||||||
"display": func() (any, error) { return &display.Service{}, nil },
|
"display": func() (any, error) { return &display.Service{}, nil },
|
||||||
"help": func() (any, error) { return &help.Service{}, nil },
|
|
||||||
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
||||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||||
|
|
@ -41,7 +39,6 @@ func TestNew(t *testing.T) {
|
||||||
assert.NotNil(t, rt.Core)
|
assert.NotNil(t, rt.Core)
|
||||||
assert.NotNil(t, rt.Config)
|
assert.NotNil(t, rt.Config)
|
||||||
assert.NotNil(t, rt.Display)
|
assert.NotNil(t, rt.Display)
|
||||||
assert.NotNil(t, rt.Help)
|
|
||||||
assert.NotNil(t, rt.Crypt)
|
assert.NotNil(t, rt.Crypt)
|
||||||
assert.NotNil(t, rt.I18n)
|
assert.NotNil(t, rt.I18n)
|
||||||
assert.NotNil(t, rt.Workspace)
|
assert.NotNil(t, rt.Workspace)
|
||||||
|
|
@ -53,7 +50,6 @@ func TestNew(t *testing.T) {
|
||||||
factories: map[string]runtime.ServiceFactory{
|
factories: map[string]runtime.ServiceFactory{
|
||||||
"config": func() (any, error) { return &config.Service{}, nil },
|
"config": func() (any, error) { return &config.Service{}, nil },
|
||||||
"display": func() (any, error) { return &display.Service{}, nil },
|
"display": func() (any, error) { return &display.Service{}, nil },
|
||||||
"help": func() (any, error) { return &help.Service{}, nil },
|
|
||||||
"crypt": func() (any, error) { return nil, errors.New("crypt service failed") },
|
"crypt": func() (any, error) { return nil, errors.New("crypt service failed") },
|
||||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||||
|
|
@ -67,7 +63,6 @@ func TestNew(t *testing.T) {
|
||||||
factories: map[string]runtime.ServiceFactory{
|
factories: map[string]runtime.ServiceFactory{
|
||||||
"config": func() (any, error) { return &config.Service{}, nil },
|
"config": func() (any, error) { return &config.Service{}, nil },
|
||||||
"display": func() (any, error) { return "not a display service", nil },
|
"display": func() (any, error) { return "not a display service", nil },
|
||||||
"help": func() (any, error) { return &help.Service{}, nil },
|
|
||||||
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
||||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||||
|
|
@ -81,7 +76,6 @@ func TestNew(t *testing.T) {
|
||||||
factories: map[string]runtime.ServiceFactory{
|
factories: map[string]runtime.ServiceFactory{
|
||||||
"config": func() (any, error) { return &config.Service{}, nil },
|
"config": func() (any, error) { return &config.Service{}, nil },
|
||||||
"display": func() (any, error) { return &display.Service{}, nil },
|
"display": func() (any, error) { return &display.Service{}, nil },
|
||||||
"help": func() (any, error) { return &help.Service{}, nil },
|
|
||||||
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
"crypt": func() (any, error) { return &crypt.Service{}, nil },
|
||||||
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
"i18n": func() (any, error) { return &i18n.Service{}, nil },
|
||||||
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
"workspace": func() (any, error) { return &workspace.Service{}, nil },
|
||||||
|
|
|
||||||