version-pill-react 1.0.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,54 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface Version {
4
+ id: string;
5
+ version: string;
6
+ type: "major" | "minor" | "patch";
7
+ title: string;
8
+ description?: string;
9
+ emoji?: string;
10
+ features?: string[];
11
+ releaseDate: number;
12
+ isActive: boolean;
13
+ }
14
+ interface VersionPillConfig {
15
+ /** Your project slug from Version Pill dashboard */
16
+ projectId: string;
17
+ /** Version Pill API base URL (default: https://versionpill.com) */
18
+ baseUrl?: string;
19
+ }
20
+ interface VersionPillProps extends VersionPillConfig {
21
+ /** Position of the pill */
22
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "inline";
23
+ /** Custom class name */
24
+ className?: string;
25
+ /** Theme */
26
+ theme?: "light" | "dark" | "auto";
27
+ /** Show "Powered by Version Pill" branding */
28
+ showBranding?: boolean;
29
+ /** Accent color (hex) */
30
+ accentColor?: string;
31
+ /** Callback when a new version is detected */
32
+ onNewVersion?: (version: Version) => void;
33
+ }
34
+ interface ChangelogProps extends VersionPillConfig {
35
+ /** Max height in pixels */
36
+ maxHeight?: number;
37
+ /** Theme */
38
+ theme?: "light" | "dark" | "auto";
39
+ /** Custom class name */
40
+ className?: string;
41
+ }
42
+ interface RoadmapProps extends VersionPillConfig {
43
+ /** Max height in pixels */
44
+ maxHeight?: number;
45
+ /** Theme */
46
+ theme?: "light" | "dark" | "auto";
47
+ /** Custom class name */
48
+ className?: string;
49
+ }
50
+ declare function VersionPill({ projectId, baseUrl, position, className, theme, showBranding, accentColor, onNewVersion, }: VersionPillProps): react_jsx_runtime.JSX.Element | null;
51
+ declare function Changelog({ projectId, baseUrl, maxHeight, theme, className, }: ChangelogProps): react_jsx_runtime.JSX.Element;
52
+ declare function Roadmap({ projectId, baseUrl, maxHeight, theme, className, }: RoadmapProps): react_jsx_runtime.JSX.Element;
53
+
54
+ export { Changelog, type ChangelogProps, Roadmap, type RoadmapProps, type Version, VersionPill, type VersionPillConfig, type VersionPillProps, VersionPill as default };
@@ -0,0 +1,54 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface Version {
4
+ id: string;
5
+ version: string;
6
+ type: "major" | "minor" | "patch";
7
+ title: string;
8
+ description?: string;
9
+ emoji?: string;
10
+ features?: string[];
11
+ releaseDate: number;
12
+ isActive: boolean;
13
+ }
14
+ interface VersionPillConfig {
15
+ /** Your project slug from Version Pill dashboard */
16
+ projectId: string;
17
+ /** Version Pill API base URL (default: https://versionpill.com) */
18
+ baseUrl?: string;
19
+ }
20
+ interface VersionPillProps extends VersionPillConfig {
21
+ /** Position of the pill */
22
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "inline";
23
+ /** Custom class name */
24
+ className?: string;
25
+ /** Theme */
26
+ theme?: "light" | "dark" | "auto";
27
+ /** Show "Powered by Version Pill" branding */
28
+ showBranding?: boolean;
29
+ /** Accent color (hex) */
30
+ accentColor?: string;
31
+ /** Callback when a new version is detected */
32
+ onNewVersion?: (version: Version) => void;
33
+ }
34
+ interface ChangelogProps extends VersionPillConfig {
35
+ /** Max height in pixels */
36
+ maxHeight?: number;
37
+ /** Theme */
38
+ theme?: "light" | "dark" | "auto";
39
+ /** Custom class name */
40
+ className?: string;
41
+ }
42
+ interface RoadmapProps extends VersionPillConfig {
43
+ /** Max height in pixels */
44
+ maxHeight?: number;
45
+ /** Theme */
46
+ theme?: "light" | "dark" | "auto";
47
+ /** Custom class name */
48
+ className?: string;
49
+ }
50
+ declare function VersionPill({ projectId, baseUrl, position, className, theme, showBranding, accentColor, onNewVersion, }: VersionPillProps): react_jsx_runtime.JSX.Element | null;
51
+ declare function Changelog({ projectId, baseUrl, maxHeight, theme, className, }: ChangelogProps): react_jsx_runtime.JSX.Element;
52
+ declare function Roadmap({ projectId, baseUrl, maxHeight, theme, className, }: RoadmapProps): react_jsx_runtime.JSX.Element;
53
+
54
+ export { Changelog, type ChangelogProps, Roadmap, type RoadmapProps, type Version, VersionPill, type VersionPillConfig, type VersionPillProps, VersionPill as default };
package/dist/index.js ADDED
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/index.tsx
32
+ var index_exports = {};
33
+ __export(index_exports, {
34
+ Changelog: () => Changelog,
35
+ Roadmap: () => Roadmap,
36
+ VersionPill: () => VersionPill,
37
+ default: () => index_default
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+ var import_react = require("react");
41
+ var import_clsx = __toESM(require("clsx"));
42
+ var import_jsx_runtime = require("react/jsx-runtime");
43
+ var DEFAULT_BASE_URL = "https://versionpill.com";
44
+ var styles = {
45
+ pill: `
46
+ inline-flex items-center gap-1.5 px-2.5 py-1
47
+ text-xs font-medium rounded-full cursor-pointer
48
+ transition-all duration-200 select-none
49
+ border shadow-sm hover:shadow-md
50
+ `,
51
+ pillLight: `bg-white text-gray-700 border-gray-200 hover:border-gray-300`,
52
+ pillDark: `bg-gray-900 text-gray-100 border-gray-700 hover:border-gray-600`,
53
+ modal: `fixed inset-0 z-[99999] flex items-center justify-center p-4`,
54
+ backdrop: `absolute inset-0 bg-black/50 backdrop-blur-sm`,
55
+ panel: `relative w-full max-w-md max-h-[80vh] overflow-hidden rounded-xl shadow-2xl`,
56
+ panelLight: `bg-white`,
57
+ panelDark: `bg-gray-900`,
58
+ header: `p-4 border-b flex items-center justify-between`,
59
+ headerLight: `border-gray-100`,
60
+ headerDark: `border-gray-800`,
61
+ content: `p-4 overflow-y-auto max-h-[60vh]`,
62
+ version: `mb-4 last:mb-0`,
63
+ versionHeader: `flex items-center gap-2 mb-2`,
64
+ versionTitle: `font-semibold`,
65
+ versionBadge: `px-1.5 py-0.5 text-[10px] font-medium rounded`,
66
+ versionFeatures: `space-y-1 mt-2`,
67
+ versionFeature: `flex items-start gap-2 text-sm`,
68
+ footer: `p-3 border-t flex items-center justify-between`,
69
+ footerLight: `border-gray-100 bg-gray-50`,
70
+ footerDark: `border-gray-800 bg-gray-950`,
71
+ button: `px-3 py-1.5 text-xs font-medium rounded-lg transition-colors`,
72
+ buttonPrimary: `bg-blue-600 text-white hover:bg-blue-700`,
73
+ buttonSecondary: `text-gray-500 hover:text-gray-700`,
74
+ branding: `text-[10px] opacity-40 flex items-center gap-1`,
75
+ newDot: `w-2 h-2 rounded-full bg-green-500 animate-pulse`,
76
+ iframe: `w-full border-0`
77
+ };
78
+ function useTheme(theme) {
79
+ const [resolved, setResolved] = (0, import_react.useState)("light");
80
+ (0, import_react.useEffect)(() => {
81
+ if (theme === "auto") {
82
+ const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
83
+ setResolved(isDark ? "dark" : "light");
84
+ const listener = (e) => setResolved(e.matches ? "dark" : "light");
85
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
86
+ mq.addEventListener("change", listener);
87
+ return () => mq.removeEventListener("change", listener);
88
+ } else {
89
+ setResolved(theme);
90
+ }
91
+ }, [theme]);
92
+ return resolved;
93
+ }
94
+ function VersionPill({
95
+ projectId,
96
+ baseUrl = DEFAULT_BASE_URL,
97
+ position = "inline",
98
+ className,
99
+ theme = "auto",
100
+ showBranding = true,
101
+ accentColor,
102
+ onNewVersion
103
+ }) {
104
+ const [versions, setVersions] = (0, import_react.useState)([]);
105
+ const [loading, setLoading] = (0, import_react.useState)(true);
106
+ const [error, setError] = (0, import_react.useState)(null);
107
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
108
+ const [hasNewVersion, setHasNewVersion] = (0, import_react.useState)(false);
109
+ const resolvedTheme = useTheme(theme);
110
+ const isLight = resolvedTheme === "light";
111
+ const fetchChangelog = (0, import_react.useCallback)(async () => {
112
+ try {
113
+ const response = await fetch(`${baseUrl}/api/changelog/${projectId}?limit=10`);
114
+ if (!response.ok) throw new Error("Failed to fetch changelog");
115
+ const data = await response.json();
116
+ setVersions(data);
117
+ const storedVersion = localStorage.getItem(`vp_${projectId}_seen`);
118
+ if (data.length > 0 && data[0].version !== storedVersion) {
119
+ setHasNewVersion(true);
120
+ onNewVersion?.(data[0]);
121
+ }
122
+ } catch (err) {
123
+ setError(err.message);
124
+ } finally {
125
+ setLoading(false);
126
+ }
127
+ }, [projectId, baseUrl, onNewVersion]);
128
+ (0, import_react.useEffect)(() => {
129
+ fetchChangelog();
130
+ }, [fetchChangelog]);
131
+ const handleOpen = () => {
132
+ setIsOpen(true);
133
+ if (versions.length > 0) {
134
+ localStorage.setItem(`vp_${projectId}_seen`, versions[0].version);
135
+ setHasNewVersion(false);
136
+ }
137
+ };
138
+ const currentVersion = versions[0];
139
+ const positionStyles = {
140
+ "top-left": "fixed top-4 left-4 z-[9999]",
141
+ "top-right": "fixed top-4 right-4 z-[9999]",
142
+ "bottom-left": "fixed bottom-4 left-4 z-[9999]",
143
+ "bottom-right": "fixed bottom-4 right-4 z-[9999]",
144
+ inline: ""
145
+ };
146
+ if (loading) {
147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_clsx.default)(positionStyles[position], className), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_clsx.default)(styles.pill, isLight ? styles.pillLight : styles.pillDark), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "opacity-50", children: "..." }) }) });
148
+ }
149
+ if (error || !currentVersion) return null;
150
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_clsx.default)(positionStyles[position], className), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
152
+ "button",
153
+ {
154
+ onClick: handleOpen,
155
+ className: (0, import_clsx.default)(styles.pill, isLight ? styles.pillLight : styles.pillDark),
156
+ style: accentColor ? { borderColor: accentColor } : void 0,
157
+ children: [
158
+ hasNewVersion && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: styles.newDot }),
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
160
+ "v",
161
+ currentVersion.version
162
+ ] }),
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6 9l6 6 6-6" }) })
164
+ ]
165
+ }
166
+ ) }),
167
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.modal, children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.backdrop, onClick: () => setIsOpen(false) }),
169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_clsx.default)(styles.panel, isLight ? styles.panelLight : styles.panelDark), children: [
170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_clsx.default)(styles.header, isLight ? styles.headerLight : styles.headerDark), children: [
171
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
172
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-lg", children: currentVersion.emoji || "\u2728" }),
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: (0, import_clsx.default)("font-semibold", isLight ? "text-gray-900" : "text-white"), children: "What's New" }),
175
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: (0, import_clsx.default)("text-xs", isLight ? "text-gray-500" : "text-gray-400"), children: "Latest updates" })
176
+ ] })
177
+ ] }),
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
179
+ "button",
180
+ {
181
+ onClick: () => setIsOpen(false),
182
+ className: (0, import_clsx.default)("p-1 rounded hover:bg-gray-100", !isLight && "hover:bg-gray-800"),
183
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M18 6L6 18M6 6l12 12" }) })
184
+ }
185
+ )
186
+ ] }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.content, children: versions.slice(0, 5).map((version) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.version, children: [
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: styles.versionHeader, children: [
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-lg", children: version.emoji || "\u{1F4E6}" }),
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: (0, import_clsx.default)(styles.versionTitle, isLight ? "text-gray-900" : "text-white"), children: version.title }),
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
192
+ "span",
193
+ {
194
+ className: (0, import_clsx.default)(
195
+ styles.versionBadge,
196
+ version.type === "major" ? "bg-purple-100 text-purple-700" : version.type === "minor" ? "bg-blue-100 text-blue-700" : "bg-gray-100 text-gray-700"
197
+ ),
198
+ children: version.type
199
+ }
200
+ )
201
+ ] }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_clsx.default)("text-xs mb-2", isLight ? "text-gray-500" : "text-gray-400"), children: [
203
+ "v",
204
+ version.version,
205
+ " \xB7 ",
206
+ new Date(version.releaseDate).toLocaleDateString()
207
+ ] }),
208
+ version.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: (0, import_clsx.default)("text-sm mb-2", isLight ? "text-gray-600" : "text-gray-300"), children: version.description }),
209
+ version.features && version.features.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: styles.versionFeatures, children: version.features.map((feature, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: (0, import_clsx.default)(styles.versionFeature, isLight ? "text-gray-600" : "text-gray-300"), children: [
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-green-500 mt-0.5", children: "\u2713" }),
211
+ feature
212
+ ] }, i)) })
213
+ ] }, version.id)) }),
214
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: (0, import_clsx.default)(styles.footer, isLight ? styles.footerLight : styles.footerDark), children: [
215
+ showBranding && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
216
+ "a",
217
+ {
218
+ href: "https://versionpill.com",
219
+ target: "_blank",
220
+ rel: "noopener noreferrer",
221
+ className: (0, import_clsx.default)(styles.branding, isLight ? "text-gray-400" : "text-gray-500"),
222
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Powered by Version Pill" })
223
+ }
224
+ ),
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
226
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
227
+ "a",
228
+ {
229
+ href: `${baseUrl}/${projectId}/roadmap`,
230
+ target: "_blank",
231
+ rel: "noopener noreferrer",
232
+ className: (0, import_clsx.default)(styles.button, styles.buttonSecondary),
233
+ children: "\u{1F4A1} Roadmap"
234
+ }
235
+ ),
236
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
237
+ "a",
238
+ {
239
+ href: `${baseUrl}/${projectId}/changelog`,
240
+ target: "_blank",
241
+ rel: "noopener noreferrer",
242
+ className: (0, import_clsx.default)(styles.button, styles.buttonPrimary),
243
+ style: accentColor ? { backgroundColor: accentColor } : void 0,
244
+ children: "View All"
245
+ }
246
+ )
247
+ ] })
248
+ ] })
249
+ ] })
250
+ ] })
251
+ ] });
252
+ }
253
+ function Changelog({
254
+ projectId,
255
+ baseUrl = DEFAULT_BASE_URL,
256
+ maxHeight = 600,
257
+ theme = "auto",
258
+ className
259
+ }) {
260
+ const resolvedTheme = useTheme(theme);
261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
262
+ "iframe",
263
+ {
264
+ src: `${baseUrl}/embed/${projectId}/changelog?theme=${resolvedTheme}`,
265
+ className: (0, import_clsx.default)(styles.iframe, className),
266
+ style: { height: maxHeight },
267
+ title: "Changelog"
268
+ }
269
+ );
270
+ }
271
+ function Roadmap({
272
+ projectId,
273
+ baseUrl = DEFAULT_BASE_URL,
274
+ maxHeight = 800,
275
+ theme = "auto",
276
+ className
277
+ }) {
278
+ const resolvedTheme = useTheme(theme);
279
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
280
+ "iframe",
281
+ {
282
+ src: `${baseUrl}/embed/${projectId}/roadmap?theme=${resolvedTheme}`,
283
+ className: (0, import_clsx.default)(styles.iframe, className),
284
+ style: { height: maxHeight },
285
+ title: "Roadmap"
286
+ }
287
+ );
288
+ }
289
+ var index_default = VersionPill;
290
+ // Annotate the CommonJS export names for ESM import in node:
291
+ 0 && (module.exports = {
292
+ Changelog,
293
+ Roadmap,
294
+ VersionPill
295
+ });
296
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useEffect, useCallback } from \"react\";\nimport clsx from \"clsx\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface Version {\n id: string;\n version: string;\n type: \"major\" | \"minor\" | \"patch\";\n title: string;\n description?: string;\n emoji?: string;\n features?: string[];\n releaseDate: number;\n isActive: boolean;\n}\n\nexport interface VersionPillConfig {\n /** Your project slug from Version Pill dashboard */\n projectId: string;\n /** Version Pill API base URL (default: https://versionpill.com) */\n baseUrl?: string;\n}\n\nexport interface VersionPillProps extends VersionPillConfig {\n /** Position of the pill */\n position?: \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\" | \"inline\";\n /** Custom class name */\n className?: string;\n /** Theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Show \"Powered by Version Pill\" branding */\n showBranding?: boolean;\n /** Accent color (hex) */\n accentColor?: string;\n /** Callback when a new version is detected */\n onNewVersion?: (version: Version) => void;\n}\n\nexport interface ChangelogProps extends VersionPillConfig {\n /** Max height in pixels */\n maxHeight?: number;\n /** Theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Custom class name */\n className?: string;\n}\n\nexport interface RoadmapProps extends VersionPillConfig {\n /** Max height in pixels */\n maxHeight?: number;\n /** Theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Custom class name */\n className?: string;\n}\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_BASE_URL = \"https://versionpill.com\";\n\n// =============================================================================\n// STYLES\n// =============================================================================\n\nconst styles = {\n pill: `\n inline-flex items-center gap-1.5 px-2.5 py-1\n text-xs font-medium rounded-full cursor-pointer\n transition-all duration-200 select-none\n border shadow-sm hover:shadow-md\n `,\n pillLight: `bg-white text-gray-700 border-gray-200 hover:border-gray-300`,\n pillDark: `bg-gray-900 text-gray-100 border-gray-700 hover:border-gray-600`,\n\n modal: `fixed inset-0 z-[99999] flex items-center justify-center p-4`,\n backdrop: `absolute inset-0 bg-black/50 backdrop-blur-sm`,\n panel: `relative w-full max-w-md max-h-[80vh] overflow-hidden rounded-xl shadow-2xl`,\n panelLight: `bg-white`,\n panelDark: `bg-gray-900`,\n\n header: `p-4 border-b flex items-center justify-between`,\n headerLight: `border-gray-100`,\n headerDark: `border-gray-800`,\n\n content: `p-4 overflow-y-auto max-h-[60vh]`,\n\n version: `mb-4 last:mb-0`,\n versionHeader: `flex items-center gap-2 mb-2`,\n versionTitle: `font-semibold`,\n versionBadge: `px-1.5 py-0.5 text-[10px] font-medium rounded`,\n versionFeatures: `space-y-1 mt-2`,\n versionFeature: `flex items-start gap-2 text-sm`,\n\n footer: `p-3 border-t flex items-center justify-between`,\n footerLight: `border-gray-100 bg-gray-50`,\n footerDark: `border-gray-800 bg-gray-950`,\n\n button: `px-3 py-1.5 text-xs font-medium rounded-lg transition-colors`,\n buttonPrimary: `bg-blue-600 text-white hover:bg-blue-700`,\n buttonSecondary: `text-gray-500 hover:text-gray-700`,\n\n branding: `text-[10px] opacity-40 flex items-center gap-1`,\n newDot: `w-2 h-2 rounded-full bg-green-500 animate-pulse`,\n\n iframe: `w-full border-0`,\n};\n\n// =============================================================================\n// HOOKS\n// =============================================================================\n\nfunction useTheme(theme: \"light\" | \"dark\" | \"auto\"): \"light\" | \"dark\" {\n const [resolved, setResolved] = useState<\"light\" | \"dark\">(\"light\");\n\n useEffect(() => {\n if (theme === \"auto\") {\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n setResolved(isDark ? \"dark\" : \"light\");\n\n const listener = (e: MediaQueryListEvent) => setResolved(e.matches ? \"dark\" : \"light\");\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n mq.addEventListener(\"change\", listener);\n return () => mq.removeEventListener(\"change\", listener);\n } else {\n setResolved(theme);\n }\n }, [theme]);\n\n return resolved;\n}\n\n// =============================================================================\n// VERSION PILL COMPONENT\n// =============================================================================\n\nexport function VersionPill({\n projectId,\n baseUrl = DEFAULT_BASE_URL,\n position = \"inline\",\n className,\n theme = \"auto\",\n showBranding = true,\n accentColor,\n onNewVersion,\n}: VersionPillProps) {\n const [versions, setVersions] = useState<Version[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [hasNewVersion, setHasNewVersion] = useState(false);\n\n const resolvedTheme = useTheme(theme);\n const isLight = resolvedTheme === \"light\";\n\n const fetchChangelog = useCallback(async () => {\n try {\n const response = await fetch(`${baseUrl}/api/changelog/${projectId}?limit=10`);\n if (!response.ok) throw new Error(\"Failed to fetch changelog\");\n\n const data = await response.json();\n setVersions(data);\n\n const storedVersion = localStorage.getItem(`vp_${projectId}_seen`);\n if (data.length > 0 && data[0].version !== storedVersion) {\n setHasNewVersion(true);\n onNewVersion?.(data[0]);\n }\n } catch (err: any) {\n setError(err.message);\n } finally {\n setLoading(false);\n }\n }, [projectId, baseUrl, onNewVersion]);\n\n useEffect(() => {\n fetchChangelog();\n }, [fetchChangelog]);\n\n const handleOpen = () => {\n setIsOpen(true);\n if (versions.length > 0) {\n localStorage.setItem(`vp_${projectId}_seen`, versions[0].version);\n setHasNewVersion(false);\n }\n };\n\n const currentVersion = versions[0];\n\n const positionStyles: Record<string, string> = {\n \"top-left\": \"fixed top-4 left-4 z-[9999]\",\n \"top-right\": \"fixed top-4 right-4 z-[9999]\",\n \"bottom-left\": \"fixed bottom-4 left-4 z-[9999]\",\n \"bottom-right\": \"fixed bottom-4 right-4 z-[9999]\",\n inline: \"\",\n };\n\n if (loading) {\n return (\n <div className={clsx(positionStyles[position], className)}>\n <div className={clsx(styles.pill, isLight ? styles.pillLight : styles.pillDark)}>\n <span className=\"opacity-50\">...</span>\n </div>\n </div>\n );\n }\n\n if (error || !currentVersion) return null;\n\n return (\n <>\n <div className={clsx(positionStyles[position], className)}>\n <button\n onClick={handleOpen}\n className={clsx(styles.pill, isLight ? styles.pillLight : styles.pillDark)}\n style={accentColor ? { borderColor: accentColor } : undefined}\n >\n {hasNewVersion && <span className={styles.newDot} />}\n <span>v{currentVersion.version}</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n </button>\n </div>\n\n {isOpen && (\n <div className={styles.modal}>\n <div className={styles.backdrop} onClick={() => setIsOpen(false)} />\n <div className={clsx(styles.panel, isLight ? styles.panelLight : styles.panelDark)}>\n <div className={clsx(styles.header, isLight ? styles.headerLight : styles.headerDark)}>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-lg\">{currentVersion.emoji || \"✨\"}</span>\n <div>\n <h2 className={clsx(\"font-semibold\", isLight ? \"text-gray-900\" : \"text-white\")}>What's New</h2>\n <p className={clsx(\"text-xs\", isLight ? \"text-gray-500\" : \"text-gray-400\")}>\n Latest updates\n </p>\n </div>\n </div>\n <button\n onClick={() => setIsOpen(false)}\n className={clsx(\"p-1 rounded hover:bg-gray-100\", !isLight && \"hover:bg-gray-800\")}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className={styles.content}>\n {versions.slice(0, 5).map((version) => (\n <div key={version.id} className={styles.version}>\n <div className={styles.versionHeader}>\n <span className=\"text-lg\">{version.emoji || \"📦\"}</span>\n <span className={clsx(styles.versionTitle, isLight ? \"text-gray-900\" : \"text-white\")}>\n {version.title}\n </span>\n <span\n className={clsx(\n styles.versionBadge,\n version.type === \"major\" ? \"bg-purple-100 text-purple-700\" :\n version.type === \"minor\" ? \"bg-blue-100 text-blue-700\" :\n \"bg-gray-100 text-gray-700\"\n )}\n >\n {version.type}\n </span>\n </div>\n <div className={clsx(\"text-xs mb-2\", isLight ? \"text-gray-500\" : \"text-gray-400\")}>\n v{version.version} · {new Date(version.releaseDate).toLocaleDateString()}\n </div>\n {version.description && (\n <p className={clsx(\"text-sm mb-2\", isLight ? \"text-gray-600\" : \"text-gray-300\")}>\n {version.description}\n </p>\n )}\n {version.features && version.features.length > 0 && (\n <ul className={styles.versionFeatures}>\n {version.features.map((feature, i) => (\n <li key={i} className={clsx(styles.versionFeature, isLight ? \"text-gray-600\" : \"text-gray-300\")}>\n <span className=\"text-green-500 mt-0.5\">✓</span>\n {feature}\n </li>\n ))}\n </ul>\n )}\n </div>\n ))}\n </div>\n\n <div className={clsx(styles.footer, isLight ? styles.footerLight : styles.footerDark)}>\n {showBranding && (\n <a\n href=\"https://versionpill.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(styles.branding, isLight ? \"text-gray-400\" : \"text-gray-500\")}\n >\n <span>Powered by Version Pill</span>\n </a>\n )}\n <div className=\"flex items-center gap-2\">\n <a\n href={`${baseUrl}/${projectId}/roadmap`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(styles.button, styles.buttonSecondary)}\n >\n 💡 Roadmap\n </a>\n <a\n href={`${baseUrl}/${projectId}/changelog`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(styles.button, styles.buttonPrimary)}\n style={accentColor ? { backgroundColor: accentColor } : undefined}\n >\n View All\n </a>\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\n// =============================================================================\n// CHANGELOG EMBED COMPONENT\n// =============================================================================\n\nexport function Changelog({\n projectId,\n baseUrl = DEFAULT_BASE_URL,\n maxHeight = 600,\n theme = \"auto\",\n className,\n}: ChangelogProps) {\n const resolvedTheme = useTheme(theme);\n\n return (\n <iframe\n src={`${baseUrl}/embed/${projectId}/changelog?theme=${resolvedTheme}`}\n className={clsx(styles.iframe, className)}\n style={{ height: maxHeight }}\n title=\"Changelog\"\n />\n );\n}\n\n// =============================================================================\n// ROADMAP EMBED COMPONENT\n// =============================================================================\n\nexport function Roadmap({\n projectId,\n baseUrl = DEFAULT_BASE_URL,\n maxHeight = 800,\n theme = \"auto\",\n className,\n}: RoadmapProps) {\n const resolvedTheme = useTheme(theme);\n\n return (\n <iframe\n src={`${baseUrl}/embed/${projectId}/roadmap?theme=${resolvedTheme}`}\n className={clsx(styles.iframe, className)}\n style={{ height: maxHeight }}\n title=\"Roadmap\"\n />\n );\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport default VersionPill;\n"],"mappings":"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","names":["clsx"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,260 @@
1
+ "use client";
2
+
3
+ // src/index.tsx
4
+ import { useState, useEffect, useCallback } from "react";
5
+ import clsx from "clsx";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ var DEFAULT_BASE_URL = "https://versionpill.com";
8
+ var styles = {
9
+ pill: `
10
+ inline-flex items-center gap-1.5 px-2.5 py-1
11
+ text-xs font-medium rounded-full cursor-pointer
12
+ transition-all duration-200 select-none
13
+ border shadow-sm hover:shadow-md
14
+ `,
15
+ pillLight: `bg-white text-gray-700 border-gray-200 hover:border-gray-300`,
16
+ pillDark: `bg-gray-900 text-gray-100 border-gray-700 hover:border-gray-600`,
17
+ modal: `fixed inset-0 z-[99999] flex items-center justify-center p-4`,
18
+ backdrop: `absolute inset-0 bg-black/50 backdrop-blur-sm`,
19
+ panel: `relative w-full max-w-md max-h-[80vh] overflow-hidden rounded-xl shadow-2xl`,
20
+ panelLight: `bg-white`,
21
+ panelDark: `bg-gray-900`,
22
+ header: `p-4 border-b flex items-center justify-between`,
23
+ headerLight: `border-gray-100`,
24
+ headerDark: `border-gray-800`,
25
+ content: `p-4 overflow-y-auto max-h-[60vh]`,
26
+ version: `mb-4 last:mb-0`,
27
+ versionHeader: `flex items-center gap-2 mb-2`,
28
+ versionTitle: `font-semibold`,
29
+ versionBadge: `px-1.5 py-0.5 text-[10px] font-medium rounded`,
30
+ versionFeatures: `space-y-1 mt-2`,
31
+ versionFeature: `flex items-start gap-2 text-sm`,
32
+ footer: `p-3 border-t flex items-center justify-between`,
33
+ footerLight: `border-gray-100 bg-gray-50`,
34
+ footerDark: `border-gray-800 bg-gray-950`,
35
+ button: `px-3 py-1.5 text-xs font-medium rounded-lg transition-colors`,
36
+ buttonPrimary: `bg-blue-600 text-white hover:bg-blue-700`,
37
+ buttonSecondary: `text-gray-500 hover:text-gray-700`,
38
+ branding: `text-[10px] opacity-40 flex items-center gap-1`,
39
+ newDot: `w-2 h-2 rounded-full bg-green-500 animate-pulse`,
40
+ iframe: `w-full border-0`
41
+ };
42
+ function useTheme(theme) {
43
+ const [resolved, setResolved] = useState("light");
44
+ useEffect(() => {
45
+ if (theme === "auto") {
46
+ const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
47
+ setResolved(isDark ? "dark" : "light");
48
+ const listener = (e) => setResolved(e.matches ? "dark" : "light");
49
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
50
+ mq.addEventListener("change", listener);
51
+ return () => mq.removeEventListener("change", listener);
52
+ } else {
53
+ setResolved(theme);
54
+ }
55
+ }, [theme]);
56
+ return resolved;
57
+ }
58
+ function VersionPill({
59
+ projectId,
60
+ baseUrl = DEFAULT_BASE_URL,
61
+ position = "inline",
62
+ className,
63
+ theme = "auto",
64
+ showBranding = true,
65
+ accentColor,
66
+ onNewVersion
67
+ }) {
68
+ const [versions, setVersions] = useState([]);
69
+ const [loading, setLoading] = useState(true);
70
+ const [error, setError] = useState(null);
71
+ const [isOpen, setIsOpen] = useState(false);
72
+ const [hasNewVersion, setHasNewVersion] = useState(false);
73
+ const resolvedTheme = useTheme(theme);
74
+ const isLight = resolvedTheme === "light";
75
+ const fetchChangelog = useCallback(async () => {
76
+ try {
77
+ const response = await fetch(`${baseUrl}/api/changelog/${projectId}?limit=10`);
78
+ if (!response.ok) throw new Error("Failed to fetch changelog");
79
+ const data = await response.json();
80
+ setVersions(data);
81
+ const storedVersion = localStorage.getItem(`vp_${projectId}_seen`);
82
+ if (data.length > 0 && data[0].version !== storedVersion) {
83
+ setHasNewVersion(true);
84
+ onNewVersion?.(data[0]);
85
+ }
86
+ } catch (err) {
87
+ setError(err.message);
88
+ } finally {
89
+ setLoading(false);
90
+ }
91
+ }, [projectId, baseUrl, onNewVersion]);
92
+ useEffect(() => {
93
+ fetchChangelog();
94
+ }, [fetchChangelog]);
95
+ const handleOpen = () => {
96
+ setIsOpen(true);
97
+ if (versions.length > 0) {
98
+ localStorage.setItem(`vp_${projectId}_seen`, versions[0].version);
99
+ setHasNewVersion(false);
100
+ }
101
+ };
102
+ const currentVersion = versions[0];
103
+ const positionStyles = {
104
+ "top-left": "fixed top-4 left-4 z-[9999]",
105
+ "top-right": "fixed top-4 right-4 z-[9999]",
106
+ "bottom-left": "fixed bottom-4 left-4 z-[9999]",
107
+ "bottom-right": "fixed bottom-4 right-4 z-[9999]",
108
+ inline: ""
109
+ };
110
+ if (loading) {
111
+ return /* @__PURE__ */ jsx("div", { className: clsx(positionStyles[position], className), children: /* @__PURE__ */ jsx("div", { className: clsx(styles.pill, isLight ? styles.pillLight : styles.pillDark), children: /* @__PURE__ */ jsx("span", { className: "opacity-50", children: "..." }) }) });
112
+ }
113
+ if (error || !currentVersion) return null;
114
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
115
+ /* @__PURE__ */ jsx("div", { className: clsx(positionStyles[position], className), children: /* @__PURE__ */ jsxs(
116
+ "button",
117
+ {
118
+ onClick: handleOpen,
119
+ className: clsx(styles.pill, isLight ? styles.pillLight : styles.pillDark),
120
+ style: accentColor ? { borderColor: accentColor } : void 0,
121
+ children: [
122
+ hasNewVersion && /* @__PURE__ */ jsx("span", { className: styles.newDot }),
123
+ /* @__PURE__ */ jsxs("span", { children: [
124
+ "v",
125
+ currentVersion.version
126
+ ] }),
127
+ /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" }) })
128
+ ]
129
+ }
130
+ ) }),
131
+ isOpen && /* @__PURE__ */ jsxs("div", { className: styles.modal, children: [
132
+ /* @__PURE__ */ jsx("div", { className: styles.backdrop, onClick: () => setIsOpen(false) }),
133
+ /* @__PURE__ */ jsxs("div", { className: clsx(styles.panel, isLight ? styles.panelLight : styles.panelDark), children: [
134
+ /* @__PURE__ */ jsxs("div", { className: clsx(styles.header, isLight ? styles.headerLight : styles.headerDark), children: [
135
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
136
+ /* @__PURE__ */ jsx("span", { className: "text-lg", children: currentVersion.emoji || "\u2728" }),
137
+ /* @__PURE__ */ jsxs("div", { children: [
138
+ /* @__PURE__ */ jsx("h2", { className: clsx("font-semibold", isLight ? "text-gray-900" : "text-white"), children: "What's New" }),
139
+ /* @__PURE__ */ jsx("p", { className: clsx("text-xs", isLight ? "text-gray-500" : "text-gray-400"), children: "Latest updates" })
140
+ ] })
141
+ ] }),
142
+ /* @__PURE__ */ jsx(
143
+ "button",
144
+ {
145
+ onClick: () => setIsOpen(false),
146
+ className: clsx("p-1 rounded hover:bg-gray-100", !isLight && "hover:bg-gray-800"),
147
+ children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" }) })
148
+ }
149
+ )
150
+ ] }),
151
+ /* @__PURE__ */ jsx("div", { className: styles.content, children: versions.slice(0, 5).map((version) => /* @__PURE__ */ jsxs("div", { className: styles.version, children: [
152
+ /* @__PURE__ */ jsxs("div", { className: styles.versionHeader, children: [
153
+ /* @__PURE__ */ jsx("span", { className: "text-lg", children: version.emoji || "\u{1F4E6}" }),
154
+ /* @__PURE__ */ jsx("span", { className: clsx(styles.versionTitle, isLight ? "text-gray-900" : "text-white"), children: version.title }),
155
+ /* @__PURE__ */ jsx(
156
+ "span",
157
+ {
158
+ className: clsx(
159
+ styles.versionBadge,
160
+ version.type === "major" ? "bg-purple-100 text-purple-700" : version.type === "minor" ? "bg-blue-100 text-blue-700" : "bg-gray-100 text-gray-700"
161
+ ),
162
+ children: version.type
163
+ }
164
+ )
165
+ ] }),
166
+ /* @__PURE__ */ jsxs("div", { className: clsx("text-xs mb-2", isLight ? "text-gray-500" : "text-gray-400"), children: [
167
+ "v",
168
+ version.version,
169
+ " \xB7 ",
170
+ new Date(version.releaseDate).toLocaleDateString()
171
+ ] }),
172
+ version.description && /* @__PURE__ */ jsx("p", { className: clsx("text-sm mb-2", isLight ? "text-gray-600" : "text-gray-300"), children: version.description }),
173
+ version.features && version.features.length > 0 && /* @__PURE__ */ jsx("ul", { className: styles.versionFeatures, children: version.features.map((feature, i) => /* @__PURE__ */ jsxs("li", { className: clsx(styles.versionFeature, isLight ? "text-gray-600" : "text-gray-300"), children: [
174
+ /* @__PURE__ */ jsx("span", { className: "text-green-500 mt-0.5", children: "\u2713" }),
175
+ feature
176
+ ] }, i)) })
177
+ ] }, version.id)) }),
178
+ /* @__PURE__ */ jsxs("div", { className: clsx(styles.footer, isLight ? styles.footerLight : styles.footerDark), children: [
179
+ showBranding && /* @__PURE__ */ jsx(
180
+ "a",
181
+ {
182
+ href: "https://versionpill.com",
183
+ target: "_blank",
184
+ rel: "noopener noreferrer",
185
+ className: clsx(styles.branding, isLight ? "text-gray-400" : "text-gray-500"),
186
+ children: /* @__PURE__ */ jsx("span", { children: "Powered by Version Pill" })
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
190
+ /* @__PURE__ */ jsx(
191
+ "a",
192
+ {
193
+ href: `${baseUrl}/${projectId}/roadmap`,
194
+ target: "_blank",
195
+ rel: "noopener noreferrer",
196
+ className: clsx(styles.button, styles.buttonSecondary),
197
+ children: "\u{1F4A1} Roadmap"
198
+ }
199
+ ),
200
+ /* @__PURE__ */ jsx(
201
+ "a",
202
+ {
203
+ href: `${baseUrl}/${projectId}/changelog`,
204
+ target: "_blank",
205
+ rel: "noopener noreferrer",
206
+ className: clsx(styles.button, styles.buttonPrimary),
207
+ style: accentColor ? { backgroundColor: accentColor } : void 0,
208
+ children: "View All"
209
+ }
210
+ )
211
+ ] })
212
+ ] })
213
+ ] })
214
+ ] })
215
+ ] });
216
+ }
217
+ function Changelog({
218
+ projectId,
219
+ baseUrl = DEFAULT_BASE_URL,
220
+ maxHeight = 600,
221
+ theme = "auto",
222
+ className
223
+ }) {
224
+ const resolvedTheme = useTheme(theme);
225
+ return /* @__PURE__ */ jsx(
226
+ "iframe",
227
+ {
228
+ src: `${baseUrl}/embed/${projectId}/changelog?theme=${resolvedTheme}`,
229
+ className: clsx(styles.iframe, className),
230
+ style: { height: maxHeight },
231
+ title: "Changelog"
232
+ }
233
+ );
234
+ }
235
+ function Roadmap({
236
+ projectId,
237
+ baseUrl = DEFAULT_BASE_URL,
238
+ maxHeight = 800,
239
+ theme = "auto",
240
+ className
241
+ }) {
242
+ const resolvedTheme = useTheme(theme);
243
+ return /* @__PURE__ */ jsx(
244
+ "iframe",
245
+ {
246
+ src: `${baseUrl}/embed/${projectId}/roadmap?theme=${resolvedTheme}`,
247
+ className: clsx(styles.iframe, className),
248
+ style: { height: maxHeight },
249
+ title: "Roadmap"
250
+ }
251
+ );
252
+ }
253
+ var index_default = VersionPill;
254
+ export {
255
+ Changelog,
256
+ Roadmap,
257
+ VersionPill,
258
+ index_default as default
259
+ };
260
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useEffect, useCallback } from \"react\";\nimport clsx from \"clsx\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface Version {\n id: string;\n version: string;\n type: \"major\" | \"minor\" | \"patch\";\n title: string;\n description?: string;\n emoji?: string;\n features?: string[];\n releaseDate: number;\n isActive: boolean;\n}\n\nexport interface VersionPillConfig {\n /** Your project slug from Version Pill dashboard */\n projectId: string;\n /** Version Pill API base URL (default: https://versionpill.com) */\n baseUrl?: string;\n}\n\nexport interface VersionPillProps extends VersionPillConfig {\n /** Position of the pill */\n position?: \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\" | \"inline\";\n /** Custom class name */\n className?: string;\n /** Theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Show \"Powered by Version Pill\" branding */\n showBranding?: boolean;\n /** Accent color (hex) */\n accentColor?: string;\n /** Callback when a new version is detected */\n onNewVersion?: (version: Version) => void;\n}\n\nexport interface ChangelogProps extends VersionPillConfig {\n /** Max height in pixels */\n maxHeight?: number;\n /** Theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Custom class name */\n className?: string;\n}\n\nexport interface RoadmapProps extends VersionPillConfig {\n /** Max height in pixels */\n maxHeight?: number;\n /** Theme */\n theme?: \"light\" | \"dark\" | \"auto\";\n /** Custom class name */\n className?: string;\n}\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst DEFAULT_BASE_URL = \"https://versionpill.com\";\n\n// =============================================================================\n// STYLES\n// =============================================================================\n\nconst styles = {\n pill: `\n inline-flex items-center gap-1.5 px-2.5 py-1\n text-xs font-medium rounded-full cursor-pointer\n transition-all duration-200 select-none\n border shadow-sm hover:shadow-md\n `,\n pillLight: `bg-white text-gray-700 border-gray-200 hover:border-gray-300`,\n pillDark: `bg-gray-900 text-gray-100 border-gray-700 hover:border-gray-600`,\n\n modal: `fixed inset-0 z-[99999] flex items-center justify-center p-4`,\n backdrop: `absolute inset-0 bg-black/50 backdrop-blur-sm`,\n panel: `relative w-full max-w-md max-h-[80vh] overflow-hidden rounded-xl shadow-2xl`,\n panelLight: `bg-white`,\n panelDark: `bg-gray-900`,\n\n header: `p-4 border-b flex items-center justify-between`,\n headerLight: `border-gray-100`,\n headerDark: `border-gray-800`,\n\n content: `p-4 overflow-y-auto max-h-[60vh]`,\n\n version: `mb-4 last:mb-0`,\n versionHeader: `flex items-center gap-2 mb-2`,\n versionTitle: `font-semibold`,\n versionBadge: `px-1.5 py-0.5 text-[10px] font-medium rounded`,\n versionFeatures: `space-y-1 mt-2`,\n versionFeature: `flex items-start gap-2 text-sm`,\n\n footer: `p-3 border-t flex items-center justify-between`,\n footerLight: `border-gray-100 bg-gray-50`,\n footerDark: `border-gray-800 bg-gray-950`,\n\n button: `px-3 py-1.5 text-xs font-medium rounded-lg transition-colors`,\n buttonPrimary: `bg-blue-600 text-white hover:bg-blue-700`,\n buttonSecondary: `text-gray-500 hover:text-gray-700`,\n\n branding: `text-[10px] opacity-40 flex items-center gap-1`,\n newDot: `w-2 h-2 rounded-full bg-green-500 animate-pulse`,\n\n iframe: `w-full border-0`,\n};\n\n// =============================================================================\n// HOOKS\n// =============================================================================\n\nfunction useTheme(theme: \"light\" | \"dark\" | \"auto\"): \"light\" | \"dark\" {\n const [resolved, setResolved] = useState<\"light\" | \"dark\">(\"light\");\n\n useEffect(() => {\n if (theme === \"auto\") {\n const isDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n setResolved(isDark ? \"dark\" : \"light\");\n\n const listener = (e: MediaQueryListEvent) => setResolved(e.matches ? \"dark\" : \"light\");\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n mq.addEventListener(\"change\", listener);\n return () => mq.removeEventListener(\"change\", listener);\n } else {\n setResolved(theme);\n }\n }, [theme]);\n\n return resolved;\n}\n\n// =============================================================================\n// VERSION PILL COMPONENT\n// =============================================================================\n\nexport function VersionPill({\n projectId,\n baseUrl = DEFAULT_BASE_URL,\n position = \"inline\",\n className,\n theme = \"auto\",\n showBranding = true,\n accentColor,\n onNewVersion,\n}: VersionPillProps) {\n const [versions, setVersions] = useState<Version[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [hasNewVersion, setHasNewVersion] = useState(false);\n\n const resolvedTheme = useTheme(theme);\n const isLight = resolvedTheme === \"light\";\n\n const fetchChangelog = useCallback(async () => {\n try {\n const response = await fetch(`${baseUrl}/api/changelog/${projectId}?limit=10`);\n if (!response.ok) throw new Error(\"Failed to fetch changelog\");\n\n const data = await response.json();\n setVersions(data);\n\n const storedVersion = localStorage.getItem(`vp_${projectId}_seen`);\n if (data.length > 0 && data[0].version !== storedVersion) {\n setHasNewVersion(true);\n onNewVersion?.(data[0]);\n }\n } catch (err: any) {\n setError(err.message);\n } finally {\n setLoading(false);\n }\n }, [projectId, baseUrl, onNewVersion]);\n\n useEffect(() => {\n fetchChangelog();\n }, [fetchChangelog]);\n\n const handleOpen = () => {\n setIsOpen(true);\n if (versions.length > 0) {\n localStorage.setItem(`vp_${projectId}_seen`, versions[0].version);\n setHasNewVersion(false);\n }\n };\n\n const currentVersion = versions[0];\n\n const positionStyles: Record<string, string> = {\n \"top-left\": \"fixed top-4 left-4 z-[9999]\",\n \"top-right\": \"fixed top-4 right-4 z-[9999]\",\n \"bottom-left\": \"fixed bottom-4 left-4 z-[9999]\",\n \"bottom-right\": \"fixed bottom-4 right-4 z-[9999]\",\n inline: \"\",\n };\n\n if (loading) {\n return (\n <div className={clsx(positionStyles[position], className)}>\n <div className={clsx(styles.pill, isLight ? styles.pillLight : styles.pillDark)}>\n <span className=\"opacity-50\">...</span>\n </div>\n </div>\n );\n }\n\n if (error || !currentVersion) return null;\n\n return (\n <>\n <div className={clsx(positionStyles[position], className)}>\n <button\n onClick={handleOpen}\n className={clsx(styles.pill, isLight ? styles.pillLight : styles.pillDark)}\n style={accentColor ? { borderColor: accentColor } : undefined}\n >\n {hasNewVersion && <span className={styles.newDot} />}\n <span>v{currentVersion.version}</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M6 9l6 6 6-6\" />\n </svg>\n </button>\n </div>\n\n {isOpen && (\n <div className={styles.modal}>\n <div className={styles.backdrop} onClick={() => setIsOpen(false)} />\n <div className={clsx(styles.panel, isLight ? styles.panelLight : styles.panelDark)}>\n <div className={clsx(styles.header, isLight ? styles.headerLight : styles.headerDark)}>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-lg\">{currentVersion.emoji || \"✨\"}</span>\n <div>\n <h2 className={clsx(\"font-semibold\", isLight ? \"text-gray-900\" : \"text-white\")}>What's New</h2>\n <p className={clsx(\"text-xs\", isLight ? \"text-gray-500\" : \"text-gray-400\")}>\n Latest updates\n </p>\n </div>\n </div>\n <button\n onClick={() => setIsOpen(false)}\n className={clsx(\"p-1 rounded hover:bg-gray-100\", !isLight && \"hover:bg-gray-800\")}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M18 6L6 18M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className={styles.content}>\n {versions.slice(0, 5).map((version) => (\n <div key={version.id} className={styles.version}>\n <div className={styles.versionHeader}>\n <span className=\"text-lg\">{version.emoji || \"📦\"}</span>\n <span className={clsx(styles.versionTitle, isLight ? \"text-gray-900\" : \"text-white\")}>\n {version.title}\n </span>\n <span\n className={clsx(\n styles.versionBadge,\n version.type === \"major\" ? \"bg-purple-100 text-purple-700\" :\n version.type === \"minor\" ? \"bg-blue-100 text-blue-700\" :\n \"bg-gray-100 text-gray-700\"\n )}\n >\n {version.type}\n </span>\n </div>\n <div className={clsx(\"text-xs mb-2\", isLight ? \"text-gray-500\" : \"text-gray-400\")}>\n v{version.version} · {new Date(version.releaseDate).toLocaleDateString()}\n </div>\n {version.description && (\n <p className={clsx(\"text-sm mb-2\", isLight ? \"text-gray-600\" : \"text-gray-300\")}>\n {version.description}\n </p>\n )}\n {version.features && version.features.length > 0 && (\n <ul className={styles.versionFeatures}>\n {version.features.map((feature, i) => (\n <li key={i} className={clsx(styles.versionFeature, isLight ? \"text-gray-600\" : \"text-gray-300\")}>\n <span className=\"text-green-500 mt-0.5\">✓</span>\n {feature}\n </li>\n ))}\n </ul>\n )}\n </div>\n ))}\n </div>\n\n <div className={clsx(styles.footer, isLight ? styles.footerLight : styles.footerDark)}>\n {showBranding && (\n <a\n href=\"https://versionpill.com\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(styles.branding, isLight ? \"text-gray-400\" : \"text-gray-500\")}\n >\n <span>Powered by Version Pill</span>\n </a>\n )}\n <div className=\"flex items-center gap-2\">\n <a\n href={`${baseUrl}/${projectId}/roadmap`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(styles.button, styles.buttonSecondary)}\n >\n 💡 Roadmap\n </a>\n <a\n href={`${baseUrl}/${projectId}/changelog`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={clsx(styles.button, styles.buttonPrimary)}\n style={accentColor ? { backgroundColor: accentColor } : undefined}\n >\n View All\n </a>\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\n// =============================================================================\n// CHANGELOG EMBED COMPONENT\n// =============================================================================\n\nexport function Changelog({\n projectId,\n baseUrl = DEFAULT_BASE_URL,\n maxHeight = 600,\n theme = \"auto\",\n className,\n}: ChangelogProps) {\n const resolvedTheme = useTheme(theme);\n\n return (\n <iframe\n src={`${baseUrl}/embed/${projectId}/changelog?theme=${resolvedTheme}`}\n className={clsx(styles.iframe, className)}\n style={{ height: maxHeight }}\n title=\"Changelog\"\n />\n );\n}\n\n// =============================================================================\n// ROADMAP EMBED COMPONENT\n// =============================================================================\n\nexport function Roadmap({\n projectId,\n baseUrl = DEFAULT_BASE_URL,\n maxHeight = 800,\n theme = \"auto\",\n className,\n}: RoadmapProps) {\n const resolvedTheme = useTheme(theme);\n\n return (\n <iframe\n src={`${baseUrl}/embed/${projectId}/roadmap?theme=${resolvedTheme}`}\n className={clsx(styles.iframe, className)}\n style={{ height: maxHeight }}\n title=\"Roadmap\"\n />\n );\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport default VersionPill;\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "version-pill-react",
3
+ "version": "1.0.0",
4
+ "description": "React components for Version Pill - embed changelog, roadmap & feedback widgets in your app",
5
+ "author": "Jimmy Harika <jimmy@jimmyharika.com>",
6
+ "homepage": "https://versionpill.com",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/jimmyharika/convex-logbook"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/jimmyharika/convex-logbook/issues"
13
+ },
14
+ "main": "./dist/index.js",
15
+ "module": "./dist/index.mjs",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.mjs",
21
+ "require": "./dist/index.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "sideEffects": false,
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "typecheck": "tsc --noEmit",
32
+ "prepublishOnly": "bun run build"
33
+ },
34
+ "peerDependencies": {
35
+ "react": ">=18.0.0",
36
+ "react-dom": ">=18.0.0"
37
+ },
38
+ "dependencies": {
39
+ "clsx": "^2.1.1"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^19.2.7",
43
+ "@types/react-dom": "^19.2.3",
44
+ "react": "^19.2.1",
45
+ "react-dom": "^19.2.1",
46
+ "tsup": "^8.0.0",
47
+ "typescript": "^5.9.3"
48
+ },
49
+ "keywords": [
50
+ "changelog",
51
+ "version",
52
+ "widget",
53
+ "react",
54
+ "roadmap",
55
+ "feedback",
56
+ "version-pill",
57
+ "versionpill",
58
+ "release-notes",
59
+ "whats-new",
60
+ "product-updates",
61
+ "feature-requests"
62
+ ],
63
+ "license": "MIT",
64
+ "publishConfig": {
65
+ "access": "public"
66
+ }
67
+ }