Compare commits

...

43 Commits

Author SHA1 Message Date
stffabi b3a9332a5b [assetserver] Add linux platform implementation (#2543) 2023-03-31 20:39:13 +11:00
Lea Anthony e91b7832ee Provide types to runtime api 2023-03-31 20:37:50 +11:00
Lea Anthony 0d25edc0c4 Improved api package 2023-03-30 12:51:43 +11:00
Lea Anthony eb36258c73 Merge branch 'master' into v3/plugins 2023-03-27 20:58:00 +11:00
Misite Bao bdb138d42c chore: fix push.yml (#2530) 2023-03-27 07:37:38 +11:00
Arpit Jain abe6f520b4 Fix dates in changelog (#2527) 2023-03-26 16:19:12 +11:00
Adam Tenderholt 130fab6c01 Feature/v3 parser: expand TS model generation tests & some fixes (#2485)
* v3 parser: add tests for model generation

* v3 parser: use single quotes for got model.ts

* v3 parser: fixes for some failing tests

* v3 parser: misc simplification and cleanup

* v3 parser: fix model tests when no structs returned

* v3 parser: fix last failing test case

* Update contributors list

* v3 parser: update README

* Revert "Update contributors list"

This reverts commit f429d2ba890020d7ff0772e6207830508261b489.

* Changelog: add line about my contribution
2023-03-25 10:09:17 +11:00
Lea Anthony 365aaf6bb1 Add sqlite plugin 2023-03-25 09:39:59 +11:00
github-actions[bot] b5f1eab59b chore: update sponsors.svg (#2525)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-24 23:20:18 +11:00
Lea Anthony 5949e305ea Reorder startup sequence
Add plugin scripts to asset server
Add InjectJS support in plugins
2023-03-24 08:43:00 +11:00
github-actions[bot] 5988cdca9a chore: update sponsors.svg (#2517)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-23 19:33:04 +11:00
Lea Anthony bf86b0d9c1 Add Window creation hooks
Add InjectJS() to plugins interface
2023-03-23 06:51:24 +11:00
Misite Bao 93d7c6b5f3 docs: optimize code block display style (#2520) 2023-03-22 21:54:08 +11:00
Lea Anthony d5808fde59 Simple logging plugin 2023-03-22 20:56:45 +11:00
Lea Anthony 3ae604e474 Exported() -> CallableByJS() 2023-03-22 20:42:20 +11:00
Lea Anthony 42fb91bc73 Fix for variadic args in bound methods 2023-03-22 20:36:46 +11:00
github-actions[bot] 7d28ad1a3b docs: sync documents (#2510)
Co-authored-by: misitebao <misitebao@users.noreply.github.com>
2023-03-21 17:47:23 +11:00
Lea Anthony 4165caa02e Add Exported() []string to plugin API 2023-03-21 08:55:55 +11:00
Lea Anthony b656bd2194 Prevent beeps when pressing a key in Mac window 2023-03-20 20:48:22 +11:00
Lea Anthony a7278b4224 Update docs 2023-03-20 20:32:25 +11:00
Lea Anthony e2b7b68ec9 v2.4.1 2023-03-20 20:31:35 +11:00
Lea Anthony ba82f27534 Fix Bug with nil parameters.
Added browser and kvstore plugins.
2023-03-20 20:28:33 +11:00
stffabi 47af68a8cb [windows] Allow single-clicks on wails-draggable items (#2482)
* [runtime] Make it possible to restore old drag behaviour on windows with window.wails.flags.deferDragToMouseMove

* [runtime] Use deferDragToMouseMove as new default

This enhanced version should work well in all cases for Windows:
- No lag/delayed start of dragging
- Double-Clicks and Single-Click events still work in JavaScript on elements

* [docs] Add changelog
2023-03-20 08:45:21 +01:00
github-actions[bot] 77f053068c docs: sync documents (#2504)
Co-authored-by: misitebao <misitebao@users.noreply.github.com>
2023-03-19 20:37:06 +11:00
Lea Anthony 13f620ae54 Add plugin init. Update plugin signature. 2023-03-19 09:13:05 +11:00
github-actions[bot] e3edea1cb3 chore: update sponsors.svg (#2498)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-18 11:48:23 +11:00
Lea Anthony c774af48b7 Extend call subsystem to support plugins 2023-03-18 09:12:44 +11:00
Lea Anthony 4300521064 Initial implementation 2023-03-18 08:11:36 +11:00
Filip Borkiewicz a0b7eb4ab6 Fix incorrect rendering of FAQ section header (#2497) 2023-03-18 07:37:21 +11:00
Lea Anthony 86aaa3a956 Merge branch 'v3/central-assetserver' into v3/plugins 2023-03-17 17:03:21 +11:00
stffabi 6ed0eb217d [webview2] Fix error handling in CreateWebResourceResponse (#2496)
The old error handling fails on WebView2Runtimes > 110.0.1587.69 and
will show a "connection refused" page and reloads after a short time.
After the reload everything worked as expected again.
2023-03-16 10:49:59 +01:00
github-actions[bot] a2c1c507e1 chore: update sponsors.svg (#2492)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-16 19:27:23 +11:00
stffabi 74f349862e [v3] AssetServer options update comments 2023-03-16 06:49:46 +01:00
stffabi 0e614b0194 [v3] Centralize the AssetServer and move it to the application 2023-03-16 06:49:45 +01:00
github-actions[bot] 4bffc57cb5 chore: update sponsors.svg (#2488)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-14 17:32:25 +11:00
stffabi 5dba18c853 [dev] Fix panic in devserver when using the logger (#2481) 2023-03-13 08:12:09 +01:00
github-actions[bot] 4cd873fecb docs: sync documents (#2443)
Co-authored-by: misitebao <misitebao@users.noreply.github.com>
2023-03-11 17:57:56 +11:00
Misite Bao e339c40e85 feat: update sync translated document script (#2479) 2023-03-11 14:09:48 +11:00
github-actions[bot] 050ac9e613 chore: update sponsors.svg (#2478)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-11 12:54:20 +11:00
Lea Anthony 58986237b9 [v3] Minor fixes to templates + improved Taskfile 2023-03-11 12:48:10 +11:00
Lea Anthony 7cb4c7b505 [v3] Improve init command 2023-03-10 08:05:13 +11:00
dependabot[bot] e3816e714e Bump golang.org/x/net in /v3/examples/binding (#2472)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20220722155237-a158d28d115b to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/commits/v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 20:07:11 +11:00
github-actions[bot] 1684894b52 chore: update sponsors.svg (#2474)
Co-authored-by: leaanthony <leaanthony@users.noreply.github.com>
2023-03-09 20:06:35 +11:00
446 changed files with 10734 additions and 4026 deletions
+1 -1
View File
@@ -22,12 +22,12 @@ jobs:
website/**/*.json
- name: Set node
if: steps.verify-changed-files.outputs.files_changed != 'true'
uses: actions/setup-node@v2
with:
node-version: 16.x
- name: Push files
if: steps.verify-changed-files.outputs.files_changed == 'true'
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
run: |
+1
View File
@@ -34,3 +34,4 @@ v2/cmd/wails/internal/commands/initialise/templates/testtemplates/
/v3/.task
/v3/examples/build/bin/testapp
/websitev3/site/
+1
View File
@@ -97,6 +97,7 @@ This project is supported by these kind people / companies:
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## FAQ
- Is this an alternative to Electron?
+1 -1
View File
@@ -2,4 +2,4 @@
cd ../website
npx @crowdin/cli@latest pull -b master
npx @crowdin/cli@latest pull -b master --export-only-approved
+1 -1
View File
@@ -1 +1 @@
v2.4.0
v2.4.1
+6 -43
View File
@@ -78,7 +78,6 @@ import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"runtime"
@@ -88,6 +87,7 @@ import (
"unsafe"
"github.com/wailsapp/wails/v2/pkg/assetserver"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/frontend"
@@ -166,10 +166,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
}
result.assets = assets
// Start 10 processors to handle requests in parallel
for i := 0; i < 10; i++ {
go result.startRequestProcessor()
}
go result.startRequestProcessor()
}
go result.startMessageProcessor()
@@ -461,50 +458,16 @@ func processMessage(message *C.char) {
messageBuffer <- goMessage
}
var requestBuffer = make(chan unsafe.Pointer, 100)
var requestBuffer = make(chan webview.Request, 100)
func (f *Frontend) startRequestProcessor() {
for request := range requestBuffer {
f.processRequest(request)
C.g_object_unref(C.gpointer(request))
f.assets.ServeWebViewRequest(request)
request.Release()
}
}
//export processURLRequest
func processURLRequest(request unsafe.Pointer) {
// Increment reference counter to allow async processing, will be decremented after the processing
// has been finished by a worker.
C.g_object_ref(C.gpointer(request))
requestBuffer <- request
}
func (f *Frontend) processRequest(request unsafe.Pointer) {
req := (*C.WebKitURISchemeRequest)(request)
uri := C.webkit_uri_scheme_request_get_uri(req)
goURI := C.GoString(uri)
rw := &webKitResponseWriter{req: req}
defer rw.Close()
f.assets.ProcessHTTPRequestLegacy(
rw,
func() (*http.Request, error) {
method := webkit_uri_scheme_request_get_http_method(req)
r, err := http.NewRequest(method, goURI, nil)
if err != nil {
return nil, err
}
r.Header = webkit_uri_scheme_request_get_http_headers(req)
if r.URL.Host != f.startURL.Host {
if r.Body != nil {
r.Body.Close()
}
return nil, fmt.Errorf("Expected host '%s' in request, but was '%s'", f.startURL.Host, r.URL.Host)
}
return r, nil
})
requestBuffer <- webview.NewRequest(request)
}
@@ -12,10 +12,12 @@ import (
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/linux"
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
)
func validateWebKit2Version(options *options.App) {
if C.webkit_get_major_version() == 2 && C.webkit_get_minor_version() >= webkit2MinMinorVersion {
if C.webkit_get_major_version() == 2 && C.webkit_get_minor_version() >= webview.Webkit2MinMinorVersion {
return
}
@@ -24,6 +26,6 @@ func validateWebKit2Version(options *options.App) {
msg = options.Linux.Messages
}
v := fmt.Sprintf("2.%d.0", webkit2MinMinorVersion)
v := fmt.Sprintf("2.%d.0", webview.Webkit2MinMinorVersion)
showModalDialogAndExit("WebKit2GTK", fmt.Sprintf(msg.WebKit2GTKMinRequired, v))
}
@@ -4,6 +4,7 @@
package edge
import (
"fmt"
"log"
"runtime"
"syscall"
@@ -249,7 +250,7 @@ func (e *ICoreWebView2Environment) CreateWebResourceResponse(content []byte, sta
return nil, err
}
var response *ICoreWebView2WebResourceResponse
_, _, err = e.vtbl.CreateWebResourceResponse.Call(
hr, _, err := e.vtbl.CreateWebResourceResponse.Call(
uintptr(unsafe.Pointer(e)),
stream,
uintptr(statusCode),
@@ -257,7 +258,14 @@ func (e *ICoreWebView2Environment) CreateWebResourceResponse(content []byte, sta
uintptr(unsafe.Pointer(_headers)),
uintptr(unsafe.Pointer(&response)),
)
if err != windows.ERROR_SUCCESS {
if windows.Handle(hr) != windows.S_OK {
return nil, syscall.Errno(hr)
}
if response == nil {
if err == nil {
err = fmt.Errorf("unknown error")
}
return nil, err
}
return response, nil
+5 -4
View File
@@ -62,9 +62,13 @@ func (d *DevWebServer) Run(ctx context.Context) error {
return err
}
var myLogger assetserver.Logger
if _logger := ctx.Value("logger"); _logger != nil {
myLogger = _logger.(*logger.Logger)
}
var assetHandler http.Handler
var wsHandler http.Handler
var myLogger *logger.Logger
_fronendDevServerURL, _ := ctx.Value("frontenddevserverurl").(string)
if _fronendDevServerURL == "" {
@@ -73,9 +77,6 @@ func (d *DevWebServer) Run(ctx context.Context) error {
return c.String(http.StatusOK, assetdir)
})
if _logger := ctx.Value("logger"); _logger != nil {
myLogger = _logger.(*logger.Logger)
}
var err error
assetHandler, err = assetserver.NewAssetHandler(assetServerConfig, myLogger)
if err != nil {
+4 -5
View File
@@ -66,7 +66,7 @@ window.wails = {
defaultCursor: null,
borderThickness: 6,
shouldDrag: false,
deferDragToMouseMove: false,
deferDragToMouseMove: true,
cssDragProperty: "--wails-draggable",
cssDragValue: "drag",
}
@@ -130,7 +130,7 @@ window.addEventListener('mousedown', (e) => {
}
}
if (window.wails.flags.deferDragToMouseMove) {
window.wails.flags.shouldDrag = true;
window.wails.flags.shouldDrag = true;
} else {
e.preventDefault()
window.WailsInvoke("drag");
@@ -152,10 +152,9 @@ function setResize(cursor) {
window.addEventListener('mousemove', function (e) {
if (window.wails.flags.shouldDrag) {
window.wails.flags.shouldDrag = false;
let mousePressed = e.buttons !== undefined ? e.buttons : e.which;
if(mousePressed <= 0) {
window.wails.flags.shouldDrag = false;
} else {
if (mousePressed > 0) {
window.WailsInvoke("drag");
return;
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+28
View File
@@ -2,8 +2,11 @@ package assetserver
import (
"bytes"
"fmt"
"math/rand"
"net/http"
"net/http/httptest"
"strings"
"golang.org/x/net/html"
@@ -42,6 +45,9 @@ type AssetServer struct {
// Use http based runtime
runtimeHandler RuntimeHandler
// plugin scripts
pluginScripts map[string]string
assetServerWebView
}
@@ -89,6 +95,16 @@ func (d *AssetServer) UseRuntimeHandler(handler RuntimeHandler) {
d.runtimeHandler = handler
}
func (d *AssetServer) AddPluginScript(pluginName string, script string) {
if d.pluginScripts == nil {
d.pluginScripts = make(map[string]string)
}
pluginName = strings.ReplaceAll(pluginName, "/", "_")
pluginName = html.EscapeString(pluginName)
pluginScriptName := fmt.Sprintf("/plugin_%s_%d.js", pluginName, rand.Intn(100000))
d.pluginScripts[pluginScriptName] = script
}
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if isWebSocket(req) {
// Forward WebSockets to the distinct websocket handler if it exists
@@ -149,6 +165,11 @@ func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
d.writeBlob(rw, path, content)
default:
// Check if this is a plugin script
if script, ok := d.pluginScripts[path]; ok {
d.writeBlob(rw, path, []byte(script))
return
}
d.handler.ServeHTTP(rw, req)
}
}
@@ -174,6 +195,13 @@ func (d *AssetServer) processIndexHTML(indexHTML []byte) ([]byte, error) {
return nil, err
}
// Inject plugins
for scriptName := range d.pluginScripts {
if err := insertScriptInHead(htmlNode, scriptName); err != nil {
return nil, err
}
}
var buffer bytes.Buffer
err = html.Render(&buffer, htmlNode)
if err != nil {
@@ -0,0 +1,85 @@
//go:build linux
// +build linux
package webview
/*
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 gio-unix-2.0
#include "gtk/gtk.h"
#include "webkit2/webkit2.h"
*/
import "C"
import (
"io"
"net/http"
"unsafe"
)
// NewRequest creates as new WebViewRequest based on a pointer to an `WebKitURISchemeRequest`
//
// Please make sure to call Release() when finished using the request.
func NewRequest(webKitURISchemeRequest unsafe.Pointer) Request {
webkitReq := (*C.WebKitURISchemeRequest)(webKitURISchemeRequest)
req := &request{req: webkitReq}
req.AddRef()
return req
}
var _ Request = &request{}
type request struct {
req *C.WebKitURISchemeRequest
header http.Header
body io.ReadCloser
rw *responseWriter
}
func (r *request) AddRef() error {
C.g_object_ref(C.gpointer(r.req))
return nil
}
func (r *request) Release() error {
C.g_object_unref(C.gpointer(r.req))
return nil
}
func (r *request) URL() (string, error) {
return C.GoString(C.webkit_uri_scheme_request_get_uri(r.req)), nil
}
func (r *request) Method() (string, error) {
return webkit_uri_scheme_request_get_http_method(r.req), nil
}
func (r *request) Header() (http.Header, error) {
if r.header != nil {
return r.header, nil
}
r.header = webkit_uri_scheme_request_get_http_headers(r.req)
return r.header, nil
}
func (r *request) Body() (io.ReadCloser, error) {
if r.body != nil {
return r.body, nil
}
// WebKit2GTK has currently no support for request bodies.
r.body = http.NoBody
return r.body, nil
}
func (r *request) Response() ResponseWriter {
if r.rw != nil {
return r.rw
}
r.rw = &responseWriter{req: r.req}
return r.rw
}
@@ -1,9 +1,20 @@
package webview
import (
"errors"
"net/http"
)
const (
HeaderContentLength = "Content-Length"
HeaderContentType = "Content-Type"
)
var (
errRequestStopped = errors.New("request has been stopped")
errResponseFinished = errors.New("response has been finished")
)
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response for the WebView.
type ResponseWriter interface {
@@ -69,16 +69,10 @@ import "C"
import (
"encoding/json"
"errors"
"net/http"
"unsafe"
)
var (
errRequestStopped = errors.New("request has been stopped")
errResponseFinished = errors.New("response has been finished")
)
var _ ResponseWriter = &responseWriter{}
type responseWriter struct {
@@ -1,7 +1,7 @@
//go:build linux
// +build linux
package linux
package webview
/*
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 gio-unix-2.0
@@ -20,28 +20,31 @@ import (
"strconv"
"syscall"
"unsafe"
"github.com/wailsapp/wails/v2/pkg/assetserver"
)
type webKitResponseWriter struct {
type responseWriter struct {
req *C.WebKitURISchemeRequest
header http.Header
wroteHeader bool
finished bool
w io.WriteCloser
wErr error
}
func (rw *webKitResponseWriter) Header() http.Header {
func (rw *responseWriter) Header() http.Header {
if rw.header == nil {
rw.header = http.Header{}
}
return rw.header
}
func (rw *webKitResponseWriter) Write(buf []byte) (int, error) {
func (rw *responseWriter) Write(buf []byte) (int, error) {
if rw.finished {
return 0, errResponseFinished
}
rw.WriteHeader(http.StatusOK)
if rw.wErr != nil {
return 0, rw.wErr
@@ -49,14 +52,14 @@ func (rw *webKitResponseWriter) Write(buf []byte) (int, error) {
return rw.w.Write(buf)
}
func (rw *webKitResponseWriter) WriteHeader(code int) {
if rw.wroteHeader {
func (rw *responseWriter) WriteHeader(code int) {
if rw.wroteHeader || rw.finished {
return
}
rw.wroteHeader = true
contentLength := int64(-1)
if sLen := rw.Header().Get(assetserver.HeaderContentLength); sLen != "" {
if sLen := rw.Header().Get(HeaderContentLength); sLen != "" {
if pLen, _ := strconv.ParseInt(sLen, 10, 64); pLen > 0 {
contentLength = pLen
}
@@ -72,7 +75,7 @@ func (rw *webKitResponseWriter) WriteHeader(code int) {
}
rw.w = w
stream := C.g_unix_input_stream_new(C.int(rFD), gtkBool(true))
stream := C.g_unix_input_stream_new(C.int(rFD), C.gboolean(1))
defer C.g_object_unref(C.gpointer(stream))
if err := webkit_uri_scheme_request_finish(rw.req, code, rw.Header(), stream, contentLength); err != nil {
@@ -81,13 +84,22 @@ func (rw *webKitResponseWriter) WriteHeader(code int) {
}
}
func (rw *webKitResponseWriter) Close() {
func (rw *responseWriter) Finish() error {
if !rw.wroteHeader {
rw.WriteHeader(http.StatusNotImplemented)
}
if rw.finished {
return nil
}
rw.finished = true
if rw.w != nil {
rw.w.Close()
}
return nil
}
func (rw *webKitResponseWriter) finishWithError(code int, err error) {
func (rw *responseWriter) finishWithError(code int, err error) {
if rw.w != nil {
rw.w.Close()
rw.w = &nopCloser{io.Discard}
@@ -1,6 +1,6 @@
//go:build linux && webkit2_36
package linux
package webview
/*
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 libsoup-2.4
@@ -15,11 +15,9 @@ import (
"net/http"
"strings"
"unsafe"
"github.com/wailsapp/wails/v2/pkg/assetserver"
)
const webkit2MinMinorVersion = 36
const Webkit2MinMinorVersion = 36
func webkit_uri_scheme_request_get_http_method(req *C.WebKitURISchemeRequest) string {
method := C.GoString(C.webkit_uri_scheme_request_get_http_method(req))
@@ -51,7 +49,7 @@ func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, h
C.webkit_uri_scheme_response_set_status(resp, C.guint(code), cReason)
C.free(unsafe.Pointer(cReason))
cMimeType := C.CString(header.Get(assetserver.HeaderContentType))
cMimeType := C.CString(header.Get(HeaderContentType))
C.webkit_uri_scheme_response_set_content_type(resp, cMimeType)
C.free(unsafe.Pointer(cMimeType))
@@ -1,6 +1,6 @@
//go:build linux && !webkit2_36
package linux
package webview
/*
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
@@ -14,11 +14,9 @@ import (
"fmt"
"net/http"
"unsafe"
"github.com/wailsapp/wails/v2/pkg/assetserver"
)
const webkit2MinMinorVersion = 0
const Webkit2MinMinorVersion = 0
func webkit_uri_scheme_request_get_http_method(_ *C.WebKitURISchemeRequest) string {
return http.MethodGet
@@ -33,7 +31,7 @@ func webkit_uri_scheme_request_finish(req *C.WebKitURISchemeRequest, code int, h
return fmt.Errorf("StatusCodes not supported: %d - %s", code, http.StatusText(code))
}
cMimeType := C.CString(header.Get(assetserver.HeaderContentType))
cMimeType := C.CString(header.Get(HeaderContentType))
C.webkit_uri_scheme_request_finish(req, stream, C.gint64(streamLength), cMimeType)
C.free(unsafe.Pointer(cMimeType))
return nil
+6 -1
View File
@@ -41,4 +41,9 @@ Informal and incomplete list of things needed in v3.
## Runtime
- [ ] To log or not to log?
- [ ] Unify cross-platform events, eg. `onClose`
- [ ] Unify cross-platform events, eg. `onClose`
## Plugins
- [ ] Move logins to `v3/plugins`
- [ ] Expose application logger to plugins

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