@moontra/moonui-pro 2.1.0 → 2.1.1
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/dist/index.d.ts +790 -0
- package/dist/index.mjs +758 -326
- package/package.json +2 -3
- package/scripts/postinstall.js +144 -66
- package/src/components/advanced-chart/index.tsx +3 -3
- package/src/components/advanced-forms/index.tsx +4 -9
- package/src/components/animated-button/index.tsx +4 -8
- package/src/components/calendar/index.tsx +2 -28
- package/src/components/color-picker/index.tsx +2 -4
- package/src/components/dashboard/index.tsx +3 -3
- package/src/components/data-table/index.tsx +5 -8
- package/src/components/enhanced/badge.tsx +191 -0
- package/src/components/enhanced/button.tsx +7 -5
- package/src/components/enhanced/card.tsx +11 -17
- package/src/components/enhanced/dialog.tsx +26 -28
- package/src/components/enhanced/index.ts +2 -1
- package/src/components/error-boundary/index.tsx +2 -4
- package/src/components/file-upload/index.tsx +3 -5
- package/src/components/floating-action-button/index.tsx +1 -4
- package/src/components/github-stars/index.tsx +4 -6
- package/src/components/health-check/index.tsx +5 -7
- package/src/components/hover-card-3d/index.tsx +3 -6
- package/src/components/kanban/index.tsx +4 -6
- package/src/components/lazy-component/index.tsx +4 -7
- package/src/components/magnetic-button/index.tsx +3 -6
- package/src/components/memory-efficient-data/index.tsx +6 -6
- package/src/components/optimized-image/index.tsx +4 -6
- package/src/components/performance-debugger/index.tsx +8 -10
- package/src/components/performance-monitor/index.tsx +37 -18
- package/src/components/pinch-zoom/index.tsx +2 -4
- package/src/components/rich-text-editor/index-old-backup.tsx +3 -9
- package/src/components/rich-text-editor/index.tsx +75 -10
- package/src/components/rich-text-editor/slash-commands-extension.ts +1 -1
- package/src/components/spotlight-card/index.tsx +1 -4
- package/src/components/swipeable-card/index.tsx +1 -1
- package/src/components/timeline/index.tsx +2 -2
- package/src/components/ui/color-picker.tsx +1 -1
- package/src/components/ui/index.ts +2 -0
- package/src/components/ui/progress.tsx +27 -0
- package/src/components/ui/skeleton.tsx +17 -0
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/components/virtual-list/index.tsx +3 -4
- package/src/hooks/use-chart.ts +2 -2
- package/src/index.ts +0 -3
- package/src/patterns/login-form/types.ts +6 -6
- package/src/use-pro-access.ts +2 -2
- package/src/utils/chart-helpers.ts +1 -1
- package/src/utils/license-guard.tsx +0 -177
- package/src/utils/package-guard.ts +0 -60
|
@@ -97,28 +97,77 @@ import { toast } from '../ui/toast';
|
|
|
97
97
|
// Note: AI providers should be handled by consuming application
|
|
98
98
|
|
|
99
99
|
// Type definitions for AI functionality
|
|
100
|
-
type AIProvider = 'openai' | 'anthropic' | 'gemini'
|
|
100
|
+
type AIProvider = 'openai' | 'anthropic' | 'gemini' | 'claude' | 'cohere'
|
|
101
101
|
|
|
102
102
|
interface AISettingsType {
|
|
103
103
|
provider: AIProvider
|
|
104
104
|
apiKey?: string
|
|
105
105
|
model?: string
|
|
106
|
+
temperature?: number
|
|
106
107
|
maxTokens?: number
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
interface SlashCommand {
|
|
110
|
-
|
|
111
|
+
id?: string
|
|
112
|
+
title?: string
|
|
111
113
|
description: string
|
|
112
114
|
command: string
|
|
113
|
-
|
|
115
|
+
icon?: string
|
|
116
|
+
action: (text: string) => Promise<{ text: string; error?: string }> | void
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
// Mock AI provider function
|
|
117
120
|
const getAIProvider = (settings: AISettingsType) => {
|
|
121
|
+
const generateWithPrompt = async (systemPrompt: string, userText: string) => {
|
|
122
|
+
// Mock implementation - consuming app should provide real AI integration
|
|
123
|
+
const result = `[${systemPrompt}] ${userText}`
|
|
124
|
+
return { text: result, error: undefined }
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
return {
|
|
119
128
|
generateText: async (prompt: string) => {
|
|
120
|
-
|
|
121
|
-
return
|
|
129
|
+
const result = await generateWithPrompt('Generate text', prompt)
|
|
130
|
+
return result.text
|
|
131
|
+
},
|
|
132
|
+
rewrite: async (text: string) => {
|
|
133
|
+
const result = await generateWithPrompt('Rewrite this text', text)
|
|
134
|
+
return result
|
|
135
|
+
},
|
|
136
|
+
expand: async (text: string) => {
|
|
137
|
+
const result = await generateWithPrompt('Expand this text', text)
|
|
138
|
+
return result
|
|
139
|
+
},
|
|
140
|
+
summarize: async (text: string) => {
|
|
141
|
+
const result = await generateWithPrompt('Summarize this text', text)
|
|
142
|
+
return result
|
|
143
|
+
},
|
|
144
|
+
fixGrammar: async (text: string) => {
|
|
145
|
+
const result = await generateWithPrompt('Fix grammar and spelling', text)
|
|
146
|
+
return result
|
|
147
|
+
},
|
|
148
|
+
translate: async (text: string, targetLang: string) => {
|
|
149
|
+
const result = await generateWithPrompt(`Translate to ${targetLang}`, text)
|
|
150
|
+
return result
|
|
151
|
+
},
|
|
152
|
+
changeTone: async (text: string, tone: string) => {
|
|
153
|
+
const result = await generateWithPrompt(`Change tone to ${tone}`, text)
|
|
154
|
+
return result
|
|
155
|
+
},
|
|
156
|
+
continueWriting: async (text: string) => {
|
|
157
|
+
const result = await generateWithPrompt('Continue writing', text)
|
|
158
|
+
return result
|
|
159
|
+
},
|
|
160
|
+
improveWriting: async (text: string) => {
|
|
161
|
+
const result = await generateWithPrompt('Improve writing quality', text)
|
|
162
|
+
return result
|
|
163
|
+
},
|
|
164
|
+
generateIdeas: async (text: string) => {
|
|
165
|
+
const result = await generateWithPrompt('Generate writing ideas', text)
|
|
166
|
+
return result
|
|
167
|
+
},
|
|
168
|
+
complete: async (text: string) => {
|
|
169
|
+
const result = await generateWithPrompt('Complete this text', text)
|
|
170
|
+
return result
|
|
122
171
|
}
|
|
123
172
|
}
|
|
124
173
|
}
|
|
@@ -255,14 +304,12 @@ export function RichTextEditor({
|
|
|
255
304
|
},
|
|
256
305
|
}: RichTextEditorProps) {
|
|
257
306
|
// Pro access kontrolü
|
|
258
|
-
const docsProAccess = { hasAccess: true }; // Pro access assumed in package
|
|
259
307
|
const { hasProAccess, isLoading } = useSubscription();
|
|
260
308
|
|
|
261
309
|
// In docs mode, always show the component
|
|
262
|
-
const canShowComponent = docsProAccess.isDocsMode || hasProAccess;
|
|
263
310
|
|
|
264
311
|
// If not in docs mode and no pro access, show upgrade prompt
|
|
265
|
-
if (!
|
|
312
|
+
if (!isLoading && !hasProAccess) {
|
|
266
313
|
return (
|
|
267
314
|
<Card className={cn("w-full", className)}>
|
|
268
315
|
<CardContent className="py-12 text-center">
|
|
@@ -548,10 +595,10 @@ export function RichTextEditor({
|
|
|
548
595
|
response = await provider.improveWriting(text);
|
|
549
596
|
break;
|
|
550
597
|
case 'ideas':
|
|
551
|
-
response = await provider.generateIdeas(text
|
|
598
|
+
response = await provider.generateIdeas(text);
|
|
552
599
|
break;
|
|
553
600
|
default:
|
|
554
|
-
response = await provider.complete(
|
|
601
|
+
response = await provider.complete(text);
|
|
555
602
|
}
|
|
556
603
|
|
|
557
604
|
if (response.error) {
|
|
@@ -938,6 +985,16 @@ export function RichTextEditor({
|
|
|
938
985
|
<DialogTrigger asChild>
|
|
939
986
|
<ToolbarButton
|
|
940
987
|
active={editor.isActive('link')}
|
|
988
|
+
onClick={() => {
|
|
989
|
+
const previousUrl = editor.getAttributes('link').href
|
|
990
|
+
const url = window.prompt('URL', previousUrl)
|
|
991
|
+
if (url === null) return
|
|
992
|
+
if (url === '') {
|
|
993
|
+
editor.chain().focus().extendMarkRange('link').unsetLink().run()
|
|
994
|
+
} else {
|
|
995
|
+
editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()
|
|
996
|
+
}
|
|
997
|
+
}}
|
|
941
998
|
tooltip="Add link"
|
|
942
999
|
disabled={isSourceView}
|
|
943
1000
|
>
|
|
@@ -994,6 +1051,12 @@ export function RichTextEditor({
|
|
|
994
1051
|
<Dialog open={isImageDialogOpen} onOpenChange={setIsImageDialogOpen}>
|
|
995
1052
|
<DialogTrigger asChild>
|
|
996
1053
|
<ToolbarButton
|
|
1054
|
+
onClick={() => {
|
|
1055
|
+
const url = window.prompt('Image URL')
|
|
1056
|
+
if (url) {
|
|
1057
|
+
editor.chain().focus().setImage({ src: url }).run()
|
|
1058
|
+
}
|
|
1059
|
+
}}
|
|
997
1060
|
tooltip="Add image"
|
|
998
1061
|
disabled={isSourceView}
|
|
999
1062
|
>
|
|
@@ -1038,6 +1101,7 @@ export function RichTextEditor({
|
|
|
1038
1101
|
<Dialog open={isTableDialogOpen} onOpenChange={setIsTableDialogOpen}>
|
|
1039
1102
|
<DialogTrigger asChild>
|
|
1040
1103
|
<ToolbarButton
|
|
1104
|
+
onClick={() => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}
|
|
1041
1105
|
tooltip="Create table"
|
|
1042
1106
|
disabled={isSourceView}
|
|
1043
1107
|
>
|
|
@@ -1255,6 +1319,7 @@ export function RichTextEditor({
|
|
|
1255
1319
|
<Dialog open={isAiSettingsOpen} onOpenChange={setIsAiSettingsOpen}>
|
|
1256
1320
|
<DialogTrigger asChild>
|
|
1257
1321
|
<ToolbarButton
|
|
1322
|
+
onClick={() => setIsAiSettingsOpen(true)}
|
|
1258
1323
|
tooltip="AI Settings - Configure your API keys here"
|
|
1259
1324
|
disabled={isSourceView}
|
|
1260
1325
|
>
|
|
@@ -66,7 +66,6 @@ const SpotlightCardInternal = React.forwardRef<HTMLDivElement, SpotlightCardProp
|
|
|
66
66
|
onMouseLeave={handleMouseLeave}
|
|
67
67
|
whileHover={{ scale: 1.02 }}
|
|
68
68
|
transition={{ duration: 0.2 }}
|
|
69
|
-
{...props}
|
|
70
69
|
>
|
|
71
70
|
{/* Spotlight effect */}
|
|
72
71
|
<motion.div
|
|
@@ -154,14 +153,12 @@ SpotlightCardInternal.displayName = "SpotlightCardInternal"
|
|
|
154
153
|
export const SpotlightCard = React.forwardRef<HTMLDivElement, SpotlightCardProps>(
|
|
155
154
|
({ className, ...props }, ref) => {
|
|
156
155
|
// Check if we're in docs mode or have pro access
|
|
157
|
-
const docsProAccess = { hasAccess: true } // Pro access assumed in package
|
|
158
156
|
const { hasProAccess, isLoading } = useSubscription()
|
|
159
157
|
|
|
160
158
|
// In docs mode, always show the component
|
|
161
|
-
const canShowComponent = docsProAccess.isDocsMode || hasProAccess
|
|
162
159
|
|
|
163
160
|
// If not in docs mode and no pro access, show upgrade prompt
|
|
164
|
-
if (!
|
|
161
|
+
if (!isLoading && !hasProAccess) {
|
|
165
162
|
return (
|
|
166
163
|
<Card className={cn("w-fit", className)}>
|
|
167
164
|
<CardContent className="py-6 text-center">
|
|
@@ -4,7 +4,7 @@ import React, { ReactNode } from "react"
|
|
|
4
4
|
import { motion, useMotionValue, useTransform, animate, PanInfo } from "framer-motion"
|
|
5
5
|
import { cn } from "../../lib/utils"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
interface SwipeableCardProps {
|
|
8
8
|
children: ReactNode
|
|
9
9
|
onSwipeLeft?: () => void
|
|
10
10
|
onSwipeRight?: () => void
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from 'lucide-react'
|
|
22
22
|
import { cn } from '@moontra/moonui'
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface TimelineEvent {
|
|
25
25
|
id: string
|
|
26
26
|
title: string
|
|
27
27
|
description?: string
|
|
@@ -44,7 +44,7 @@ export interface TimelineEvent {
|
|
|
44
44
|
color?: string
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
interface TimelineProps {
|
|
48
48
|
events: TimelineEvent[]
|
|
49
49
|
onEventClick?: (event: TimelineEvent) => void
|
|
50
50
|
className?: string
|
|
@@ -18,6 +18,8 @@ export * from './switch';
|
|
|
18
18
|
export * from './checkbox';
|
|
19
19
|
export * from './color-picker';
|
|
20
20
|
export * from './textarea';
|
|
21
|
+
export * from './skeleton';
|
|
22
|
+
export * from './progress';
|
|
21
23
|
|
|
22
24
|
// Micro-interaction Components
|
|
23
25
|
export { AnimatedButton, animatedButtonVariants } from "./animated-button";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const Progress = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof ProgressPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
|
10
|
+
>(({ className, value, ...props }, ref) => (
|
|
11
|
+
<ProgressPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
<ProgressPrimitive.Indicator
|
|
20
|
+
className="h-full w-full flex-1 bg-primary transition-all"
|
|
21
|
+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
|
22
|
+
/>
|
|
23
|
+
</ProgressPrimitive.Root>
|
|
24
|
+
))
|
|
25
|
+
Progress.displayName = ProgressPrimitive.Root.displayName
|
|
26
|
+
|
|
27
|
+
export { Progress }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils"
|
|
4
|
+
|
|
5
|
+
interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
6
|
+
|
|
7
|
+
export function Skeleton({
|
|
8
|
+
className,
|
|
9
|
+
...props
|
|
10
|
+
}: SkeletonProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
className={cn("animate-pulse rounded-md bg-muted", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -72,7 +72,7 @@ const TooltipContent = React.forwardRef<
|
|
|
72
72
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
73
73
|
|
|
74
74
|
// Simplified Tooltip component for easy usage
|
|
75
|
-
|
|
75
|
+
interface SimpleTooltipProps {
|
|
76
76
|
children: React.ReactElement
|
|
77
77
|
content: React.ReactNode
|
|
78
78
|
variant?: TooltipContentProps["variant"]
|
|
@@ -43,8 +43,8 @@ export function VirtualList<T = any>({
|
|
|
43
43
|
// Refs
|
|
44
44
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
45
45
|
const scrollElementRef = useRef<HTMLDivElement>(null)
|
|
46
|
-
const scrollTimeoutRef = useRef<NodeJS.Timeout>()
|
|
47
|
-
const isScrollingTimeoutRef = useRef<NodeJS.Timeout>()
|
|
46
|
+
const scrollTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
47
|
+
const isScrollingTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
|
48
48
|
|
|
49
49
|
// Item pozisyonlarını hesapla
|
|
50
50
|
const itemPositions = useMemo(() => {
|
|
@@ -365,5 +365,4 @@ export function SelectableVirtualList<T = any>({
|
|
|
365
365
|
return <VirtualList {...props} renderItem={renderItem} />
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
// Export types
|
|
369
|
-
export type { VirtualListProps, SelectableVirtualListProps }
|
|
368
|
+
// Export types
|
package/src/hooks/use-chart.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import { ChartDataPoint, ChartSeries } from '../components/advanced-chart'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
interface UseChartOptions {
|
|
7
7
|
data: ChartDataPoint[]
|
|
8
8
|
series: ChartSeries[]
|
|
9
9
|
realtime?: boolean
|
|
@@ -11,7 +11,7 @@ export interface UseChartOptions {
|
|
|
11
11
|
maxDataPoints?: number
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
interface UseChartReturn {
|
|
15
15
|
data: ChartDataPoint[]
|
|
16
16
|
series: ChartSeries[]
|
|
17
17
|
isLoading: boolean
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// Pro Components Export - Source of Truth: packages/moonui-pro
|
|
2
2
|
// Development environment imports from local packages
|
|
3
3
|
|
|
4
|
-
// Package access guard - ensures CLI usage
|
|
5
|
-
import "./utils/package-guard"
|
|
6
|
-
|
|
7
4
|
// Import CSS for auto-loading
|
|
8
5
|
import "./styles/index.css"
|
|
9
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface LoginFormTheme {
|
|
2
2
|
primary?: string
|
|
3
3
|
secondary?: string
|
|
4
4
|
background?: string
|
|
@@ -7,7 +7,7 @@ export interface LoginFormTheme {
|
|
|
7
7
|
spacing?: 'compact' | 'normal' | 'relaxed'
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface LoginFormFeatures {
|
|
11
11
|
socialLogin?: boolean | string[]
|
|
12
12
|
rememberMe?: boolean
|
|
13
13
|
forgotPassword?: boolean
|
|
@@ -15,7 +15,7 @@ export interface LoginFormFeatures {
|
|
|
15
15
|
passwordStrength?: boolean
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
interface LoginFormTexts {
|
|
19
19
|
title?: string
|
|
20
20
|
subtitle?: string
|
|
21
21
|
emailLabel?: string
|
|
@@ -27,18 +27,18 @@ export interface LoginFormTexts {
|
|
|
27
27
|
orContinueWith?: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
interface LoginData {
|
|
31
31
|
email: string
|
|
32
32
|
password: string
|
|
33
33
|
rememberMe?: boolean
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
interface ValidationRules {
|
|
37
37
|
email?: (value: string) => string | null
|
|
38
38
|
password?: (value: string) => string | null
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
interface LoginFormProps {
|
|
42
42
|
// Stil özelleştirme
|
|
43
43
|
theme?: LoginFormTheme
|
|
44
44
|
className?: string
|
package/src/use-pro-access.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { useSession } from 'next-auth/react'
|
|
|
6
6
|
import { useQuery } from '@tanstack/react-query'
|
|
7
7
|
import { getComponentAccess } from '@/lib/component-metadata'
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface UserSubscription {
|
|
10
10
|
userId: string
|
|
11
11
|
plan: 'free' | 'pro_monthly' | 'pro_annual' | 'pro_lifetime'
|
|
12
12
|
status: 'active' | 'expired' | 'cancelled' | 'trial'
|
|
@@ -15,7 +15,7 @@ export interface UserSubscription {
|
|
|
15
15
|
updatedAt: Date
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
interface ProAccessStatus {
|
|
19
19
|
hasProAccess: boolean
|
|
20
20
|
subscription: UserSubscription | null | undefined
|
|
21
21
|
isLoading: boolean
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import React, { useEffect, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
const API_BASE = process.env.NEXT_PUBLIC_MOONUI_API || 'https://moonui.dev';
|
|
6
|
-
|
|
7
|
-
interface LicenseCheckResult {
|
|
8
|
-
valid: boolean;
|
|
9
|
-
error?: string;
|
|
10
|
-
loading: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// In-memory cache for license check
|
|
14
|
-
let licenseCache: {
|
|
15
|
-
result: boolean;
|
|
16
|
-
timestamp: number;
|
|
17
|
-
} | null = null;
|
|
18
|
-
|
|
19
|
-
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
20
|
-
|
|
21
|
-
export function useLicenseCheck(): LicenseCheckResult {
|
|
22
|
-
const [state, setState] = useState<LicenseCheckResult>({
|
|
23
|
-
valid: false,
|
|
24
|
-
loading: true,
|
|
25
|
-
error: undefined
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
checkLicense().then(result => {
|
|
30
|
-
setState({
|
|
31
|
-
valid: result.valid,
|
|
32
|
-
loading: false,
|
|
33
|
-
error: result.error
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
}, []);
|
|
37
|
-
|
|
38
|
-
return state;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function checkLicense(): Promise<{ valid: boolean; error?: string }> {
|
|
42
|
-
// Check cache first
|
|
43
|
-
if (licenseCache && Date.now() - licenseCache.timestamp < CACHE_DURATION) {
|
|
44
|
-
return { valid: licenseCache.result };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
// Get auth token from localStorage or cookie
|
|
49
|
-
const token = getAuthToken();
|
|
50
|
-
|
|
51
|
-
if (!token) {
|
|
52
|
-
return { valid: false, error: 'No authentication token found' };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const response = await fetch(`${API_BASE}/api/license/verify`, {
|
|
56
|
-
method: 'POST',
|
|
57
|
-
headers: {
|
|
58
|
-
'Authorization': `Bearer ${token}`,
|
|
59
|
-
'Content-Type': 'application/json'
|
|
60
|
-
},
|
|
61
|
-
body: JSON.stringify({
|
|
62
|
-
component: 'runtime-check',
|
|
63
|
-
source: 'moonui-pro'
|
|
64
|
-
})
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (!response.ok) {
|
|
68
|
-
return { valid: false, error: 'License verification failed' };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const data = await response.json();
|
|
72
|
-
|
|
73
|
-
// Cache the result
|
|
74
|
-
licenseCache = {
|
|
75
|
-
result: data.valid && data.planType !== 'free',
|
|
76
|
-
timestamp: Date.now()
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return { valid: licenseCache.result };
|
|
80
|
-
} catch (error) {
|
|
81
|
-
return { valid: false, error: 'Network error during license check' };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getAuthToken(): string | null {
|
|
86
|
-
// First check localStorage (for client-side)
|
|
87
|
-
if (typeof window !== 'undefined') {
|
|
88
|
-
const stored = localStorage.getItem('moonui_auth');
|
|
89
|
-
if (stored) {
|
|
90
|
-
try {
|
|
91
|
-
const auth = JSON.parse(stored);
|
|
92
|
-
return auth.accessToken;
|
|
93
|
-
} catch {
|
|
94
|
-
// Invalid JSON
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Check cookies (for SSR)
|
|
100
|
-
if (typeof document !== 'undefined') {
|
|
101
|
-
const cookies = document.cookie.split(';');
|
|
102
|
-
for (const cookie of cookies) {
|
|
103
|
-
const [name, value] = cookie.trim().split('=');
|
|
104
|
-
if (name === 'moonui_token') {
|
|
105
|
-
return decodeURIComponent(value);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// License Guard HOC
|
|
114
|
-
export function withLicenseGuard<P extends object>(
|
|
115
|
-
Component: React.ComponentType<P>,
|
|
116
|
-
options?: {
|
|
117
|
-
fallback?: React.ReactNode;
|
|
118
|
-
onUnauthorized?: () => void;
|
|
119
|
-
}
|
|
120
|
-
) {
|
|
121
|
-
return function LicenseGuardedComponent(props: P) {
|
|
122
|
-
const { valid, loading, error } = useLicenseCheck();
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
if (!loading && !valid && options?.onUnauthorized) {
|
|
126
|
-
options.onUnauthorized();
|
|
127
|
-
}
|
|
128
|
-
}, [loading, valid]);
|
|
129
|
-
|
|
130
|
-
if (loading) {
|
|
131
|
-
return (
|
|
132
|
-
<div className="flex items-center justify-center p-8">
|
|
133
|
-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
|
|
134
|
-
</div>
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!valid) {
|
|
139
|
-
if (options?.fallback) {
|
|
140
|
-
return <>{options.fallback}</>;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<div className="rounded-lg border border-red-200 bg-red-50 p-8 text-center">
|
|
145
|
-
<h3 className="text-lg font-semibold text-red-900 mb-2">
|
|
146
|
-
Pro License Required
|
|
147
|
-
</h3>
|
|
148
|
-
<p className="text-red-700 mb-4">
|
|
149
|
-
This component requires a MoonUI Pro license.
|
|
150
|
-
</p>
|
|
151
|
-
<a
|
|
152
|
-
href="https://moonui.dev/pricing"
|
|
153
|
-
className="inline-flex items-center justify-center rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700"
|
|
154
|
-
>
|
|
155
|
-
Get Pro License
|
|
156
|
-
</a>
|
|
157
|
-
{error && (
|
|
158
|
-
<p className="text-xs text-red-600 mt-4">
|
|
159
|
-
Error: {error}
|
|
160
|
-
</p>
|
|
161
|
-
)}
|
|
162
|
-
</div>
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return <Component {...props} />;
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Self-protecting component wrapper
|
|
171
|
-
export function ProComponent<P extends object>({
|
|
172
|
-
component: Component,
|
|
173
|
-
...props
|
|
174
|
-
}: P & { component: React.ComponentType<P> }) {
|
|
175
|
-
const GuardedComponent = withLicenseGuard(Component);
|
|
176
|
-
return <GuardedComponent {...(props as P)} />;
|
|
177
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package Access Guard
|
|
3
|
-
* Prevents direct NPM package usage without CLI
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const ALLOWED_CALLERS = [
|
|
7
|
-
'@moontra/moonui-cli',
|
|
8
|
-
'moonui-cli',
|
|
9
|
-
'.moonui/temp', // CLI temp directory
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
const DEV_MODE = process.env.NODE_ENV === 'development';
|
|
13
|
-
|
|
14
|
-
export function checkPackageAccess(): void {
|
|
15
|
-
// Skip in development mode for easier testing
|
|
16
|
-
if (DEV_MODE) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Check if running in a browser (client-side)
|
|
21
|
-
if (typeof window !== 'undefined') {
|
|
22
|
-
// Client-side access is allowed after server-side validation
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Server-side check
|
|
27
|
-
const stack = new Error().stack;
|
|
28
|
-
|
|
29
|
-
if (!stack) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
'MoonUI Pro: This package can only be used via @moontra/moonui-cli. ' +
|
|
32
|
-
'Please install components using: npx @moontra/moonui-cli add <component>'
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Check if called from allowed sources
|
|
37
|
-
const isAllowed = ALLOWED_CALLERS.some(caller => stack.includes(caller));
|
|
38
|
-
|
|
39
|
-
// Also check for local file paths that indicate CLI usage
|
|
40
|
-
const isCliGenerated = stack.includes('/components/ui/') ||
|
|
41
|
-
stack.includes('/components/pro/');
|
|
42
|
-
|
|
43
|
-
if (!isAllowed && !isCliGenerated) {
|
|
44
|
-
console.error('Stack trace:', stack);
|
|
45
|
-
throw new Error(
|
|
46
|
-
'MoonUI Pro: Unauthorized package access detected.\n' +
|
|
47
|
-
'This package can only be used via @moontra/moonui-cli.\n' +
|
|
48
|
-
'Install components using: npx @moontra/moonui-cli add <component>\n\n' +
|
|
49
|
-
'If you have a Pro license, make sure to:\n' +
|
|
50
|
-
'1. Login with: npx @moontra/moonui-cli login\n' +
|
|
51
|
-
'2. Install components via CLI\n\n' +
|
|
52
|
-
'Learn more at: https://moonui.dev/docs/installation'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Auto-check on import
|
|
58
|
-
if (typeof window === 'undefined') {
|
|
59
|
-
checkPackageAccess();
|
|
60
|
-
}
|