@aiready/components 0.11.13 → 0.11.15

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/components",
3
- "version": "0.11.13",
3
+ "version": "0.11.15",
4
4
  "description": "Unified shared components library (UI, charts, hooks, utilities) for AIReady",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -62,8 +62,10 @@
62
62
  "clsx": "^2.1.1",
63
63
  "d3": "^7.9.0",
64
64
  "d3-force": "^3.0.0",
65
+ "framer-motion": "^12.35.0",
66
+ "lucide-react": "^0.577.0",
65
67
  "tailwind-merge": "^3.0.0",
66
- "@aiready/core": "0.21.13"
68
+ "@aiready/core": "0.21.14"
67
69
  },
68
70
  "devDependencies": {
69
71
  "@testing-library/jest-dom": "^6.6.5",
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useState, useCallback, useMemo } from 'react';
4
+ import { motion, AnimatePresence } from 'framer-motion';
5
+ import { cn } from '../utils/cn';
4
6
 
5
7
  export interface CodeBlockProps {
6
8
  children: React.ReactNode;
@@ -8,17 +10,15 @@ export interface CodeBlockProps {
8
10
  showCopy?: boolean;
9
11
  showHeader?: boolean;
10
12
  className?: string;
13
+ variant?: 'default' | 'glass';
11
14
  }
12
15
 
13
16
  // Dedent helper - removes common leading indentation
14
17
  function dedentCode(code: string): string {
15
- // Normalize tabs to two spaces
16
18
  const normalized = code.replace(/\t/g, ' ').replace(/[ \t]+$/gm, '');
17
-
18
19
  const lines = normalized.split('\n');
19
20
  if (lines.length <= 1) return normalized.trim();
20
21
 
21
- // Remove leading/trailing empty lines
22
22
  let start = 0;
23
23
  while (start < lines.length && lines[start].trim() === '') start++;
24
24
  let end = lines.length - 1;
@@ -27,27 +27,21 @@ function dedentCode(code: string): string {
27
27
  if (start > end) return '';
28
28
  const relevantLines = lines.slice(start, end + 1);
29
29
 
30
- // Find minimum indent across non-empty lines
31
30
  const nonEmpty = relevantLines.filter((l) => l.trim() !== '');
32
31
  const minIndent = nonEmpty.reduce((min, line) => {
33
32
  const m = line.match(/^\s*/)?.[0].length ?? 0;
34
33
  return Math.min(min, m);
35
34
  }, Infinity);
36
35
 
37
- // Remove common indentation
38
- const dedented =
39
- minIndent === Infinity || minIndent === 0
40
- ? relevantLines.join('\n')
41
- : relevantLines
42
- .map((l) =>
43
- l.startsWith(' '.repeat(minIndent)) ? l.slice(minIndent) : l
44
- )
45
- .join('\n');
46
-
47
- return dedented;
36
+ return minIndent === Infinity || minIndent === 0
37
+ ? relevantLines.join('\n')
38
+ : relevantLines
39
+ .map((l) =>
40
+ l.startsWith(' '.repeat(minIndent)) ? l.slice(minIndent) : l
41
+ )
42
+ .join('\n');
48
43
  }
49
44
 
50
- // Simple Copy Button
51
45
  function CopyButton({ code }: { code: string }) {
52
46
  const [copied, setCopied] = useState(false);
53
47
 
@@ -57,7 +51,6 @@ function CopyButton({ code }: { code: string }) {
57
51
  setCopied(true);
58
52
  setTimeout(() => setCopied(false), 2000);
59
53
  } catch {
60
- // Fallback for older browsers
61
54
  const textarea = document.createElement('textarea');
62
55
  textarea.value = code;
63
56
  textarea.style.position = 'fixed';
@@ -74,38 +67,30 @@ function CopyButton({ code }: { code: string }) {
74
67
  return (
75
68
  <button
76
69
  onClick={handleCopy}
77
- className="rounded-md p-1.5 text-slate-400 hover:text-slate-200 hover:bg-slate-700/50 transition-colors"
78
- title={copied ? 'Copied!' : 'Copy code'}
70
+ className="flex items-center gap-1.5 rounded-md px-2 py-1 text-[10px] font-bold uppercase tracking-wider text-slate-400 hover:bg-slate-700/50 hover:text-cyan-400 transition-all"
79
71
  >
80
- {copied ? (
81
- <svg
82
- className="h-4 w-4"
83
- fill="none"
84
- viewBox="0 0 24 24"
85
- strokeWidth="1.5"
86
- stroke="currentColor"
87
- >
88
- <path
89
- strokeLinecap="round"
90
- strokeLinejoin="round"
91
- d="M4.5 12.75l6 6 9-13.5"
92
- />
93
- </svg>
94
- ) : (
95
- <svg
96
- className="h-4 w-4"
97
- fill="none"
98
- viewBox="0 0 24 24"
99
- strokeWidth="1.5"
100
- stroke="currentColor"
101
- >
102
- <path
103
- strokeLinecap="round"
104
- strokeLinejoin="round"
105
- d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
106
- />
107
- </svg>
108
- )}
72
+ <AnimatePresence mode="wait">
73
+ {copied ? (
74
+ <motion.span
75
+ key="copied"
76
+ initial={{ opacity: 0, y: 5 }}
77
+ animate={{ opacity: 1, y: 0 }}
78
+ exit={{ opacity: 0, y: -5 }}
79
+ className="text-cyan-400"
80
+ >
81
+ Copied!
82
+ </motion.span>
83
+ ) : (
84
+ <motion.span
85
+ key="copy"
86
+ initial={{ opacity: 0, y: 5 }}
87
+ animate={{ opacity: 1, y: 0 }}
88
+ exit={{ opacity: 0, y: -5 }}
89
+ >
90
+ Copy
91
+ </motion.span>
92
+ )}
93
+ </AnimatePresence>
109
94
  </button>
110
95
  );
111
96
  }
@@ -116,13 +101,10 @@ export function CodeBlock({
116
101
  showCopy = true,
117
102
  showHeader = true,
118
103
  className = '',
104
+ variant = 'glass',
119
105
  }: CodeBlockProps) {
120
- // Get code string from children
121
106
  const codeString = useMemo(() => {
122
- if (typeof children === 'string') {
123
- return dedentCode(children);
124
- }
125
- // Handle template literal children
107
+ if (typeof children === 'string') return dedentCode(children);
126
108
  try {
127
109
  const raw = React.Children.toArray(children)
128
110
  .map((c) =>
@@ -137,18 +119,30 @@ export function CodeBlock({
137
119
 
138
120
  return (
139
121
  <div
140
- className={`group relative my-4 overflow-hidden rounded-xl border border-slate-700 bg-slate-900 shadow-lg ${className}`}
122
+ className={cn(
123
+ 'group relative my-6 overflow-hidden rounded-2xl border transition-all',
124
+ variant === 'glass'
125
+ ? 'border-indigo-500/20 bg-slate-950/40 backdrop-blur-md shadow-2xl'
126
+ : 'border-slate-700 bg-slate-900 shadow-lg',
127
+ className
128
+ )}
141
129
  >
142
- {/* Header bar */}
143
130
  {showHeader && (
144
- <div className="flex items-center justify-between border-b border-slate-700 bg-slate-800/50 px-4 py-2">
131
+ <div
132
+ className={cn(
133
+ 'flex items-center justify-between px-4 py-2 border-b',
134
+ variant === 'glass'
135
+ ? 'border-indigo-500/10 bg-slate-900/20'
136
+ : 'border-slate-800 bg-slate-800/50'
137
+ )}
138
+ >
145
139
  <div className="flex items-center gap-2">
146
140
  <div className="flex gap-1.5">
147
- <div className="h-3 w-3 rounded-full bg-red-500/50" />
148
- <div className="h-3 w-3 rounded-full bg-amber-500/50" />
149
- <div className="h-3 w-3 rounded-full bg-emerald-500/50" />
141
+ <div className="h-2.5 w-2.5 rounded-full bg-red-500/20 border border-red-500/40" />
142
+ <div className="h-2.5 w-2.5 rounded-full bg-amber-500/20 border border-amber-500/40" />
143
+ <div className="h-2.5 w-2.5 rounded-full bg-emerald-500/20 border border-emerald-500/40" />
150
144
  </div>
151
- <span className="ml-2 text-xs font-semibold uppercase tracking-wider text-slate-500 font-mono">
145
+ <span className="ml-2 text-[10px] font-bold uppercase tracking-widest text-slate-500 font-mono">
152
146
  {language}
153
147
  </span>
154
148
  </div>
@@ -156,8 +150,7 @@ export function CodeBlock({
156
150
  </div>
157
151
  )}
158
152
 
159
- {/* Code body */}
160
- <pre className="overflow-x-auto p-4 text-sm leading-relaxed">
153
+ <pre className="overflow-x-auto p-4 text-sm leading-relaxed scrollbar-thin scrollbar-thumb-slate-700">
161
154
  <code className="font-mono block whitespace-pre text-slate-300">
162
155
  {codeString}
163
156
  </code>
@@ -166,7 +159,6 @@ export function CodeBlock({
166
159
  );
167
160
  }
168
161
 
169
- // Inline code component
170
162
  export function InlineCode({
171
163
  children,
172
164
  className = '',
@@ -176,7 +168,10 @@ export function InlineCode({
176
168
  }) {
177
169
  return (
178
170
  <code
179
- className={`rounded-md bg-slate-100 px-1.5 py-0.5 text-sm font-mono text-slate-800 ${className}`}
171
+ className={cn(
172
+ 'rounded-md bg-slate-800/50 border border-slate-700/50 px-1.5 py-0.5 text-xs font-mono text-cyan-400',
173
+ className
174
+ )}
180
175
  >
181
176
  {children}
182
177
  </code>
@@ -3,7 +3,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
3
3
  import { cn } from '../utils/cn';
4
4
 
5
5
  const buttonVariants = cva(
6
- 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
6
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
7
7
  {
8
8
  variants: {
9
9
  variant: {
@@ -16,12 +16,20 @@ const buttonVariants = cva(
16
16
  'bg-secondary text-secondary-foreground hover:bg-secondary/80',
17
17
  ghost: 'hover:bg-accent hover:text-accent-foreground',
18
18
  link: 'text-primary underline-offset-4 hover:underline',
19
+ // Platform specific high-polish variants
20
+ glow: 'bg-gradient-to-r from-cyan-600 to-blue-600 text-white shadow-lg shadow-cyan-500/20 hover:shadow-cyan-500/40 hover:scale-[1.02] active:scale-[0.98]',
21
+ glass:
22
+ 'bg-slate-800/50 backdrop-blur-sm border border-slate-700 text-slate-200 hover:bg-slate-700/50 hover:text-white',
23
+ accent:
24
+ 'bg-cyan-500/10 text-cyan-400 border border-cyan-500/20 hover:bg-cyan-500/20',
19
25
  },
20
26
  size: {
21
27
  default: 'h-10 px-4 py-2',
22
28
  sm: 'h-9 rounded-md px-3',
23
29
  lg: 'h-11 rounded-md px-8',
24
30
  icon: 'h-10 w-10',
31
+ // Extra sizes for dashboard use
32
+ xs: 'h-7 rounded-md px-2 text-[10px] font-bold uppercase tracking-wider',
25
33
  },
26
34
  },
27
35
  defaultVariants: {
@@ -75,6 +75,45 @@ const CardFooter = React.forwardRef<
75
75
  ));
76
76
  CardFooter.displayName = 'CardFooter';
77
77
 
78
+ // GlassCard - Highly polished platform style
79
+ const GlassCard = React.forwardRef<
80
+ HTMLDivElement,
81
+ React.HTMLAttributes<HTMLDivElement>
82
+ >(({ className, ...props }, ref) => (
83
+ <div
84
+ ref={ref}
85
+ className={cn(
86
+ 'rounded-2xl border border-indigo-500/20 bg-slate-900/40 backdrop-blur-md shadow-xl transition-all hover:border-indigo-500/30',
87
+ className
88
+ )}
89
+ {...props}
90
+ />
91
+ ));
92
+ GlassCard.displayName = 'GlassCard';
93
+
94
+ const GlassCardHeader = React.forwardRef<
95
+ HTMLDivElement,
96
+ React.HTMLAttributes<HTMLDivElement>
97
+ >(({ className, ...props }, ref) => (
98
+ <div
99
+ ref={ref}
100
+ className={cn(
101
+ 'flex flex-col space-y-1.5 p-6 border-b border-indigo-500/10',
102
+ className
103
+ )}
104
+ {...props}
105
+ />
106
+ ));
107
+ GlassCardHeader.displayName = 'GlassCardHeader';
108
+
109
+ const GlassCardContent = React.forwardRef<
110
+ HTMLDivElement,
111
+ React.HTMLAttributes<HTMLDivElement>
112
+ >(({ className, ...props }, ref) => (
113
+ <div ref={ref} className={cn('p-6', className)} {...props} />
114
+ ));
115
+ GlassCardContent.displayName = 'GlassCardContent';
116
+
78
117
  export {
79
118
  Card,
80
119
  CardHeader,
@@ -82,4 +121,7 @@ export {
82
121
  CardTitle,
83
122
  CardDescription,
84
123
  CardContent,
124
+ GlassCard,
125
+ GlassCardHeader,
126
+ GlassCardContent,
85
127
  };