init tiramisu
This commit is contained in:
commit
967b77c51c
10 changed files with 253 additions and 0 deletions
52
.air.toml
Normal file
52
.air.toml
Normal file
|
@ -0,0 +1,52 @@
|
|||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "tmp\\main.exe"
|
||||
cmd = "go build -o ./tmp/main.exe ./example"
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "runtime/out", "node_modules", "dist"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go", "out"]
|
||||
exclude_unchanged = true
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html", "js", "ts"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = ["bunx tsc -p ./runtime"]
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
silent = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
tmp/
|
||||
out/
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# 🍥 tiramisu
|
||||
> ` is tiramisu a cake or a pie?`
|
||||
|
||||
Build modern, cross-platform desktop apps in HTML + Go from one codebase.
|
49
example/main.go
Normal file
49
example/main.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
t "github.com/grngxd/tiramisu"
|
||||
webview "github.com/webview/webview_go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := t.New(t.TiramisuOptions{
|
||||
Debug: true,
|
||||
Width: 1200,
|
||||
Height: 800,
|
||||
Title: "Tiramisu Example",
|
||||
Hints: webview.HintFixed,
|
||||
})
|
||||
|
||||
app.Run(func() {
|
||||
app.Bind("hello", func(args ...any) (any, error) {
|
||||
if len(args) == 0 {
|
||||
return "Hello, World!", nil
|
||||
}
|
||||
if len(args) == 1 {
|
||||
return fmt.Sprintf("Hello, %s!", args[0]), nil
|
||||
}
|
||||
return "Hello, unknown!", nil
|
||||
})
|
||||
|
||||
app.HTML(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Tiramisu Example</h1>
|
||||
<p>Click the button to see a greeting:</p>
|
||||
<button onclick="greet()">Greet</button>
|
||||
|
||||
<script>
|
||||
function greet() {
|
||||
tiramisu.invoke("hello", "world")
|
||||
.then(response => alert(response))
|
||||
.catch(console.error)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
})
|
||||
}
|
5
go.mod
Normal file
5
go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module github.com/grngxd/tiramisu
|
||||
|
||||
go 1.23.4
|
||||
|
||||
require github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 // indirect
|
2
go.sum
Normal file
2
go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6 h1:VQpB2SpK88C6B5lPHTuSZKb2Qee1QWwiFlC5CKY4AW0=
|
||||
github.com/webview/webview_go v0.0.0-20240831120633-6173450d4dd6/go.mod h1:yE65LFCeWf4kyWD5re+h4XNvOHJEXOCOuJZ4v8l5sgk=
|
11
runtime/index.d.ts
vendored
Normal file
11
runtime/index.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
invoke: (name: string, ...args: any[]) => Promise<any>;
|
||||
tiramisu: {
|
||||
invoke: (name: string, ...args: any[]) => Promise<any>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export { };
|
||||
|
5
runtime/preload.ts
Normal file
5
runtime/preload.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const tiramisu = {
|
||||
invoke: window.invoke
|
||||
}
|
||||
|
||||
window.tiramisu = tiramisu
|
12
runtime/tsconfig.json
Normal file
12
runtime/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": false,
|
||||
"declaration": true,
|
||||
"outDir": "./out",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
}
|
111
tiramisu.go
Normal file
111
tiramisu.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package tiramisu
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
wv "github.com/webview/webview_go"
|
||||
)
|
||||
|
||||
type TiramisuOptions struct {
|
||||
Debug bool
|
||||
Width int
|
||||
Height int
|
||||
Title string
|
||||
Hints wv.Hint
|
||||
}
|
||||
|
||||
type FuncHandler func(args ...any) (any, error)
|
||||
|
||||
type Tiramisu struct {
|
||||
w wv.WebView
|
||||
funcs map[string]FuncHandler
|
||||
}
|
||||
|
||||
func New(o TiramisuOptions) *Tiramisu {
|
||||
w := wv.New(o.Debug)
|
||||
t := &Tiramisu{
|
||||
w: w,
|
||||
funcs: make(map[string]FuncHandler),
|
||||
}
|
||||
|
||||
w.SetSize(o.Width, o.Height, o.Hints)
|
||||
w.SetTitle(o.Title)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *Tiramisu) Run(fn func()) {
|
||||
defer t.w.Destroy()
|
||||
t.w.Dispatch(func() {
|
||||
t.injectJS()
|
||||
|
||||
if fn != nil {
|
||||
fn()
|
||||
}
|
||||
})
|
||||
t.w.Run()
|
||||
}
|
||||
|
||||
func (t *Tiramisu) bind(name string, fn FuncHandler) {
|
||||
t.w.Bind(name, fn)
|
||||
}
|
||||
|
||||
func (t *Tiramisu) Bind(name string, fn FuncHandler) {
|
||||
if _, exists := t.funcs[name]; exists {
|
||||
panic(fmt.Sprintf("function %s is already bound", name))
|
||||
}
|
||||
|
||||
t.funcs[name] = fn
|
||||
t.bind(name, func(args ...any) (any, error) {
|
||||
return t.invoke(name, args...)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Tiramisu) invoke(name string, args ...any) (any, error) {
|
||||
fn, ok := t.funcs[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("function %s not found", name)
|
||||
}
|
||||
result, err := fn(args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error invoking function %s: %w", name, err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (t *Tiramisu) Eval(js string) {
|
||||
t.w.Eval(js)
|
||||
}
|
||||
|
||||
func (t *Tiramisu) Evalf(js string, args ...any) {
|
||||
fmt := fmt.Sprintf(js, args...)
|
||||
t.w.Eval(fmt)
|
||||
}
|
||||
|
||||
func (t *Tiramisu) HTML(html string) {
|
||||
t.w.SetHtml(html)
|
||||
t.injectJS()
|
||||
}
|
||||
|
||||
//go:embed runtime/out/*
|
||||
var runtimeFS embed.FS
|
||||
|
||||
func (t *Tiramisu) injectJS() {
|
||||
js, err := runtimeFS.ReadFile("runtime/out/preload.js")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to read preload.js: %v", err))
|
||||
}
|
||||
|
||||
t.w.Eval(string(js))
|
||||
t.bind("invoke", func(args ...any) (any, error) {
|
||||
name, ok := args[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("first argument must be a string, got %T", args[0])
|
||||
}
|
||||
if len(args) == 1 {
|
||||
return t.invoke(name)
|
||||
}
|
||||
return t.invoke(name, args[1:]...)
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue