create-eventus-app 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/package.json +0 -2
- package/template/src/App.tsx +29 -112
- package/template/src/styles.css +6 -73
- package/template/src/dev-preview.ts +0 -102
package/package.json
CHANGED
package/template/package.json
CHANGED
|
@@ -10,12 +10,10 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@eventusgo/sdk": "__EVENTUS_SDK_VERSION__",
|
|
13
|
-
"qrcode": "1.5.4",
|
|
14
13
|
"react": "18.3.1",
|
|
15
14
|
"react-dom": "18.3.1"
|
|
16
15
|
},
|
|
17
16
|
"devDependencies": {
|
|
18
|
-
"@types/qrcode": "1.5.5",
|
|
19
17
|
"@eventusgo/vite-plugin": "__EVENTUS_VITE_PLUGIN_VERSION__",
|
|
20
18
|
"@types/react": "18.3.28",
|
|
21
19
|
"@types/react-dom": "18.3.7",
|
package/template/src/App.tsx
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { useEffect, useState } from "react";
|
|
2
2
|
import manifest from "virtual:eventus-manifest";
|
|
3
|
-
import QRCode from "qrcode";
|
|
4
3
|
import {
|
|
5
4
|
eventus,
|
|
5
|
+
getLivePreviewHtml,
|
|
6
|
+
isBridgeUnavailableError,
|
|
6
7
|
type EventusContext,
|
|
7
8
|
type EventusRuntimeSource,
|
|
8
9
|
} from "@eventusgo/sdk";
|
|
9
|
-
import {
|
|
10
|
-
fetchDevPreviewDescriptor,
|
|
11
|
-
isBridgeUnavailableError,
|
|
12
|
-
type EventusDevPreviewDescriptor,
|
|
13
|
-
} from "./dev-preview";
|
|
14
10
|
|
|
15
11
|
type LoadState =
|
|
16
12
|
| { status: "idle" | "loading" }
|
|
17
13
|
| { status: "ready"; context: EventusContext }
|
|
18
14
|
| {
|
|
19
15
|
status: "bridge-unavailable";
|
|
20
|
-
|
|
16
|
+
previewHtml: string | null;
|
|
21
17
|
}
|
|
22
18
|
| { status: "error"; message: string };
|
|
23
19
|
|
|
@@ -60,12 +56,12 @@ export default function App() {
|
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
if (isBridgeUnavailableError(error)) {
|
|
63
|
-
const
|
|
59
|
+
const previewHtml = await getLivePreviewHtml();
|
|
64
60
|
if (!cancelled && !receivedReadyEvent) {
|
|
65
61
|
setRuntimeSource("bridge-unavailable");
|
|
66
62
|
setState({
|
|
67
63
|
status: "bridge-unavailable",
|
|
68
|
-
|
|
64
|
+
previewHtml,
|
|
69
65
|
});
|
|
70
66
|
}
|
|
71
67
|
return;
|
|
@@ -97,7 +93,7 @@ export default function App() {
|
|
|
97
93
|
? "Explicit mock mode active"
|
|
98
94
|
: runtimeSource === "bridge-unavailable"
|
|
99
95
|
? "Open this mini app inside Eventus X"
|
|
100
|
-
|
|
96
|
+
: "Detecting runtime…";
|
|
101
97
|
|
|
102
98
|
return (
|
|
103
99
|
<main className="app-shell">
|
|
@@ -105,7 +101,8 @@ export default function App() {
|
|
|
105
101
|
<p className="eyebrow">Eventus Mini App Toolkit</p>
|
|
106
102
|
<h1>{manifest.name}</h1>
|
|
107
103
|
<p className="subtitle">
|
|
108
|
-
App ID <code>{manifest.appId}</code> · Version
|
|
104
|
+
App ID <code>{manifest.appId}</code> · Version{" "}
|
|
105
|
+
<code>{manifest.version}</code>
|
|
109
106
|
</p>
|
|
110
107
|
|
|
111
108
|
<div className="status-banner">{runtimeLabel}</div>
|
|
@@ -119,7 +116,22 @@ export default function App() {
|
|
|
119
116
|
) : null}
|
|
120
117
|
|
|
121
118
|
{state.status === "bridge-unavailable" ? (
|
|
122
|
-
|
|
119
|
+
state.previewHtml ? (
|
|
120
|
+
<div
|
|
121
|
+
dangerouslySetInnerHTML={{
|
|
122
|
+
__html: state.previewHtml,
|
|
123
|
+
}}
|
|
124
|
+
/>
|
|
125
|
+
) : (
|
|
126
|
+
<div className="handoff-panel">
|
|
127
|
+
<h2>Open inside Eventus X</h2>
|
|
128
|
+
<p>
|
|
129
|
+
This mini app needs the Eventus bridge to access the real
|
|
130
|
+
runtime. Open it from Eventus X to test it with production-like
|
|
131
|
+
behavior.
|
|
132
|
+
</p>
|
|
133
|
+
</div>
|
|
134
|
+
)
|
|
123
135
|
) : null}
|
|
124
136
|
|
|
125
137
|
{state.status === "ready" ? (
|
|
@@ -134,7 +146,11 @@ export default function App() {
|
|
|
134
146
|
</div>
|
|
135
147
|
<div>
|
|
136
148
|
<dt>User</dt>
|
|
137
|
-
<dd>
|
|
149
|
+
<dd>
|
|
150
|
+
{state.context.user?.name ??
|
|
151
|
+
state.context.user?.id ??
|
|
152
|
+
"Anonymous"}
|
|
153
|
+
</dd>
|
|
138
154
|
</div>
|
|
139
155
|
<div>
|
|
140
156
|
<dt>Manifest Permissions</dt>
|
|
@@ -152,102 +168,3 @@ export default function App() {
|
|
|
152
168
|
</main>
|
|
153
169
|
);
|
|
154
170
|
}
|
|
155
|
-
|
|
156
|
-
function DeveloperHandoff({
|
|
157
|
-
preview,
|
|
158
|
-
}: {
|
|
159
|
-
preview: EventusDevPreviewDescriptor | null;
|
|
160
|
-
}) {
|
|
161
|
-
if (!preview) {
|
|
162
|
-
return (
|
|
163
|
-
<div className="handoff-panel">
|
|
164
|
-
<h2>Open inside Eventus X</h2>
|
|
165
|
-
<p>
|
|
166
|
-
This mini app needs the Eventus bridge to access the real runtime.
|
|
167
|
-
Open it from Eventus X to test it with production-like behavior.
|
|
168
|
-
</p>
|
|
169
|
-
</div>
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return (
|
|
174
|
-
<div className="handoff-panel">
|
|
175
|
-
<div className="handoff-copy">
|
|
176
|
-
<h2>Live preview from your phone</h2>
|
|
177
|
-
<p>
|
|
178
|
-
Open Eventus X, go to <strong>Developer</strong>, and scan this QR
|
|
179
|
-
code to launch the mini app with the real Eventus bridge.
|
|
180
|
-
</p>
|
|
181
|
-
<dl className="preview-grid">
|
|
182
|
-
<div>
|
|
183
|
-
<dt>Preview URL</dt>
|
|
184
|
-
<dd>
|
|
185
|
-
<code>{preview.urls.preview}</code>
|
|
186
|
-
</dd>
|
|
187
|
-
</div>
|
|
188
|
-
<div>
|
|
189
|
-
<dt>Local URL</dt>
|
|
190
|
-
<dd>
|
|
191
|
-
<code>{preview.urls.local}</code>
|
|
192
|
-
</dd>
|
|
193
|
-
</div>
|
|
194
|
-
{preview.urls.network ? (
|
|
195
|
-
<div>
|
|
196
|
-
<dt>Network URL</dt>
|
|
197
|
-
<dd>
|
|
198
|
-
<code>{preview.urls.network}</code>
|
|
199
|
-
</dd>
|
|
200
|
-
</div>
|
|
201
|
-
) : null}
|
|
202
|
-
<div>
|
|
203
|
-
<dt>Permissions</dt>
|
|
204
|
-
<dd>{preview.manifest.permissions?.join(", ") ?? "None"}</dd>
|
|
205
|
-
</div>
|
|
206
|
-
</dl>
|
|
207
|
-
<p className="hint-text">
|
|
208
|
-
Your phone and computer must be on the same local network.
|
|
209
|
-
</p>
|
|
210
|
-
</div>
|
|
211
|
-
<QrPanel payload={preview} />
|
|
212
|
-
</div>
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function QrPanel({
|
|
217
|
-
payload,
|
|
218
|
-
}: {
|
|
219
|
-
payload: EventusDevPreviewDescriptor;
|
|
220
|
-
}) {
|
|
221
|
-
const [svg, setSvg] = useState("");
|
|
222
|
-
|
|
223
|
-
useEffect(() => {
|
|
224
|
-
let cancelled = false;
|
|
225
|
-
|
|
226
|
-
void (async () => {
|
|
227
|
-
const nextSvg = await QRCode.toString(JSON.stringify(payload), {
|
|
228
|
-
type: "svg",
|
|
229
|
-
width: 220,
|
|
230
|
-
margin: 1,
|
|
231
|
-
});
|
|
232
|
-
if (!cancelled) {
|
|
233
|
-
setSvg(nextSvg);
|
|
234
|
-
}
|
|
235
|
-
})();
|
|
236
|
-
|
|
237
|
-
return () => {
|
|
238
|
-
cancelled = true;
|
|
239
|
-
};
|
|
240
|
-
}, [payload]);
|
|
241
|
-
|
|
242
|
-
return (
|
|
243
|
-
<div className="qr-panel">
|
|
244
|
-
<div
|
|
245
|
-
className="qr-code"
|
|
246
|
-
aria-label="Eventus live preview QR code"
|
|
247
|
-
dangerouslySetInnerHTML={{
|
|
248
|
-
__html: svg || "<div class='qr-placeholder'>Generating QR…</div>",
|
|
249
|
-
}}
|
|
250
|
-
/>
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
253
|
-
}
|
package/template/src/styles.css
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
:root {
|
|
2
2
|
font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
|
|
3
3
|
color: #102347;
|
|
4
|
-
background:
|
|
5
|
-
|
|
4
|
+
background: radial-gradient(
|
|
5
|
+
circle at top,
|
|
6
|
+
#d8f2ff 0%,
|
|
7
|
+
#f3f9ff 35%,
|
|
8
|
+
#eef3ff 100%
|
|
9
|
+
);
|
|
6
10
|
line-height: 1.5;
|
|
7
11
|
font-weight: 400;
|
|
8
12
|
}
|
|
@@ -73,70 +77,6 @@ h1 {
|
|
|
73
77
|
gap: 20px;
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
.handoff-copy h2 {
|
|
77
|
-
margin: 0 0 8px;
|
|
78
|
-
font-size: 1.5rem;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.handoff-copy p {
|
|
82
|
-
margin: 0;
|
|
83
|
-
color: #465d82;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.preview-grid {
|
|
87
|
-
margin: 20px 0 0;
|
|
88
|
-
display: grid;
|
|
89
|
-
gap: 12px;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
.preview-grid div {
|
|
93
|
-
padding: 14px 16px;
|
|
94
|
-
border-radius: 16px;
|
|
95
|
-
background: #f8fbff;
|
|
96
|
-
border: 1px solid rgba(16, 35, 71, 0.08);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.preview-grid code {
|
|
100
|
-
word-break: break-all;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.hint-text {
|
|
104
|
-
margin-top: 16px;
|
|
105
|
-
font-size: 0.95rem;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.qr-panel {
|
|
109
|
-
display: grid;
|
|
110
|
-
place-items: center;
|
|
111
|
-
padding: 20px;
|
|
112
|
-
border-radius: 24px;
|
|
113
|
-
background: linear-gradient(180deg, #fefefe 0%, #f3f7ff 100%);
|
|
114
|
-
border: 1px solid rgba(16, 35, 71, 0.08);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.qr-code {
|
|
118
|
-
width: min(260px, 100%);
|
|
119
|
-
aspect-ratio: 1;
|
|
120
|
-
display: grid;
|
|
121
|
-
place-items: center;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.qr-code svg {
|
|
125
|
-
width: 100%;
|
|
126
|
-
height: auto;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.qr-placeholder {
|
|
130
|
-
display: grid;
|
|
131
|
-
place-items: center;
|
|
132
|
-
width: 100%;
|
|
133
|
-
height: 100%;
|
|
134
|
-
border-radius: 20px;
|
|
135
|
-
background: #eef6ff;
|
|
136
|
-
color: #22548d;
|
|
137
|
-
font-weight: 600;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
80
|
.details-grid {
|
|
141
81
|
display: grid;
|
|
142
82
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
@@ -186,10 +126,3 @@ dd {
|
|
|
186
126
|
grid-template-columns: 1fr;
|
|
187
127
|
}
|
|
188
128
|
}
|
|
189
|
-
|
|
190
|
-
@media (min-width: 641px) {
|
|
191
|
-
.handoff-panel {
|
|
192
|
-
grid-template-columns: minmax(0, 1.3fr) minmax(220px, 0.7fr);
|
|
193
|
-
align-items: start;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
export type EventusDevPreviewDescriptor = {
|
|
2
|
-
type: "eventus-dev-preview";
|
|
3
|
-
version: 1;
|
|
4
|
-
manifest: {
|
|
5
|
-
name: string;
|
|
6
|
-
appId: string;
|
|
7
|
-
version: string;
|
|
8
|
-
permissions?: string[];
|
|
9
|
-
};
|
|
10
|
-
urls: {
|
|
11
|
-
local: string;
|
|
12
|
-
network: string | null;
|
|
13
|
-
preview: string;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export function isBridgeUnavailableError(error: unknown): boolean {
|
|
18
|
-
return (
|
|
19
|
-
error instanceof Error && error.name === "EventusBridgeUnavailableError"
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function fetchDevPreviewDescriptor(): Promise<EventusDevPreviewDescriptor | null> {
|
|
24
|
-
if (typeof fetch !== "function") {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const response = await fetch("/__eventus__/preview.json", {
|
|
30
|
-
headers: {
|
|
31
|
-
Accept: "application/json",
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
if (!response.ok) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const json = (await response.json()) as unknown;
|
|
39
|
-
return parseDevPreviewDescriptor(json);
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function parseDevPreviewDescriptor(
|
|
46
|
-
value: unknown,
|
|
47
|
-
): EventusDevPreviewDescriptor | null {
|
|
48
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const descriptor = value as Record<string, unknown>;
|
|
53
|
-
const manifest = descriptor.manifest;
|
|
54
|
-
const urls = descriptor.urls;
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
descriptor.type !== "eventus-dev-preview" ||
|
|
58
|
-
descriptor.version !== 1 ||
|
|
59
|
-
!manifest ||
|
|
60
|
-
typeof manifest !== "object" ||
|
|
61
|
-
Array.isArray(manifest) ||
|
|
62
|
-
!urls ||
|
|
63
|
-
typeof urls !== "object" ||
|
|
64
|
-
Array.isArray(urls)
|
|
65
|
-
) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const nextManifest = manifest as Record<string, unknown>;
|
|
70
|
-
const nextUrls = urls as Record<string, unknown>;
|
|
71
|
-
const permissions = nextManifest.permissions;
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
typeof nextManifest.name !== "string" ||
|
|
75
|
-
typeof nextManifest.appId !== "string" ||
|
|
76
|
-
typeof nextManifest.version !== "string" ||
|
|
77
|
-
typeof nextUrls.local !== "string" ||
|
|
78
|
-
(nextUrls.network !== null && typeof nextUrls.network !== "string") ||
|
|
79
|
-
typeof nextUrls.preview !== "string" ||
|
|
80
|
-
(permissions !== undefined &&
|
|
81
|
-
(!Array.isArray(permissions) ||
|
|
82
|
-
permissions.some((permission) => typeof permission !== "string")))
|
|
83
|
-
) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
type: "eventus-dev-preview",
|
|
89
|
-
version: 1,
|
|
90
|
-
manifest: {
|
|
91
|
-
name: nextManifest.name,
|
|
92
|
-
appId: nextManifest.appId,
|
|
93
|
-
version: nextManifest.version,
|
|
94
|
-
...(permissions ? { permissions: permissions as string[] } : {}),
|
|
95
|
-
},
|
|
96
|
-
urls: {
|
|
97
|
-
local: nextUrls.local,
|
|
98
|
-
network: nextUrls.network as string | null,
|
|
99
|
-
preview: nextUrls.preview,
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
}
|