create-eventus-app 0.1.0

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.
@@ -0,0 +1,108 @@
1
+ // src/scaffold.ts
2
+ import {
3
+ cp,
4
+ mkdir,
5
+ readdir,
6
+ readFile,
7
+ rm,
8
+ stat,
9
+ writeFile
10
+ } from "fs/promises";
11
+ import path from "path";
12
+ import { fileURLToPath } from "url";
13
+ var DEFAULT_TEMPLATE_CANDIDATES = [
14
+ path.resolve(
15
+ path.dirname(fileURLToPath(import.meta.url)),
16
+ "..",
17
+ "template"
18
+ ),
19
+ path.resolve(
20
+ path.dirname(fileURLToPath(import.meta.url)),
21
+ "..",
22
+ "..",
23
+ "template-react",
24
+ "template"
25
+ )
26
+ ];
27
+ var PRESERVED_DIRECTORY_ENTRIES = /* @__PURE__ */ new Set([".git"]);
28
+ async function scaffoldProject(options) {
29
+ const templateDir = await resolveTemplateDir(options.templateDir);
30
+ const isTargetDirectoryNonEmpty = await directoryHasFiles(options.targetDir);
31
+ if (isTargetDirectoryNonEmpty && !options.force) {
32
+ const shouldContinue = options.onConfirmOverwrite ? await options.onConfirmOverwrite() : false;
33
+ if (!shouldContinue) {
34
+ throw new Error(
35
+ `Target directory ${options.targetDir} is not empty. Use --force to overwrite it.`
36
+ );
37
+ }
38
+ }
39
+ await mkdir(options.targetDir, { recursive: true });
40
+ if (isTargetDirectoryNonEmpty) {
41
+ await clearTargetDirectory(options.targetDir);
42
+ }
43
+ await cp(templateDir, options.targetDir, { recursive: true });
44
+ const replacements = /* @__PURE__ */ new Map([
45
+ ["__EVENTUS_PACKAGE_NAME__", options.packageName],
46
+ ["__EVENTUS_DISPLAY_NAME__", options.displayName],
47
+ ["__EVENTUS_APP_ID__", options.appId],
48
+ ["__EVENTUS_SDK_VERSION__", options.sdkVersion],
49
+ ["__EVENTUS_VITE_PLUGIN_VERSION__", options.vitePluginVersion]
50
+ ]);
51
+ await replacePlaceholders(options.targetDir, replacements);
52
+ }
53
+ async function resolveTemplateDir(explicitTemplateDir) {
54
+ if (explicitTemplateDir) {
55
+ return explicitTemplateDir;
56
+ }
57
+ for (const candidate of DEFAULT_TEMPLATE_CANDIDATES) {
58
+ try {
59
+ const candidateStats = await stat(candidate);
60
+ if (candidateStats.isDirectory()) {
61
+ return candidate;
62
+ }
63
+ } catch {
64
+ }
65
+ }
66
+ throw new Error("Unable to locate the Eventus React template.");
67
+ }
68
+ async function directoryHasFiles(targetDir) {
69
+ try {
70
+ const entries = await readdir(targetDir);
71
+ return entries.length > 0;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ async function clearTargetDirectory(targetDir) {
77
+ const entries = await readdir(targetDir, { withFileTypes: true });
78
+ await Promise.all(
79
+ entries.filter((entry) => !PRESERVED_DIRECTORY_ENTRIES.has(entry.name)).map(
80
+ (entry) => rm(path.join(targetDir, entry.name), {
81
+ recursive: true,
82
+ force: true
83
+ })
84
+ )
85
+ );
86
+ }
87
+ async function replacePlaceholders(directory, replacements) {
88
+ const entries = await readdir(directory, { withFileTypes: true });
89
+ for (const entry of entries) {
90
+ const fullPath = path.join(directory, entry.name);
91
+ if (entry.isDirectory()) {
92
+ await replacePlaceholders(fullPath, replacements);
93
+ continue;
94
+ }
95
+ const fileContents = await readFile(fullPath, "utf8");
96
+ let nextContents = fileContents;
97
+ for (const [placeholder, value] of replacements) {
98
+ nextContents = nextContents.replaceAll(placeholder, value);
99
+ }
100
+ if (nextContents !== fileContents) {
101
+ await writeFile(fullPath, nextContents, "utf8");
102
+ }
103
+ }
104
+ }
105
+
106
+ export {
107
+ scaffoldProject
108
+ };
@@ -0,0 +1,58 @@
1
+ // src/utils.ts
2
+ import { readFile } from "fs/promises";
3
+ import path from "path";
4
+ import validateNpmPackageName from "validate-npm-package-name";
5
+ var PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
6
+ function resolveProjectInput(input, cwd = process.cwd()) {
7
+ const trimmedInput = input.trim();
8
+ const targetDir = path.resolve(cwd, trimmedInput);
9
+ const directoryName = path.basename(targetDir);
10
+ return {
11
+ input: trimmedInput,
12
+ targetDir,
13
+ directoryName,
14
+ packageName: directoryName
15
+ };
16
+ }
17
+ function validateProjectName(name) {
18
+ const validation = validateNpmPackageName(name);
19
+ if (validation.validForNewPackages) {
20
+ return [];
21
+ }
22
+ return [...validation.errors ?? [], ...validation.warnings ?? []];
23
+ }
24
+ function toDisplayName(projectName) {
25
+ return projectName.split(/[-_.\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join(" ");
26
+ }
27
+ function toAppId(projectName) {
28
+ return projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
29
+ }
30
+ function isPackageManager(value) {
31
+ return PACKAGE_MANAGERS.includes(value);
32
+ }
33
+ async function readToolkitVersion(packageFileUrl) {
34
+ const packageJson = JSON.parse(await readFile(packageFileUrl, "utf8"));
35
+ return packageJson.version ?? "0.1.0";
36
+ }
37
+ function createNextStepsMessage(packageManager, projectDirectory) {
38
+ const relativeDirectory = path.relative(process.cwd(), projectDirectory) || ".";
39
+ const installCommand = packageManager === "npm" ? "npm install" : `${packageManager} install`;
40
+ const devCommand = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
41
+ return [
42
+ "Next steps:",
43
+ ` cd ${relativeDirectory}`,
44
+ ` ${installCommand}`,
45
+ ` ${devCommand}`
46
+ ].join("\n");
47
+ }
48
+
49
+ export {
50
+ PACKAGE_MANAGERS,
51
+ resolveProjectInput,
52
+ validateProjectName,
53
+ toDisplayName,
54
+ toAppId,
55
+ isPackageManager,
56
+ readToolkitVersion,
57
+ createNextStepsMessage
58
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ scaffoldProject
4
+ } from "./chunk-5JPHU7SZ.js";
5
+ import {
6
+ PACKAGE_MANAGERS,
7
+ createNextStepsMessage,
8
+ isPackageManager,
9
+ readToolkitVersion,
10
+ resolveProjectInput,
11
+ toAppId,
12
+ toDisplayName,
13
+ validateProjectName
14
+ } from "./chunk-BFKUHPSY.js";
15
+
16
+ // src/index.ts
17
+ import {
18
+ cancel,
19
+ confirm,
20
+ intro,
21
+ isCancel,
22
+ outro,
23
+ select,
24
+ text
25
+ } from "@clack/prompts";
26
+ import { cac } from "cac";
27
+ async function run() {
28
+ const packageVersion = await readToolkitVersion(new URL("../package.json", import.meta.url));
29
+ const cli = cac("create-eventus-app");
30
+ cli.command("[targetDir]", "Create a new Eventus mini app").option("--package-manager <name>", "Package manager to show in next steps").option("--force", "Allow scaffolding into a non-empty directory").option("--sdk-version <version>", "Internal override for local SDK testing").option(
31
+ "--vite-plugin-version <version>",
32
+ "Internal override for local Vite plugin testing"
33
+ ).action(async (targetDir, options) => {
34
+ intro("Create Eventus App");
35
+ try {
36
+ const resolvedInput = await promptForProject(targetDir);
37
+ const packageManager = await resolvePackageManager(options.packageManager);
38
+ const toolkitVersion = options.sdkVersion && options.vitePluginVersion ? void 0 : packageVersion;
39
+ const sdkVersion = options.sdkVersion?.trim() || toolkitVersion || packageVersion;
40
+ const vitePluginVersion = options.vitePluginVersion?.trim() || toolkitVersion || packageVersion;
41
+ await scaffoldProject({
42
+ targetDir: resolvedInput.targetDir,
43
+ packageName: resolvedInput.packageName,
44
+ displayName: toDisplayName(resolvedInput.packageName),
45
+ appId: toAppId(resolvedInput.packageName),
46
+ packageManager,
47
+ sdkVersion,
48
+ vitePluginVersion,
49
+ force: options.force,
50
+ onConfirmOverwrite: async () => {
51
+ const answer = await confirm({
52
+ message: `The directory ${resolvedInput.targetDir} is not empty. Continue anyway?`,
53
+ initialValue: false
54
+ });
55
+ if (isCancel(answer)) {
56
+ throw new Error("Scaffolding cancelled by user.");
57
+ }
58
+ return answer;
59
+ }
60
+ });
61
+ outro(createNextStepsMessage(packageManager, resolvedInput.targetDir));
62
+ } catch (error) {
63
+ cancel(error instanceof Error ? error.message : "Scaffolding failed.");
64
+ process.exitCode = 1;
65
+ }
66
+ });
67
+ cli.help();
68
+ cli.version(packageVersion);
69
+ cli.parse();
70
+ }
71
+ async function promptForProject(targetDir) {
72
+ const targetInput = targetDir?.trim() ? targetDir : await promptText("Project name:", "eventus-mini-app");
73
+ const resolvedInput = resolveProjectInput(targetInput);
74
+ const validationErrors = validateProjectName(resolvedInput.packageName);
75
+ if (validationErrors.length > 0) {
76
+ throw new Error(
77
+ `Invalid project name "${resolvedInput.packageName}": ${validationErrors.join("; ")}`
78
+ );
79
+ }
80
+ const appId = toAppId(resolvedInput.packageName);
81
+ if (!appId) {
82
+ throw new Error(
83
+ `Unable to derive a valid Eventus appId from "${resolvedInput.packageName}".`
84
+ );
85
+ }
86
+ return resolvedInput;
87
+ }
88
+ async function resolvePackageManager(packageManagerOption) {
89
+ if (packageManagerOption) {
90
+ if (!isPackageManager(packageManagerOption)) {
91
+ throw new Error(
92
+ `Unsupported package manager "${packageManagerOption}". Expected one of: ${PACKAGE_MANAGERS.join(", ")}.`
93
+ );
94
+ }
95
+ return packageManagerOption;
96
+ }
97
+ const answer = await select({
98
+ message: "Which package manager should the next steps use?",
99
+ options: PACKAGE_MANAGERS.map((packageManager) => ({
100
+ value: packageManager,
101
+ label: packageManager
102
+ })),
103
+ initialValue: "pnpm"
104
+ });
105
+ if (isCancel(answer)) {
106
+ throw new Error("Scaffolding cancelled by user.");
107
+ }
108
+ return answer;
109
+ }
110
+ async function promptText(message, initialValue) {
111
+ const answer = await text({
112
+ message,
113
+ initialValue,
114
+ placeholder: initialValue
115
+ });
116
+ if (isCancel(answer)) {
117
+ throw new Error("Scaffolding cancelled by user.");
118
+ }
119
+ return answer.trim();
120
+ }
121
+ void run();
@@ -0,0 +1,17 @@
1
+ import { PackageManager } from './utils.js';
2
+
3
+ type ScaffoldProjectOptions = {
4
+ targetDir: string;
5
+ packageName: string;
6
+ displayName: string;
7
+ appId: string;
8
+ packageManager: PackageManager;
9
+ sdkVersion: string;
10
+ vitePluginVersion: string;
11
+ force?: boolean | undefined;
12
+ templateDir?: string | undefined;
13
+ onConfirmOverwrite?: (() => Promise<boolean>) | undefined;
14
+ };
15
+ declare function scaffoldProject(options: ScaffoldProjectOptions): Promise<void>;
16
+
17
+ export { type ScaffoldProjectOptions, scaffoldProject };
@@ -0,0 +1,6 @@
1
+ import {
2
+ scaffoldProject
3
+ } from "./chunk-5JPHU7SZ.js";
4
+ export {
5
+ scaffoldProject
6
+ };
@@ -0,0 +1,17 @@
1
+ declare const PACKAGE_MANAGERS: readonly ["pnpm", "npm", "yarn", "bun"];
2
+ type PackageManager = (typeof PACKAGE_MANAGERS)[number];
3
+ type ResolvedProjectInput = {
4
+ input: string;
5
+ targetDir: string;
6
+ directoryName: string;
7
+ packageName: string;
8
+ };
9
+ declare function resolveProjectInput(input: string, cwd?: string): ResolvedProjectInput;
10
+ declare function validateProjectName(name: string): string[];
11
+ declare function toDisplayName(projectName: string): string;
12
+ declare function toAppId(projectName: string): string;
13
+ declare function isPackageManager(value: string): value is PackageManager;
14
+ declare function readToolkitVersion(packageFileUrl: URL): Promise<string>;
15
+ declare function createNextStepsMessage(packageManager: PackageManager, projectDirectory: string): string;
16
+
17
+ export { PACKAGE_MANAGERS, type PackageManager, type ResolvedProjectInput, createNextStepsMessage, isPackageManager, readToolkitVersion, resolveProjectInput, toAppId, toDisplayName, validateProjectName };
package/dist/utils.js ADDED
@@ -0,0 +1,20 @@
1
+ import {
2
+ PACKAGE_MANAGERS,
3
+ createNextStepsMessage,
4
+ isPackageManager,
5
+ readToolkitVersion,
6
+ resolveProjectInput,
7
+ toAppId,
8
+ toDisplayName,
9
+ validateProjectName
10
+ } from "./chunk-BFKUHPSY.js";
11
+ export {
12
+ PACKAGE_MANAGERS,
13
+ createNextStepsMessage,
14
+ isPackageManager,
15
+ readToolkitVersion,
16
+ resolveProjectInput,
17
+ toAppId,
18
+ toDisplayName,
19
+ validateProjectName
20
+ };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "create-eventus-app",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold an Eventus mini app.",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-eventus-app": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./scaffold": {
18
+ "types": "./dist/scaffold.d.ts",
19
+ "import": "./dist/scaffold.js"
20
+ },
21
+ "./utils": {
22
+ "types": "./dist/utils.d.ts",
23
+ "import": "./dist/utils.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "template"
29
+ ],
30
+ "scripts": {
31
+ "build": "node ../../scripts/sync-template.mjs && tsup src/index.ts src/scaffold.ts src/utils.ts --format esm --dts --clean",
32
+ "check": "tsc -p tsconfig.json --noEmit",
33
+ "test": "vitest run"
34
+ },
35
+ "dependencies": {
36
+ "@clack/prompts": "^0.10.1",
37
+ "cac": "^6.7.14",
38
+ "validate-npm-package-name": "^5.0.0"
39
+ }
40
+ }
@@ -0,0 +1,22 @@
1
+ # __EVENTUS_DISPLAY_NAME__
2
+
3
+ Starter mini app generated for the Eventus platform.
4
+
5
+ ## Quickstart
6
+
7
+ ```bash
8
+ pnpm install
9
+ pnpm dev
10
+ ```
11
+
12
+ ## Available Scripts
13
+
14
+ - `pnpm dev`: start the Vite development server
15
+ - `pnpm build`: type-check and build the app
16
+ - `pnpm preview`: preview the production build locally
17
+
18
+ ## Eventus Files
19
+
20
+ - `eventus.manifest.json`: base Eventus manifest consumed by the Vite plugin
21
+ - `src/eventus-env.d.ts`: type declarations for the Eventus virtual module
22
+ - `src/App.tsx`: sample usage of `@eventusgo/sdk` and the manifest module
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "__EVENTUS_DISPLAY_NAME__",
3
+ "appId": "__EVENTUS_APP_ID__",
4
+ "version": "0.1.0",
5
+ "permissions": ["user.profile.read", "theme.read"]
6
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__EVENTUS_DISPLAY_NAME__</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "__EVENTUS_PACKAGE_NAME__",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc --noEmit && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@eventusgo/sdk": "__EVENTUS_SDK_VERSION__",
13
+ "react": "18.3.1",
14
+ "react-dom": "18.3.1"
15
+ },
16
+ "devDependencies": {
17
+ "@eventusgo/vite-plugin": "__EVENTUS_VITE_PLUGIN_VERSION__",
18
+ "@types/react": "18.3.28",
19
+ "@types/react-dom": "18.3.7",
20
+ "@vitejs/plugin-react": "4.7.0",
21
+ "typescript": "5.9.3",
22
+ "vite": "5.4.21"
23
+ }
24
+ }
@@ -0,0 +1,122 @@
1
+ import { useEffect, useState } from "react";
2
+ import manifest from "virtual:eventus-manifest";
3
+ import {
4
+ eventus,
5
+ type EventusContext,
6
+ type EventusRuntimeSource,
7
+ } from "@eventusgo/sdk";
8
+
9
+ type LoadState =
10
+ | { status: "idle" | "loading" }
11
+ | { status: "ready"; context: EventusContext }
12
+ | { status: "error"; message: string };
13
+
14
+ export default function App() {
15
+ const [state, setState] = useState<LoadState>({ status: "loading" });
16
+ const [runtimeSource, setRuntimeSource] = useState<EventusRuntimeSource | "loading">(
17
+ "loading",
18
+ );
19
+
20
+ useEffect(() => {
21
+ let cancelled = false;
22
+ let receivedReadyEvent = false;
23
+
24
+ const unsubscribeReady = eventus.on("ready", ({ context, source }) => {
25
+ receivedReadyEvent = true;
26
+ if (!cancelled) {
27
+ setRuntimeSource(source);
28
+ setState({ status: "ready", context });
29
+ }
30
+ });
31
+
32
+ const unsubscribe = eventus.on("themeChange", ({ context }) => {
33
+ if (!cancelled) {
34
+ setState({ status: "ready", context });
35
+ }
36
+ });
37
+
38
+ void (async () => {
39
+ try {
40
+ await eventus.ready();
41
+ const context = await eventus.getContext();
42
+
43
+ if (!cancelled && !receivedReadyEvent) {
44
+ setRuntimeSource("mock");
45
+ setState({ status: "ready", context });
46
+ }
47
+ } catch (error) {
48
+ if (!cancelled) {
49
+ setState({
50
+ status: "error",
51
+ message:
52
+ error instanceof Error
53
+ ? error.message
54
+ : "Unable to initialize the Eventus client.",
55
+ });
56
+ }
57
+ }
58
+ })();
59
+
60
+ return () => {
61
+ cancelled = true;
62
+ unsubscribeReady();
63
+ unsubscribe();
64
+ };
65
+ }, []);
66
+
67
+ const runtimeLabel =
68
+ runtimeSource === "native"
69
+ ? "Native bridge detected"
70
+ : runtimeSource === "mock"
71
+ ? "Browser mock mode active"
72
+ : "Detecting runtime…";
73
+
74
+ return (
75
+ <main className="app-shell">
76
+ <section className="card">
77
+ <p className="eyebrow">Eventus Mini App Toolkit</p>
78
+ <h1>{manifest.name}</h1>
79
+ <p className="subtitle">
80
+ App ID <code>{manifest.appId}</code> · Version <code>{manifest.version}</code>
81
+ </p>
82
+
83
+ <div className="status-banner">{runtimeLabel}</div>
84
+
85
+ {state.status === "loading" ? (
86
+ <p>Connecting to the Eventus client…</p>
87
+ ) : null}
88
+
89
+ {state.status === "error" ? (
90
+ <p className="error-text">{state.message}</p>
91
+ ) : null}
92
+
93
+ {state.status === "ready" ? (
94
+ <dl className="details-grid">
95
+ <div>
96
+ <dt>Theme</dt>
97
+ <dd>{state.context.theme}</dd>
98
+ </div>
99
+ <div>
100
+ <dt>Locale</dt>
101
+ <dd>{state.context.locale}</dd>
102
+ </div>
103
+ <div>
104
+ <dt>User</dt>
105
+ <dd>{state.context.user?.name ?? state.context.user?.id ?? "Anonymous"}</dd>
106
+ </div>
107
+ <div>
108
+ <dt>Manifest Permissions</dt>
109
+ <dd>{manifest.permissions?.join(", ") ?? "None"}</dd>
110
+ </div>
111
+ </dl>
112
+ ) : null}
113
+
114
+ {state.status === "ready" ? (
115
+ <pre className="context-preview">
116
+ {JSON.stringify(state.context, null, 2)}
117
+ </pre>
118
+ ) : null}
119
+ </section>
120
+ </main>
121
+ );
122
+ }
@@ -0,0 +1,15 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ type EventusManifest = {
4
+ name: string;
5
+ appId: string;
6
+ version: string;
7
+ permissions?: string[];
8
+ };
9
+
10
+ declare module "virtual:eventus-manifest" {
11
+ const manifest: EventusManifest;
12
+ export default manifest;
13
+ }
14
+
15
+ declare const __EVENTUS_MANIFEST__: EventusManifest;
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+
4
+ import App from "./App";
5
+ import "./styles.css";
6
+
7
+ ReactDOM.createRoot(document.getElementById("root")!).render(
8
+ <React.StrictMode>
9
+ <App />
10
+ </React.StrictMode>,
11
+ );
@@ -0,0 +1,114 @@
1
+ :root {
2
+ font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
3
+ color: #102347;
4
+ background:
5
+ radial-gradient(circle at top, #d8f2ff 0%, #f3f9ff 35%, #eef3ff 100%);
6
+ line-height: 1.5;
7
+ font-weight: 400;
8
+ }
9
+
10
+ * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ margin: 0;
16
+ min-width: 320px;
17
+ min-height: 100vh;
18
+ }
19
+
20
+ code,
21
+ pre {
22
+ font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
23
+ }
24
+
25
+ .app-shell {
26
+ min-height: 100vh;
27
+ display: grid;
28
+ place-items: center;
29
+ padding: 24px;
30
+ }
31
+
32
+ .card {
33
+ width: min(720px, 100%);
34
+ padding: 32px;
35
+ border-radius: 24px;
36
+ background: rgba(255, 255, 255, 0.84);
37
+ border: 1px solid rgba(16, 35, 71, 0.08);
38
+ box-shadow: 0 24px 80px rgba(38, 73, 134, 0.14);
39
+ backdrop-filter: blur(16px);
40
+ }
41
+
42
+ .eyebrow {
43
+ margin: 0 0 8px;
44
+ font-size: 0.85rem;
45
+ letter-spacing: 0.12em;
46
+ text-transform: uppercase;
47
+ color: #3f6ea8;
48
+ }
49
+
50
+ h1 {
51
+ margin: 0;
52
+ font-size: clamp(2rem, 4vw, 3rem);
53
+ }
54
+
55
+ .subtitle {
56
+ margin: 12px 0 0;
57
+ color: #465d82;
58
+ }
59
+
60
+ .status-banner {
61
+ margin: 24px 0;
62
+ display: inline-flex;
63
+ padding: 8px 12px;
64
+ border-radius: 999px;
65
+ background: #eef6ff;
66
+ color: #22548d;
67
+ font-weight: 600;
68
+ }
69
+
70
+ .details-grid {
71
+ display: grid;
72
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
73
+ gap: 16px;
74
+ margin: 24px 0;
75
+ }
76
+
77
+ .details-grid div {
78
+ padding: 16px;
79
+ border-radius: 16px;
80
+ background: #f8fbff;
81
+ border: 1px solid rgba(16, 35, 71, 0.08);
82
+ }
83
+
84
+ dt {
85
+ margin-bottom: 6px;
86
+ font-size: 0.85rem;
87
+ color: #6880a6;
88
+ text-transform: uppercase;
89
+ letter-spacing: 0.06em;
90
+ }
91
+
92
+ dd {
93
+ margin: 0;
94
+ font-weight: 600;
95
+ }
96
+
97
+ .context-preview {
98
+ margin: 0;
99
+ padding: 16px;
100
+ border-radius: 16px;
101
+ overflow: auto;
102
+ background: #0d1d3a;
103
+ color: #eef5ff;
104
+ }
105
+
106
+ .error-text {
107
+ color: #b42318;
108
+ }
109
+
110
+ @media (max-width: 640px) {
111
+ .card {
112
+ padding: 24px;
113
+ }
114
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "allowJs": false,
7
+ "skipLibCheck": true,
8
+ "esModuleInterop": false,
9
+ "allowSyntheticDefaultImports": true,
10
+ "strict": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "module": "ESNext",
13
+ "moduleResolution": "Bundler",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "jsx": "react-jsx"
18
+ },
19
+ "include": ["src", "vite.config.ts"]
20
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import { eventusVitePlugin } from "@eventusgo/vite-plugin";
4
+
5
+ export default defineConfig({
6
+ plugins: [react(), eventusVitePlugin()],
7
+ });