mirror of
https://github.com/cf-sonr/motr.git
synced 2026-01-12 02:59:13 +00:00
feat: streamline front-end dependencies and build process
This commit is contained in:
85
Makefile
85
Makefile
@@ -1,60 +1,55 @@
|
|||||||
export ROOT_DIR := $(shell git rev-parse --show-toplevel)
|
export ROOT_DIR := $(shell git rev-parse --show-toplevel)
|
||||||
|
|
||||||
|
export ENCLAVE_ROOT := $(ROOT_DIR)/cmd/enclave
|
||||||
export RADAR_ROOT := $(ROOT_DIR)/cmd/radar
|
export RADAR_ROOT := $(ROOT_DIR)/cmd/radar
|
||||||
|
export SIGNER_ROOT := $(ROOT_DIR)/cmd/signer
|
||||||
export WORKER_ROOT := $(ROOT_DIR)/cmd/worker
|
export WORKER_ROOT := $(ROOT_DIR)/cmd/worker
|
||||||
|
export VERIFIER_ROOT := $(ROOT_DIR)/cmd/verifier
|
||||||
|
|
||||||
export SQLC_ROOT := $(ROOT_DIR)/internal/db
|
export SQLC_ROOT := $(ROOT_DIR)/internal/db
|
||||||
export MIGRATE_ROOT := $(ROOT_DIR)/internal/migrations
|
export MIGRATE_ROOT := $(ROOT_DIR)/internal/migrations
|
||||||
export RADAR_OUT := $(RADAR_ROOT)/build/app.wasm
|
export RADAR_OUT := $(RADAR_ROOT)/build/app.wasm
|
||||||
export WORKER_OUT := $(WORKER_ROOT)/build/app.wasm
|
export WORKER_OUT := $(WORKER_ROOT)/build/app.wasm
|
||||||
|
|
||||||
.PHONY: install clean help
|
|
||||||
|
|
||||||
install:
|
|
||||||
@go mod download
|
|
||||||
@go install github.com/syumai/workers/cmd/workers-assets-gen@latest
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@rm -rf ./build
|
|
||||||
@rm -rf ./bin
|
|
||||||
@rm -rf ./dist
|
|
||||||
@rm -rf ./cmd/worker/node_modules
|
|
||||||
@rm -rf ./cmd/radar/node_modules
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Usage: make <command>"
|
|
||||||
@echo ""
|
|
||||||
@echo "Commands:"
|
|
||||||
@echo " help Show this help message"
|
|
||||||
@echo " tidy Tidy up the project"
|
|
||||||
@echo " templ Generate templates"
|
|
||||||
@echo " sqlc Generate SQL schema"
|
|
||||||
@echo " worker Build and deploy worker"
|
|
||||||
@echo " radar Build and deploy radar"
|
|
||||||
|
|
||||||
.PHONY: templ sqlc
|
|
||||||
|
|
||||||
templ:
|
|
||||||
@templ generate
|
|
||||||
|
|
||||||
sqlc:
|
|
||||||
@devbox run gen:sqlc
|
|
||||||
|
|
||||||
migrate:
|
migrate:
|
||||||
@cd $(MIGRATE_ROOT) && task
|
@cd $(MIGRATE_ROOT) && task
|
||||||
|
|
||||||
worker:
|
.PHONY: worker worker-build worker-deploy worker-start
|
||||||
@devbox run serve:worker
|
worker: worker-deploy
|
||||||
|
worker-build:
|
||||||
|
@make -C $(WORKER_ROOT) build
|
||||||
|
worker-deploy:
|
||||||
|
@make -C $(WORKER_ROOT) deploy
|
||||||
|
worker-start:
|
||||||
|
@make -C $(WORKER_ROOT) start
|
||||||
|
|
||||||
radar:
|
.PHONY: radar radar-build radar-deploy radar-start
|
||||||
@devbox run serve:radar
|
radar: radar-deploy
|
||||||
|
radar-build:
|
||||||
|
@make -C $(RADAR_ROOT) build
|
||||||
|
radar-deploy:
|
||||||
|
@make -C $(RADAR_ROOT) deploy
|
||||||
|
radar-start:
|
||||||
|
@make -C $(RADAR_ROOT) start
|
||||||
|
|
||||||
|
.PHONY: enclave enclave-build enclave-publish
|
||||||
|
enclave: enclave-publish
|
||||||
|
enclave-build:
|
||||||
|
@make -C $(ENCLAVE_ROOT) build
|
||||||
|
enclave-publish:
|
||||||
|
@make -C $(ENCLAVE_ROOT) publish
|
||||||
|
|
||||||
deploy:
|
.PHONY: signer signer-build signer-publish
|
||||||
@devbox run deploy
|
signer: signer-publish
|
||||||
|
signer-build:
|
||||||
|
@make -C $(SIGNER_ROOT) build
|
||||||
|
signer-publish:
|
||||||
|
@make -C $(SIGNER_ROOT) publish
|
||||||
|
|
||||||
release:
|
|
||||||
@devbox run release
|
|
||||||
|
|
||||||
templ-watch:
|
.PHONY: verifier verifier-build verifier-publish
|
||||||
@devbox run watch:templ
|
verifier: verifier-publish
|
||||||
|
verifier-build:
|
||||||
migrate:
|
@make -C $(VERIFIER_ROOT) build
|
||||||
@devbox run db:migrate
|
verifier-publish:
|
||||||
|
@make -C $(VERIFIER_ROOT) publish
|
||||||
|
|||||||
1
cmd/enclave/.gitignore
vendored
Normal file
1
cmd/enclave/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.wasm
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
all: build publish
|
all: build publish
|
||||||
|
|
||||||
build: tidy
|
build:
|
||||||
@gum spin --show-error --title "[ENCLAVE] Running go build..." -- sh -c "GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o enclave.wasm"
|
@gum spin --show-error --title "[ENCLAVE] Running go build..." -- sh -c "GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o enclave.wasm"
|
||||||
@gum log --level info --time kitchen "[ENCLAVE] Completed go build successfully."
|
@gum log --level info --time kitchen "[ENCLAVE] Completed go build successfully."
|
||||||
|
|
||||||
|
|||||||
37
cmd/enclave/README.md
Normal file
37
cmd/enclave/README.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# `enclave`
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The Motr Enclave is used for DKG-MPC Generation of Decentralized Web Nodes.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Call Plugin from JavaScript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/sonr-io/motr.git
|
||||||
|
cd motr
|
||||||
|
go mod tidy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Call Plugin from Extism CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/sonr-io/motr.git
|
||||||
|
cd motr
|
||||||
|
go mod tidy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### `generate()`
|
||||||
|
|
||||||
|
The Generate function initializes an MPC Vault
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
Copyright (c) 2025, Sonr Inc.
|
||||||
@@ -2,11 +2,7 @@
|
|||||||
|
|
||||||
all: build publish
|
all: build publish
|
||||||
|
|
||||||
tidy:
|
build:
|
||||||
@gum spin --show-error --title "[SIGNER] Running go mod tidy..." -- sh -c "go mod tidy"
|
|
||||||
@gum log --level info --time kitchen "[SIGNER] Completed go mod tidy successfully."
|
|
||||||
|
|
||||||
build: tidy
|
|
||||||
@gum spin --show-error --title "[SIGNER] Running tinygo build..." -- sh -c "tinygo build -o signer.wasm -target wasip1 -buildmode=c-shared main.go"
|
@gum spin --show-error --title "[SIGNER] Running tinygo build..." -- sh -c "tinygo build -o signer.wasm -target wasip1 -buildmode=c-shared main.go"
|
||||||
@gum log --level info --time kitchen "[SIGNER] Completed tinygo build successfully."
|
@gum log --level info --time kitchen "[SIGNER] Completed tinygo build successfully."
|
||||||
|
|
||||||
|
|||||||
1
cmd/verifier/.gitignore
vendored
Normal file
1
cmd/verifier/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.wasm
|
||||||
@@ -2,11 +2,7 @@
|
|||||||
|
|
||||||
all: build publish
|
all: build publish
|
||||||
|
|
||||||
tidy:
|
build:
|
||||||
@gum spin --show-error --title "[VERIFIER] Running go mod tidy..." -- sh -c "go mod tidy"
|
|
||||||
@gum log --level info --time kitchen "[VERIFIER] Completed go mod tidy successfully."
|
|
||||||
|
|
||||||
build: tidy
|
|
||||||
@gum spin --show-error --title "[VERIFIER] Running tinygo build..." -- sh -c "tinygo build -o verifier.wasm -target wasip1 -buildmode=c-shared main.go"
|
@gum spin --show-error --title "[VERIFIER] Running tinygo build..." -- sh -c "tinygo build -o verifier.wasm -target wasip1 -buildmode=c-shared main.go"
|
||||||
@gum log --level info --time kitchen "[VERIFIER] Completed tinygo build successfully."
|
@gum log --level info --time kitchen "[VERIFIER] Completed tinygo build successfully."
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
templ:
|
generate:
|
||||||
go run github.com/a-h/templ/cmd/templ generate
|
@go run github.com/a-h/templ/cmd/templ generate
|
||||||
|
@go run github.com/syumai/workers/cmd/workers-assets-gen -mode=go
|
||||||
assets:
|
|
||||||
go run github.com/syumai/workers/cmd/workers-assets-gen -mode=go
|
|
||||||
|
|
||||||
build: generate
|
build: generate
|
||||||
GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o ./build/app.wasm .
|
GOOS=js GOARCH=wasm go build -ldflags="-s -w" -o ./build/app.wasm .
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
"sqlc@latest",
|
"sqlc@latest",
|
||||||
"nodejs@latest",
|
"nodejs@latest",
|
||||||
"goreleaser@latest",
|
"goreleaser@latest",
|
||||||
"go-task@latest",
|
"rclone@latest",
|
||||||
"doppler@latest"
|
"go-task@latest"
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"SONR_API_URL": "https://api.sonr.ws",
|
"SONR_API_URL": "https://api.sonr.ws",
|
||||||
|
|||||||
113
pkg/render/ui/layout.templ
Normal file
113
pkg/render/ui/layout.templ
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
// Body is a component that renders the body tag
|
||||||
|
templ Body() {
|
||||||
|
<body>
|
||||||
|
{ children... }
|
||||||
|
</body>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head is a component that renders the head of the document
|
||||||
|
templ Head() {
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
@ApexCharts()
|
||||||
|
@Helia()
|
||||||
|
@Dexie()
|
||||||
|
@Htmx()
|
||||||
|
@Tailwind()
|
||||||
|
@Shoelace()
|
||||||
|
@DefaultStyles()
|
||||||
|
{ children... }
|
||||||
|
</head>
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML is a component that renders the html tag
|
||||||
|
templ HTML() {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
{ children... }
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Columns is a component that renders a responsive flex container that stacks on mobile
|
||||||
|
templ Columns() {
|
||||||
|
<div class="flex flex-col h-full w-full gap-4 md:gap-6 md:flex-row md:flex-wrap">
|
||||||
|
{ children... }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container is a component that renders a full screen container
|
||||||
|
templ Container() {
|
||||||
|
<div id="container" class="flex fixed inset-0 z-[99] w-screen min-h-screen">
|
||||||
|
<div class="relative flex flex-wrap items-center w-full min-h-full px-4 py-6 sm:px-6 md:px-8">
|
||||||
|
<div class="relative w-full max-w-screen-lg mx-auto">
|
||||||
|
<div class="flex flex-col items-center justify-center min-h-full gap-4">
|
||||||
|
{ children... }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tailwind css dependencies
|
||||||
|
templ Tailwind() {
|
||||||
|
@tailwindHandle.Once() {
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nav is a component that renders the navigation bar
|
||||||
|
templ Nav() {
|
||||||
|
<nav class="absolute inset-x-0 top-0 z-[100] flex h-16 w-full items-center justify-between px-4 py-4 sm:px-6 md:px-8">
|
||||||
|
{ children... }
|
||||||
|
</nav>
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavCTA is a component that renders a call to action button
|
||||||
|
templ NavCTA(href string, text string) {
|
||||||
|
<sl-button type="primary" href={ href }>{ text }</sl-button>
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavItem is a component that renders a navigation item
|
||||||
|
templ NavItem(href string, text string) {
|
||||||
|
<sl-button type="text" href={ href }>{ text }</sl-button>
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavLogo is a component that renders a logo
|
||||||
|
templ NavLogo(title string) {
|
||||||
|
<a href="/" class="flex items-center justify-center gap-1.5 px-2 py-2">
|
||||||
|
{ children... }
|
||||||
|
<span class="text-xl font-bold pb-1.5">{ title }</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
|
||||||
|
// NavLeft is a component that renders the left side of the navigation bar
|
||||||
|
templ NavLeft() {
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
{ children... }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ NavRight() {
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
{ children... }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rows is a component that renders a responsive flex container that wraps on mobile
|
||||||
|
templ Rows() {
|
||||||
|
<div class="flex flex-col w-full gap-3 sm:flex-row sm:flex-wrap sm:gap-4">
|
||||||
|
{ children... }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ Separator(text string) {
|
||||||
|
<div class="relative py-6">
|
||||||
|
<div class="absolute inset-0 flex items-center"><span class="w-full border-t"></span></div>
|
||||||
|
<div class="relative flex justify-center text-xs uppercase">
|
||||||
|
<span class="px-2 text-neutral-500">{ text }</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
139
pkg/render/ui/providers.templ
Normal file
139
pkg/render/ui/providers.templ
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var (
|
||||||
|
apexChartsHandle = templ.NewOnceHandle()
|
||||||
|
d3Handle = templ.NewOnceHandle()
|
||||||
|
dexieHandle = templ.NewOnceHandle()
|
||||||
|
heliaHandle = templ.NewOnceHandle()
|
||||||
|
htmxHandle = templ.NewOnceHandle()
|
||||||
|
tailwindHandle = templ.NewOnceHandle()
|
||||||
|
)
|
||||||
|
|
||||||
|
// ApexCharts is a component that renders the ApexCharts.js library
|
||||||
|
templ ApexCharts() {
|
||||||
|
@apexChartsHandle.Once() {
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// d3 is a component that renders the D3.js library
|
||||||
|
templ D3() {
|
||||||
|
@d3Handle.Once() {
|
||||||
|
<script type="module">
|
||||||
|
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dexie is a component that renders the Dexie.js library
|
||||||
|
templ Dexie() {
|
||||||
|
@dexieHandle.Once() {
|
||||||
|
<script src={ jsDelivrURL("dexie", "4.0.10", "dist/dexie.min.js") }></script>
|
||||||
|
<script src={ jsDelivrURL("dexie-export-import", "4.1.4", "dist/dexie-export-import.min.js") }></script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In package deps
|
||||||
|
templ Helia() {
|
||||||
|
@heliaHandle.Once() {
|
||||||
|
<script src="https://unpkg.com/@helia/unixfs/dist/index.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/blockstore-core/dist/index.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/datastore-core/dist/index.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/helia/dist/index.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Time formatting helper
|
||||||
|
function ms2TimeString(a) {
|
||||||
|
const k = a % 1e3
|
||||||
|
const s = a / 1e3 % 60 | 0
|
||||||
|
const m = a / 6e4 % 60 | 0
|
||||||
|
const h = a / 36e5 % 24 | 0
|
||||||
|
|
||||||
|
return (h ? (h < 10 ? '0' + h : h) + ':' : '00:') +
|
||||||
|
(m < 10 ? 0 : '') + m + ':' +
|
||||||
|
(s < 10 ? 0 : '') + s + ':' +
|
||||||
|
(k < 100 ? k < 10 ? '00' : 0 : '') + k
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log management
|
||||||
|
const getLogLineEl = (msg) => {
|
||||||
|
const logLine = document.createElement('span')
|
||||||
|
logLine.innerHTML = `${ms2TimeString(performance.now())} - ${msg}`
|
||||||
|
return logLine
|
||||||
|
}
|
||||||
|
|
||||||
|
const addToLog = (msg) => {
|
||||||
|
const logEl = document.getElementById('runningLog')
|
||||||
|
if (logEl) {
|
||||||
|
logEl.appendChild(getLogLineEl(msg))
|
||||||
|
logEl.appendChild(document.createElement('br'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peer management
|
||||||
|
window.discoveredPeers = new Map()
|
||||||
|
const updateConnectedPeers = () => {
|
||||||
|
if (!window.helia || !window.helia.libp2p) return
|
||||||
|
|
||||||
|
const peers = window.helia.libp2p.getPeers()
|
||||||
|
const connectedPeerCountEl = document.getElementById('connectedPeerCount')
|
||||||
|
const connectedPeersListEl = document.getElementById('connectedPeersList')
|
||||||
|
|
||||||
|
if (connectedPeerCountEl) {
|
||||||
|
connectedPeerCountEl.innerHTML = peers.length
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectedPeersListEl) {
|
||||||
|
connectedPeersListEl.innerHTML = ''
|
||||||
|
|
||||||
|
for (const peer of peers) {
|
||||||
|
const peerEl = document.createElement('li')
|
||||||
|
peerEl.innerText = peer.toString()
|
||||||
|
connectedPeersListEl.appendChild(peerEl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updateDiscoveredPeers = () => {
|
||||||
|
const discoveredPeerCountEl = document.getElementById('discoveredPeerCount')
|
||||||
|
if (discoveredPeerCountEl) {
|
||||||
|
discoveredPeerCountEl.innerHTML = window.discoveredPeers.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Helia node instantiation
|
||||||
|
let heliaInstance = null
|
||||||
|
window.instantiateHeliaNode = async () => {
|
||||||
|
// application-specific data lives in the datastore
|
||||||
|
const datastore = new DatastoreCore.MemoryDatastore()
|
||||||
|
const blockstore = new BlockstoreCore.MemoryBlockstore()
|
||||||
|
|
||||||
|
if (heliaInstance != null) {
|
||||||
|
return heliaInstance
|
||||||
|
}
|
||||||
|
heliaInstance = await Helia.createHelia({
|
||||||
|
datastore,
|
||||||
|
blockstore
|
||||||
|
})
|
||||||
|
addToLog('Created Helia instance')
|
||||||
|
return heliaInstance
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Htmx is a component that renders the Htmx.js library
|
||||||
|
templ Htmx() {
|
||||||
|
@htmxHandle.Once() {
|
||||||
|
<script src={ jsDelivrURL("htmx.org", "1.9.12", "dist/htmx.min.js") }></script>
|
||||||
|
<script src={ jsDelivrURL("htmx-ext-include-vals", "2.0.0", "include-vals.min.js") }></script>
|
||||||
|
<script src={ jsDelivrURL("htmx-ext-path-params", "2.0.0", "path-params.min.js") }></script>
|
||||||
|
<script src={ jsDelivrURL("htmx-ext-alpine-morph", "2.0.0", "alpine-morph.min.js") }></script>
|
||||||
|
<script src={ jsDelivrURL("htmx-ext-sse", "2.2.2", "sse.min.js") }></script>
|
||||||
|
<script src={ jsDelivrURL("htmx-ext-ws", "2.0.2", "ws.min.js") }></script>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsDelivrURL returns the URL of a package on jsDelivr
|
||||||
|
func jsDelivrURL(pkg string, version string, path string) string {
|
||||||
|
return fmt.Sprintf("https://cdn.jsdelivr.net/npm/%s/%s/%s", pkg, version, path)
|
||||||
|
}
|
||||||
54
pkg/render/ui/styles.templ
Normal file
54
pkg/render/ui/styles.templ
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
templ DefaultStyles() {
|
||||||
|
<style>
|
||||||
|
@theme {
|
||||||
|
--color-primary: #17c2ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-from-right {
|
||||||
|
from { transform: translateX(90px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-to-left {
|
||||||
|
to { transform: translateX(-90px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-it {
|
||||||
|
view-transition-name: slide-it;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-old(slide-it) {
|
||||||
|
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
|
||||||
|
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
|
||||||
|
}
|
||||||
|
::view-transition-new(slide-it) {
|
||||||
|
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
|
||||||
|
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shoelace dependencies
|
||||||
|
templ Shoelace() {
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
media="(prefers-color-scheme:light)"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/themes/light.css"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
media="(prefers-color-scheme:dark)"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/themes/dark.css"
|
||||||
|
onload="document.documentElement.classList.add('sl-theme-dark');"
|
||||||
|
/>
|
||||||
|
<script type="module" src="https://cdn.jsdelivr.net/npm/sonr-shoelace/cdn/shoelace-autoloader.js"></script>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user