@viibestack/ui 0.6.3 → 0.6.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@viibestack/ui",
3
- "version": "0.6.3",
4
- "description": "Dependency-free React UI kit -- icons, core components (Button, Card, Input, Modal, etc.), a real per-app data client (listRows/upsertRow/deleteRow/captureClientError), and a real per-app end-user auth client (signUp/logIn/logOut/getCurrentUser/reportError) backed by ViibeStack's own platform-managed data store.",
3
+ "version": "0.6.4",
4
+ "description": "Dependency-free React UI kit -- icons, core components (Button, Card, Input, Modal, etc.), a real per-app data client (listRows/upsertRow/deleteRow/captureClientError), a real per-app end-user auth client (signUp/logIn/logOut/getCurrentUser/reportError), and a self-gating PoweredByBadge component -- backed by ViibeStack's own platform-managed data store.",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
7
7
  "types": "./src/index.ts",
package/src/badge.tsx ADDED
@@ -0,0 +1,74 @@
1
+ "use client";
2
+
3
+ // Small fixed "Powered by ViibeStack" badge every generated app is required
4
+ // to render once near its root layout (see integrationRequirements() item
5
+ // 11 in services/deploy/src/mcp-agent.ts, and scratch-scaffold.ts's
6
+ // layout.tsx template, which injects this automatically for from-scratch
7
+ // builds). Self-gating: resolves this app's own tenant plan at runtime via
8
+ // GET /api/apps/:id/badge-status and renders nothing once that tenant is
9
+ // actively paying for a plan that waives the badge (Growing Business and
10
+ // above) -- no build-time branching needed, so the same shipped code keeps
11
+ // working correctly if a tenant upgrades/downgrades after the app was
12
+ // already deployed.
13
+ import { useEffect, useState } from "react";
14
+
15
+ let showBadgePromise: Promise<boolean> | null = null;
16
+
17
+ function resolveShowBadge(): Promise<boolean> {
18
+ if (!showBadgePromise) {
19
+ showBadgePromise = fetch(`${window.location.origin}/api/apps/resolve?hostname=${window.location.hostname}`)
20
+ .then((res) => (res.ok ? res.json() : null))
21
+ .then((data: { app_id?: string } | null) => {
22
+ if (!data?.app_id) return true;
23
+ return fetch(`${window.location.origin}/api/apps/${data.app_id}/badge-status`)
24
+ .then((res) => (res.ok ? res.json() : null))
25
+ .then((status: { show_badge?: boolean } | null) => status?.show_badge ?? true);
26
+ })
27
+ .catch(() => true);
28
+ }
29
+ return showBadgePromise;
30
+ }
31
+
32
+ export function PoweredByBadge() {
33
+ const [show, setShow] = useState(false);
34
+
35
+ useEffect(() => {
36
+ let cancelled = false;
37
+ resolveShowBadge().then((result) => {
38
+ if (!cancelled) setShow(result);
39
+ });
40
+ return () => {
41
+ cancelled = true;
42
+ };
43
+ }, []);
44
+
45
+ if (!show) return null;
46
+
47
+ return (
48
+ <a
49
+ href="https://viibestack.ai?utm_source=powered_by_badge"
50
+ target="_blank"
51
+ rel="noopener noreferrer"
52
+ style={{
53
+ position: "fixed",
54
+ bottom: "1rem",
55
+ right: "1rem",
56
+ zIndex: 9999,
57
+ display: "flex",
58
+ alignItems: "center",
59
+ gap: "0.375rem",
60
+ padding: "0.375rem 0.75rem",
61
+ borderRadius: "9999px",
62
+ backgroundColor: "rgba(17, 17, 17, 0.85)",
63
+ color: "#fff",
64
+ fontSize: "0.75rem",
65
+ fontFamily: "system-ui, -apple-system, sans-serif",
66
+ fontWeight: 500,
67
+ textDecoration: "none",
68
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.2)",
69
+ }}
70
+ >
71
+ Powered by ViibeStack
72
+ </a>
73
+ );
74
+ }
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@ export * from "./components";
3
3
  export * from "./nav";
4
4
  export * from "./data";
5
5
  export * from "./auth";
6
+ export * from "./badge";