diff --git a/v3/examples/notifications/frontend/index.html b/v3/examples/notifications/frontend/index.html
new file mode 100644
index 000000000..88daa5e2f
--- /dev/null
+++ b/v3/examples/notifications/frontend/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+ Wails App
+
+
+
+
+
Wails + Javascript
+
+
+
+
+
diff --git a/v3/examples/notifications/frontend/main.js b/v3/examples/notifications/frontend/main.js
new file mode 100644
index 000000000..18dd0214c
--- /dev/null
+++ b/v3/examples/notifications/frontend/main.js
@@ -0,0 +1,67 @@
+import { GreetService } from "./bindings/notifications";
+import { NotificationService } from "./bindings/github.com/wailsapp/wails/v3/pkg/services/notification";
+import { Events } from "@wailsio/runtime";
+
+const resultElement = document.getElementById('result');
+const timeElement = document.getElementById('time');
+const notificationsElement = document.getElementById('notifications');
+
+const nofitications = new Map();
+
+window.doGreet = () => {
+ let name = document.getElementById('name').value;
+ if (!name) {
+ name = 'anonymous';
+ }
+ GreetService.Greet(name).then((result) => {
+ resultElement.innerText = result;
+ }).catch((err) => {
+ console.log(err);
+ });
+}
+
+window.sendNotification = async () => {
+ const granted = await NotificationService.CheckNotificationAuthorization();
+ if (granted) {
+ await NotificationService.SendNotification("some-uuid-fronted", "Frontend Notificaiton", "", "Notificaiton sent through JS!");
+ }
+}
+
+window.sendComplexNotification = async () => {
+ const granted = await NotificationService.CheckNotificationAuthorization();
+ if (granted) {
+ await NotificationService.RegisterNotificationCategory({
+ id: "FRONTEND_NOTIF",
+ actions: [
+ { id: "VIEW_ACTION", title: "View" },
+ { id: "MARK_READ_ACTION", title: "Mark as Read" },
+ { id: "DELETE_ACTION", title: "Delete", destructive: true },
+ ],
+ hasReplyField: true,
+ replyButtonTitle: "Reply",
+ replyPlaceholder: "Reply to frontend...",
+ });
+
+ await NotificationService.SendNotificationWithActions({
+ id: "some-uuid-complex",
+ title: "Complex Frontend Notification",
+ subtitle: "From: Jane Doe",
+ body: "Is it rainging today where you are?",
+ categoryId: "FRONTEND_NOTIF",
+ data: {
+ messageId: "msg-456",
+ senderId: "user-456",
+ timestamp: Date.now(),
+ }
+ });
+ }
+}
+
+Events.On('time', (time) => {
+ timeElement.innerText = time.data;
+});
+
+Events.On("notification", (data) => {
+ nofitications.set(data.identifier, data);
+ notificationsElement.innerText = Array.from(nofitications.values()).map(notification => JSON.stringify(notification)).join(", ");
+});
\ No newline at end of file
diff --git a/v3/examples/notifications/frontend/package.json b/v3/examples/notifications/frontend/package.json
new file mode 100644
index 000000000..9ae87549e
--- /dev/null
+++ b/v3/examples/notifications/frontend/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build:dev": "vite build --minify false --mode development",
+ "build": "vite build --mode production",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "vite": "^5.0.0",
+ "@wailsio/runtime": "latest"
+ }
+}
\ No newline at end of file
diff --git a/v3/examples/notifications/frontend/public/Inter-Medium.ttf b/v3/examples/notifications/frontend/public/Inter-Medium.ttf
new file mode 100644
index 000000000..a01f3777a
Binary files /dev/null and b/v3/examples/notifications/frontend/public/Inter-Medium.ttf differ
diff --git a/v3/examples/notifications/frontend/public/javascript.svg b/v3/examples/notifications/frontend/public/javascript.svg
new file mode 100644
index 000000000..f9abb2b72
--- /dev/null
+++ b/v3/examples/notifications/frontend/public/javascript.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/v3/examples/notifications/frontend/public/style.css b/v3/examples/notifications/frontend/public/style.css
new file mode 100644
index 000000000..127799eda
--- /dev/null
+++ b/v3/examples/notifications/frontend/public/style.css
@@ -0,0 +1,160 @@
+:root {
+ font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
+ "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ sans-serif;
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 400;
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: rgba(27, 38, 54, 1);
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+* {
+ user-select: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+}
+
+@font-face {
+ font-family: "Inter";
+ font-style: normal;
+ font-weight: 400;
+ src: local(""),
+ url("./Inter-Medium.ttf") format("truetype");
+}
+
+h3 {
+ font-size: 3em;
+ line-height: 1.1;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+
+a:hover {
+ color: #535bf2;
+}
+
+button {
+ /* width: 60px; */
+ height: 30px;
+ line-height: 30px;
+ border-radius: 3px;
+ border: none;
+ margin: 0 0 0 20px;
+ padding: 0 8px;
+ cursor: pointer;
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ place-content: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+}
+
+.logo:hover {
+ filter: drop-shadow(0 0 2em #e80000aa);
+}
+
+.logo.vanilla:hover {
+ filter: drop-shadow(0 0 2em #f7df1eaa);
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+ margin: 1.5rem auto;
+ text-align: center;
+}
+
+.footer {
+ margin-top: 1rem;
+ align-content: center;
+ text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+
+ a:hover {
+ color: #747bff;
+ }
+
+ button {
+ background-color: #f9f9f9;
+ }
+}
+
+
+.input-box .btn:hover {
+ background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
+ color: #333333;
+}
+
+.input-box .input {
+ border: none;
+ border-radius: 3px;
+ outline: none;
+ height: 30px;
+ line-height: 30px;
+ padding: 0 10px;
+ color: black;
+ background-color: rgba(240, 240, 240, 1);
+ -webkit-font-smoothing: antialiased;
+}
+
+.input-box .input:hover {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
+
+.input-box .input:focus {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
\ No newline at end of file
diff --git a/v3/examples/notifications/frontend/public/wails.png b/v3/examples/notifications/frontend/public/wails.png
new file mode 100644
index 000000000..8bdf42483
Binary files /dev/null and b/v3/examples/notifications/frontend/public/wails.png differ
diff --git a/v3/examples/notifications/main.go b/v3/examples/notifications/main.go
index 2bf9735fd..35fb7b06b 100644
--- a/v3/examples/notifications/main.go
+++ b/v3/examples/notifications/main.go
@@ -8,6 +8,7 @@ import (
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
+ "github.com/wailsapp/wails/v3/pkg/services/notification"
)
func main() {
@@ -15,6 +16,9 @@ func main() {
Name: "Notifications Demo",
Description: "A test of macOS notifications",
Assets: application.AlphaAssets,
+ Services: []application.Service{
+ application.NewService(¬ification.NotificationService{}),
+ },
})
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{