@nordsym/apiclaw 1.3.8 → 1.3.9
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/README.md +29 -13
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/cli/commands/mcp-install.d.ts +14 -0
- package/dist/cli/commands/mcp-install.d.ts.map +1 -0
- package/dist/cli/commands/mcp-install.js +246 -0
- package/dist/cli/commands/mcp-install.js.map +1 -0
- package/dist/cli/index.js +8 -0
- package/dist/cli/index.js.map +1 -1
- package/landing/public/book/index.html +24 -5
- package/landing/public/demo-product.jpg +0 -0
- package/landing/scripts/generate-stats.js +3 -2
- package/landing/src/app/api/og/route.tsx +1 -3
- package/landing/src/app/layout.tsx +1 -1
- package/landing/src/app/page.tsx +47 -20
- package/landing/src/app/workspace/page.tsx +2 -97
- package/landing/src/components/HeroTabs.tsx +2 -2
- package/landing/src/components/demo/PhoneDemo.tsx +423 -0
- package/landing/src/components/demo/index.ts +1 -0
- package/landing/src/lib/stats.json +2 -2
- package/landing/tailwind.config.ts +10 -0
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/cli/commands/mcp-install.ts +291 -0
- package/src/cli/index.ts +9 -0
- package/STATUS.md +0 -160
|
@@ -1462,7 +1462,7 @@ function AgentsTab({
|
|
|
1462
1462
|
setTimeout(() => setCopied(false), 2000);
|
|
1463
1463
|
};
|
|
1464
1464
|
|
|
1465
|
-
const mcpCommand = "npx @nordsym/apiclaw";
|
|
1465
|
+
const mcpCommand = "npx @nordsym/apiclaw mcp-install";
|
|
1466
1466
|
|
|
1467
1467
|
return (
|
|
1468
1468
|
<div className="space-y-6">
|
|
@@ -3725,7 +3725,7 @@ function DocsTab() {
|
|
|
3725
3725
|
</div>
|
|
3726
3726
|
<div>
|
|
3727
3727
|
<p className="text-sm text-[var(--text-muted)] mb-2">2. Or run directly:</p>
|
|
3728
|
-
<pre className="bg-[var(--background)] rounded-lg p-4 text-sm">npx @nordsym/apiclaw</pre>
|
|
3728
|
+
<pre className="bg-[var(--background)] rounded-lg p-4 text-sm">npx @nordsym/apiclaw mcp-install</pre>
|
|
3729
3729
|
</div>
|
|
3730
3730
|
<div>
|
|
3731
3731
|
<p className="text-sm text-[var(--text-muted)] mb-2">3. Interactive CLI mode:</p>
|
|
@@ -4011,101 +4011,6 @@ function FeedbackTab() {
|
|
|
4011
4011
|
</div>
|
|
4012
4012
|
</form>
|
|
4013
4013
|
</div>
|
|
4014
|
-
|
|
4015
|
-
<div className="rounded-2xl border border-[var(--border)] bg-[var(--surface-elevated)] p-6">
|
|
4016
|
-
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-6">
|
|
4017
|
-
<h3 className="font-semibold text-lg">Community Feedback</h3>
|
|
4018
|
-
<div className="flex flex-wrap gap-2">
|
|
4019
|
-
<select
|
|
4020
|
-
value={filterType}
|
|
4021
|
-
onChange={(e) => setFilterType(e.target.value as typeof filterType)}
|
|
4022
|
-
className="px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--background)] text-sm focus:outline-none focus:ring-2 focus:ring-[#ef4444]/50"
|
|
4023
|
-
>
|
|
4024
|
-
<option value="all">All Types</option>
|
|
4025
|
-
<option value="bug">Bugs</option>
|
|
4026
|
-
<option value="feature">Features</option>
|
|
4027
|
-
<option value="general">General</option>
|
|
4028
|
-
</select>
|
|
4029
|
-
|
|
4030
|
-
<select
|
|
4031
|
-
value={sortBy}
|
|
4032
|
-
onChange={(e) => setSortBy(e.target.value as typeof sortBy)}
|
|
4033
|
-
className="px-3 py-1.5 rounded-lg border border-[var(--border)] bg-[var(--background)] text-sm focus:outline-none focus:ring-2 focus:ring-[#ef4444]/50"
|
|
4034
|
-
>
|
|
4035
|
-
<option value="votes">Most Votes</option>
|
|
4036
|
-
<option value="recent">Most Recent</option>
|
|
4037
|
-
</select>
|
|
4038
|
-
</div>
|
|
4039
|
-
</div>
|
|
4040
|
-
|
|
4041
|
-
{loading ? (
|
|
4042
|
-
<div className="text-center py-8">
|
|
4043
|
-
<Loader2 className="w-8 h-8 text-[#ef4444] animate-spin mx-auto mb-2" />
|
|
4044
|
-
<p className="text-sm text-[var(--text-muted)]">Loading feedback...</p>
|
|
4045
|
-
</div>
|
|
4046
|
-
) : feedbackList.length === 0 ? (
|
|
4047
|
-
<div className="text-center py-12 rounded-xl border border-dashed border-[var(--border)] bg-[var(--surface)]/50">
|
|
4048
|
-
<MessageSquare className="w-12 h-12 text-[var(--text-muted)] mx-auto mb-3" />
|
|
4049
|
-
<h4 className="font-semibold mb-1">No Feedback Yet</h4>
|
|
4050
|
-
<p className="text-sm text-[var(--text-muted)]">
|
|
4051
|
-
Be the first to share your thoughts!
|
|
4052
|
-
</p>
|
|
4053
|
-
</div>
|
|
4054
|
-
) : (
|
|
4055
|
-
<div className="space-y-3">
|
|
4056
|
-
{feedbackList.map((item) => (
|
|
4057
|
-
<div
|
|
4058
|
-
key={item._id}
|
|
4059
|
-
className={`flex gap-3 p-4 rounded-xl border transition ${
|
|
4060
|
-
item.isOwn
|
|
4061
|
-
? "border-[#ef4444]/30 bg-[#ef4444]/5"
|
|
4062
|
-
: "border-[var(--border)] bg-[var(--surface)]"
|
|
4063
|
-
}`}
|
|
4064
|
-
>
|
|
4065
|
-
<div className="flex flex-col items-center gap-1 min-w-[40px]">
|
|
4066
|
-
<button
|
|
4067
|
-
onClick={() => handleVote(item._id, "up")}
|
|
4068
|
-
disabled={votingId === item._id}
|
|
4069
|
-
className={`p-1 rounded hover:bg-[var(--background)] transition ${
|
|
4070
|
-
item.hasVoted ? "text-[#ef4444]" : "text-[var(--text-muted)]"
|
|
4071
|
-
}`}
|
|
4072
|
-
>
|
|
4073
|
-
<ChevronUp className="w-5 h-5" />
|
|
4074
|
-
</button>
|
|
4075
|
-
<span className={`text-sm font-bold ${item.votes > 0 ? "text-[#ef4444]" : item.votes < 0 ? "text-red-500" : "text-[var(--text-muted)]"}`}>
|
|
4076
|
-
{item.votes}
|
|
4077
|
-
</span>
|
|
4078
|
-
<button
|
|
4079
|
-
onClick={() => handleVote(item._id, "down")}
|
|
4080
|
-
disabled={votingId === item._id}
|
|
4081
|
-
className="p-1 rounded text-[var(--text-muted)] hover:bg-[var(--background)] transition"
|
|
4082
|
-
>
|
|
4083
|
-
<ChevronDown className="w-5 h-5" />
|
|
4084
|
-
</button>
|
|
4085
|
-
</div>
|
|
4086
|
-
|
|
4087
|
-
<div className="flex-1 min-w-0">
|
|
4088
|
-
<p className="text-[var(--text-primary)] mb-2">{item.content}</p>
|
|
4089
|
-
<div className="flex flex-wrap items-center gap-2 text-xs">
|
|
4090
|
-
<span className={`px-2 py-0.5 rounded-full capitalize ${getTypeBadge(item.type)}`}>
|
|
4091
|
-
{item.type}
|
|
4092
|
-
</span>
|
|
4093
|
-
<span className={`px-2 py-0.5 rounded-full capitalize ${getStatusBadge(item.status)}`}>
|
|
4094
|
-
{item.status}
|
|
4095
|
-
</span>
|
|
4096
|
-
<span className="text-[var(--text-muted)]">
|
|
4097
|
-
{formatDate(item.createdAt)}
|
|
4098
|
-
</span>
|
|
4099
|
-
{item.isOwn && (
|
|
4100
|
-
<span className="text-[#ef4444]">• You</span>
|
|
4101
|
-
)}
|
|
4102
|
-
</div>
|
|
4103
|
-
</div>
|
|
4104
|
-
</div>
|
|
4105
|
-
))}
|
|
4106
|
-
</div>
|
|
4107
|
-
)}
|
|
4108
|
-
</div>
|
|
4109
4014
|
</div>
|
|
4110
4015
|
);
|
|
4111
4016
|
}
|
|
@@ -31,7 +31,7 @@ export function HeroTabs() {
|
|
|
31
31
|
|
|
32
32
|
const configSnippetJson = selectedClient === "chatgpt" ? chatGptInstructions : jsonConfig;
|
|
33
33
|
|
|
34
|
-
const terminalCommand = "npx @nordsym/apiclaw";
|
|
34
|
+
const terminalCommand = "npx @nordsym/apiclaw mcp-install";
|
|
35
35
|
|
|
36
36
|
const copyConfig = () => {
|
|
37
37
|
navigator.clipboard.writeText(configSnippetJson);
|
|
@@ -126,7 +126,7 @@ export function HeroTabs() {
|
|
|
126
126
|
<div className="code-preview-header">terminal</div>
|
|
127
127
|
<div className="code-preview-body">
|
|
128
128
|
<pre className="text-sm">
|
|
129
|
-
<span className="text-green-400">$</span> <span className="text-blue-400">npx</span> @nordsym/apiclaw
|
|
129
|
+
<span className="text-green-400">$</span> <span className="text-blue-400">npx</span> @nordsym/apiclaw mcp-install
|
|
130
130
|
</pre>
|
|
131
131
|
</div>
|
|
132
132
|
</div>
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from "react";
|
|
4
|
+
import Image from "next/image";
|
|
5
|
+
|
|
6
|
+
interface WithMessage {
|
|
7
|
+
role: "user" | "assistant";
|
|
8
|
+
text: string;
|
|
9
|
+
meta?: string;
|
|
10
|
+
typing?: boolean;
|
|
11
|
+
image?: boolean;
|
|
12
|
+
currency?: boolean;
|
|
13
|
+
success?: boolean;
|
|
14
|
+
search?: boolean;
|
|
15
|
+
models?: { name: string; match: string }[];
|
|
16
|
+
results?: { name: string; match: string; cost: string }[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface WithoutMessage {
|
|
20
|
+
role: "step";
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Message = WithMessage | WithoutMessage;
|
|
25
|
+
|
|
26
|
+
// Example 1: Direct Call - Replicate
|
|
27
|
+
const DirectCallExample: WithMessage[] = [
|
|
28
|
+
{ role: "user", text: "Generate a product photo of a coffee mug" },
|
|
29
|
+
{ role: "assistant", text: "Direct Call → Replicate", search: true },
|
|
30
|
+
{
|
|
31
|
+
role: "assistant",
|
|
32
|
+
text: "Selecting model...",
|
|
33
|
+
models: [
|
|
34
|
+
{ name: "Flux Pro", match: "Best for products" },
|
|
35
|
+
{ name: "SDXL", match: "Fast generation" },
|
|
36
|
+
{ name: "Stable Diffusion 3", match: "Versatile" },
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{ role: "assistant", text: "Using Flux Pro", meta: "via Replicate Direct Call" },
|
|
40
|
+
{ role: "assistant", text: "Generating...", typing: true },
|
|
41
|
+
{ role: "assistant", text: "Done", image: true, success: true },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// Example 2: Open API - Currency
|
|
45
|
+
const OpenAPIExample: WithMessage[] = [
|
|
46
|
+
{ role: "user", text: "What's the USD to SEK exchange rate?" },
|
|
47
|
+
{ role: "assistant", text: "Open API → Frankfurter", search: true },
|
|
48
|
+
{ role: "assistant", text: "No API key needed", meta: "Free, open access" },
|
|
49
|
+
{ role: "assistant", text: "Fetching rate...", typing: true },
|
|
50
|
+
{ role: "assistant", text: "Current rate", currency: true, success: true },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Example 3: Discovery - Search 22k APIs
|
|
54
|
+
const DiscoveryExample: WithMessage[] = [
|
|
55
|
+
{ role: "user", text: "I need to transcribe meeting recordings" },
|
|
56
|
+
{ role: "assistant", text: "Searching 22,000+ APIs...", search: true },
|
|
57
|
+
{
|
|
58
|
+
role: "assistant",
|
|
59
|
+
text: "Found 4 matches",
|
|
60
|
+
results: [
|
|
61
|
+
{ name: "Deepgram", match: "96%", cost: "$0.0043/min" },
|
|
62
|
+
{ name: "AssemblyAI", match: "94%", cost: "$0.0065/min" },
|
|
63
|
+
{ name: "Rev.ai", match: "91%", cost: "$0.02/min" },
|
|
64
|
+
{ name: "Google STT", match: "89%", cost: "$0.006/min" },
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{ role: "assistant", text: "Deepgram recommended", meta: "Best accuracy + pricing", success: true },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const WithoutAPIClaw: WithoutMessage[] = [
|
|
71
|
+
{ role: "step", text: "Search for the right API" },
|
|
72
|
+
{ role: "step", text: "Open 12 tabs, compare providers" },
|
|
73
|
+
{ role: "step", text: "Read documentation for each" },
|
|
74
|
+
{ role: "step", text: "Create account, verify email" },
|
|
75
|
+
{ role: "step", text: "Set up billing, generate key" },
|
|
76
|
+
{ role: "step", text: "Store key securely in .env" },
|
|
77
|
+
{ role: "step", text: "Write API integration code" },
|
|
78
|
+
{ role: "step", text: "Debug authentication errors" },
|
|
79
|
+
{ role: "step", text: "Finally make first API call" },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const examples = [
|
|
83
|
+
{ id: "direct", label: "Direct Call", messages: DirectCallExample },
|
|
84
|
+
{ id: "open", label: "Open API", messages: OpenAPIExample },
|
|
85
|
+
{ id: "discovery", label: "Discovery", messages: DiscoveryExample },
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
// OpenAI-style logo
|
|
89
|
+
function OpenAILogo({ className }: { className?: string }) {
|
|
90
|
+
return (
|
|
91
|
+
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
|
92
|
+
<path d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023l-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.795.795 0 0 0-.393.681zm1.097-2.365l2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z" />
|
|
93
|
+
</svg>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function PhoneDemo() {
|
|
98
|
+
const [withClaw, setWithClaw] = useState(true);
|
|
99
|
+
const [exampleIndex, setExampleIndex] = useState(0);
|
|
100
|
+
const [visibleMessages, setVisibleMessages] = useState(0);
|
|
101
|
+
const [isPaused, setIsPaused] = useState(false);
|
|
102
|
+
const [completedSteps, setCompletedSteps] = useState<number[]>([]);
|
|
103
|
+
|
|
104
|
+
const currentExample = examples[exampleIndex];
|
|
105
|
+
const messages: Message[] = withClaw ? currentExample.messages : WithoutAPIClaw;
|
|
106
|
+
|
|
107
|
+
// Reset when switching modes or examples
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
setVisibleMessages(0);
|
|
110
|
+
setCompletedSteps([]);
|
|
111
|
+
}, [exampleIndex, withClaw]);
|
|
112
|
+
|
|
113
|
+
// Animate messages appearing
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (visibleMessages >= messages.length) return;
|
|
116
|
+
|
|
117
|
+
const delay = withClaw ? 800 : 700;
|
|
118
|
+
const timer = setTimeout(() => {
|
|
119
|
+
setVisibleMessages(v => v + 1);
|
|
120
|
+
// For "Without" mode, mark previous step as completed after a delay
|
|
121
|
+
if (!withClaw && visibleMessages > 0) {
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
setCompletedSteps(prev => [...prev, visibleMessages - 1]);
|
|
124
|
+
}, 300);
|
|
125
|
+
}
|
|
126
|
+
}, delay);
|
|
127
|
+
|
|
128
|
+
return () => clearTimeout(timer);
|
|
129
|
+
}, [visibleMessages, messages.length, withClaw]);
|
|
130
|
+
|
|
131
|
+
// Mark last step as completed when all visible
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
if (!withClaw && visibleMessages === messages.length && visibleMessages > 0) {
|
|
134
|
+
const timer = setTimeout(() => {
|
|
135
|
+
setCompletedSteps(prev => [...prev, visibleMessages - 1]);
|
|
136
|
+
}, 500);
|
|
137
|
+
return () => clearTimeout(timer);
|
|
138
|
+
}
|
|
139
|
+
}, [visibleMessages, messages.length, withClaw]);
|
|
140
|
+
|
|
141
|
+
// Auto-rotate examples (only when withClaw is true)
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!withClaw || isPaused) return;
|
|
144
|
+
if (visibleMessages < messages.length) return;
|
|
145
|
+
|
|
146
|
+
const timer = setTimeout(() => {
|
|
147
|
+
setExampleIndex((i) => (i + 1) % examples.length);
|
|
148
|
+
}, 3500);
|
|
149
|
+
|
|
150
|
+
return () => clearTimeout(timer);
|
|
151
|
+
}, [visibleMessages, messages.length, withClaw, isPaused]);
|
|
152
|
+
|
|
153
|
+
const handleDotClick = useCallback((index: number) => {
|
|
154
|
+
setExampleIndex(index);
|
|
155
|
+
setIsPaused(true);
|
|
156
|
+
setTimeout(() => setIsPaused(false), 15000);
|
|
157
|
+
}, []);
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<div className="w-full max-w-sm mx-auto">
|
|
161
|
+
{/* Toggle */}
|
|
162
|
+
<div className="flex items-center justify-center gap-3 mb-6">
|
|
163
|
+
<button
|
|
164
|
+
onClick={() => setWithClaw(true)}
|
|
165
|
+
className={`px-5 py-2.5 rounded-full font-medium text-sm transition-all duration-200 ${
|
|
166
|
+
withClaw
|
|
167
|
+
? "bg-zinc-900 text-white shadow-lg"
|
|
168
|
+
: "bg-zinc-100 text-zinc-600 hover:bg-zinc-200"
|
|
169
|
+
}`}
|
|
170
|
+
>
|
|
171
|
+
With APIClaw
|
|
172
|
+
</button>
|
|
173
|
+
<button
|
|
174
|
+
onClick={() => setWithClaw(false)}
|
|
175
|
+
className={`px-5 py-2.5 rounded-full font-medium text-sm transition-all duration-200 ${
|
|
176
|
+
!withClaw
|
|
177
|
+
? "bg-zinc-900 text-white shadow-lg"
|
|
178
|
+
: "bg-zinc-100 text-zinc-600 hover:bg-zinc-200"
|
|
179
|
+
}`}
|
|
180
|
+
>
|
|
181
|
+
Without
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{/* Example indicator (only show when withClaw) */}
|
|
186
|
+
{withClaw && (
|
|
187
|
+
<div className="flex items-center justify-center gap-2 mb-4">
|
|
188
|
+
{examples.map((ex, i) => (
|
|
189
|
+
<button
|
|
190
|
+
key={ex.id}
|
|
191
|
+
onClick={() => handleDotClick(i)}
|
|
192
|
+
className={`px-3 py-1.5 rounded-full text-xs font-medium transition-all duration-200 ${
|
|
193
|
+
i === exampleIndex
|
|
194
|
+
? "bg-zinc-900 text-white"
|
|
195
|
+
: "bg-zinc-100 text-zinc-500 hover:bg-zinc-200"
|
|
196
|
+
}`}
|
|
197
|
+
>
|
|
198
|
+
{ex.label}
|
|
199
|
+
</button>
|
|
200
|
+
))}
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
{/* ChatGPT-style Phone Frame */}
|
|
205
|
+
<div className="relative mx-auto" style={{ maxWidth: "340px" }}>
|
|
206
|
+
<div className="relative bg-zinc-900 rounded-[2.5rem] p-2 shadow-2xl">
|
|
207
|
+
<div className="bg-white rounded-[2.2rem] overflow-hidden min-h-[480px] flex flex-col">
|
|
208
|
+
{/* Status bar */}
|
|
209
|
+
<div className="flex items-center justify-between px-6 py-2 text-xs text-zinc-900 font-medium">
|
|
210
|
+
<span>9:41</span>
|
|
211
|
+
<div className="flex items-center gap-1">
|
|
212
|
+
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9-4.03-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z"/></svg>
|
|
213
|
+
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M17 4h-3V2h-4v2H7v18h10V4zm-2 16H9V6h6v14z"/></svg>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
{/* App Header */}
|
|
218
|
+
<div className="flex items-center justify-between px-4 py-3 border-b border-zinc-100">
|
|
219
|
+
<div className="flex items-center gap-3">
|
|
220
|
+
{withClaw ? (
|
|
221
|
+
<div className="w-8 h-8 rounded-full bg-zinc-900 flex items-center justify-center">
|
|
222
|
+
<OpenAILogo className="w-5 h-5 text-white" />
|
|
223
|
+
</div>
|
|
224
|
+
) : (
|
|
225
|
+
<div className="w-8 h-8 rounded-full bg-orange-500 flex items-center justify-center">
|
|
226
|
+
<svg className="w-5 h-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
227
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
228
|
+
</svg>
|
|
229
|
+
</div>
|
|
230
|
+
)}
|
|
231
|
+
<div>
|
|
232
|
+
<div className="text-zinc-900 font-semibold text-sm">
|
|
233
|
+
{withClaw ? "Agent + APIClaw" : "Manual Workflow"}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
{/* Messages area */}
|
|
240
|
+
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-3 bg-white">
|
|
241
|
+
{messages.slice(0, visibleMessages).map((msg, i) => (
|
|
242
|
+
<div
|
|
243
|
+
key={`${withClaw}-${exampleIndex}-${i}`}
|
|
244
|
+
className="transition-all duration-500 ease-out"
|
|
245
|
+
style={{
|
|
246
|
+
opacity: 1,
|
|
247
|
+
transform: 'translateY(0)',
|
|
248
|
+
animation: 'slideUp 0.4s ease-out'
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
{msg.role === "user" ? (
|
|
252
|
+
<div className="flex justify-end">
|
|
253
|
+
<div className="bg-zinc-100 text-zinc-900 px-4 py-2.5 rounded-2xl rounded-br-md max-w-[85%] text-sm">
|
|
254
|
+
{msg.text}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
) : msg.role === "step" ? (
|
|
258
|
+
<div className="flex items-center gap-3 transition-all duration-300">
|
|
259
|
+
<div className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium flex-shrink-0 transition-all duration-300 ${
|
|
260
|
+
completedSteps.includes(i)
|
|
261
|
+
? "bg-green-500 text-white scale-100"
|
|
262
|
+
: "bg-zinc-100 text-zinc-500"
|
|
263
|
+
}`}>
|
|
264
|
+
{completedSteps.includes(i) ? (
|
|
265
|
+
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
|
|
266
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
267
|
+
</svg>
|
|
268
|
+
) : (
|
|
269
|
+
i + 1
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
<span className={`text-sm transition-all duration-300 ${
|
|
273
|
+
completedSteps.includes(i) ? "text-zinc-400" : "text-zinc-700"
|
|
274
|
+
}`}>
|
|
275
|
+
{msg.text}
|
|
276
|
+
</span>
|
|
277
|
+
{!completedSteps.includes(i) && i === visibleMessages - 1 && (
|
|
278
|
+
<div className="flex gap-0.5 ml-1">
|
|
279
|
+
<span className="w-1 h-1 bg-zinc-400 rounded-full animate-pulse" />
|
|
280
|
+
<span className="w-1 h-1 bg-zinc-400 rounded-full animate-pulse" style={{ animationDelay: "0.15s" }} />
|
|
281
|
+
<span className="w-1 h-1 bg-zinc-400 rounded-full animate-pulse" style={{ animationDelay: "0.3s" }} />
|
|
282
|
+
</div>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
) : (
|
|
286
|
+
<div className="flex gap-3">
|
|
287
|
+
<div className="w-7 h-7 rounded-full bg-zinc-900 flex items-center justify-center flex-shrink-0">
|
|
288
|
+
<OpenAILogo className="w-4 h-4 text-white" />
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<div className="flex-1 min-w-0">
|
|
292
|
+
{msg.search && (
|
|
293
|
+
<div className="flex items-center gap-2 text-zinc-700 text-sm py-1 font-medium">
|
|
294
|
+
<svg className="w-4 h-4 text-zinc-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
295
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
296
|
+
</svg>
|
|
297
|
+
{msg.text}
|
|
298
|
+
</div>
|
|
299
|
+
)}
|
|
300
|
+
{msg.models && (
|
|
301
|
+
<div className="space-y-2">
|
|
302
|
+
<div className="text-zinc-900 text-sm font-medium">{msg.text}</div>
|
|
303
|
+
<div className="space-y-1.5">
|
|
304
|
+
{msg.models.map((m, j) => (
|
|
305
|
+
<div key={j} className={`flex items-center justify-between text-xs px-3 py-2 rounded-lg transition-all duration-200 ${
|
|
306
|
+
j === 0 ? "bg-zinc-900 text-white" : "bg-zinc-50 text-zinc-600"
|
|
307
|
+
}`}>
|
|
308
|
+
<span className={j === 0 ? "font-medium" : ""}>{m.name}</span>
|
|
309
|
+
<span className={j === 0 ? "text-zinc-300" : "text-zinc-400"}>{m.match}</span>
|
|
310
|
+
</div>
|
|
311
|
+
))}
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
)}
|
|
315
|
+
{msg.results && (
|
|
316
|
+
<div className="space-y-2">
|
|
317
|
+
<div className="text-zinc-900 text-sm font-medium">{msg.text}</div>
|
|
318
|
+
<div className="space-y-1.5">
|
|
319
|
+
{msg.results.map((r, j) => (
|
|
320
|
+
<div key={j} className={`flex items-center justify-between text-xs px-3 py-2 rounded-lg transition-all duration-200 ${
|
|
321
|
+
j === 0 ? "bg-zinc-900 text-white" : "bg-zinc-50 text-zinc-600"
|
|
322
|
+
}`}>
|
|
323
|
+
<span className={j === 0 ? "font-medium" : ""}>{r.name}</span>
|
|
324
|
+
<div className="flex items-center gap-2">
|
|
325
|
+
<span className={j === 0 ? "text-zinc-300" : "text-zinc-400"}>{r.match}</span>
|
|
326
|
+
<span className={j === 0 ? "text-zinc-500" : "text-zinc-300"}>·</span>
|
|
327
|
+
<span className={j === 0 ? "text-zinc-300" : "text-zinc-400"}>{r.cost}</span>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
))}
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
{!msg.search && !msg.results && !msg.models && (
|
|
335
|
+
<div className="text-zinc-800 text-sm flex items-center gap-2 py-1">
|
|
336
|
+
{msg.success && (
|
|
337
|
+
<svg className="w-4 h-4 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
338
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
339
|
+
</svg>
|
|
340
|
+
)}
|
|
341
|
+
{msg.typing && (
|
|
342
|
+
<span className="flex gap-1">
|
|
343
|
+
<span className="w-1.5 h-1.5 bg-zinc-400 rounded-full animate-bounce" />
|
|
344
|
+
<span className="w-1.5 h-1.5 bg-zinc-400 rounded-full animate-bounce" style={{ animationDelay: "0.15s" }} />
|
|
345
|
+
<span className="w-1.5 h-1.5 bg-zinc-400 rounded-full animate-bounce" style={{ animationDelay: "0.3s" }} />
|
|
346
|
+
</span>
|
|
347
|
+
)}
|
|
348
|
+
{msg.text}
|
|
349
|
+
</div>
|
|
350
|
+
)}
|
|
351
|
+
{msg.meta && (
|
|
352
|
+
<div className="text-zinc-400 text-xs mt-0.5">{msg.meta}</div>
|
|
353
|
+
)}
|
|
354
|
+
{msg.image && (
|
|
355
|
+
<div className="mt-2 w-44 aspect-square rounded-xl overflow-hidden shadow-md">
|
|
356
|
+
<Image
|
|
357
|
+
src="/demo-product.jpg"
|
|
358
|
+
alt="Generated product image"
|
|
359
|
+
width={176}
|
|
360
|
+
height={176}
|
|
361
|
+
className="w-full h-full object-cover"
|
|
362
|
+
/>
|
|
363
|
+
</div>
|
|
364
|
+
)}
|
|
365
|
+
{msg.currency && (
|
|
366
|
+
<div className="mt-2 bg-zinc-50 border border-zinc-200 rounded-xl p-3 inline-block">
|
|
367
|
+
<div className="flex items-center gap-3">
|
|
368
|
+
<div className="text-2xl font-bold text-zinc-900">10.82</div>
|
|
369
|
+
<div className="text-sm text-zinc-500">
|
|
370
|
+
<div className="font-medium">USD → SEK</div>
|
|
371
|
+
<div className="text-xs text-zinc-400">Live rate</div>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
)}
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
))}
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
{/* Input area */}
|
|
384
|
+
<div className="p-3 border-t border-zinc-100">
|
|
385
|
+
<div className="flex items-center gap-2 bg-zinc-50 border border-zinc-200 rounded-2xl px-4 py-3">
|
|
386
|
+
<input
|
|
387
|
+
type="text"
|
|
388
|
+
placeholder="Message..."
|
|
389
|
+
className="flex-1 bg-transparent text-sm text-zinc-900 placeholder-zinc-400 outline-none"
|
|
390
|
+
readOnly
|
|
391
|
+
/>
|
|
392
|
+
<button className="w-7 h-7 bg-zinc-900 rounded-full flex items-center justify-center">
|
|
393
|
+
<svg className="w-3.5 h-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
394
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 10l7-7m0 0l7 7m-7-7v18" />
|
|
395
|
+
</svg>
|
|
396
|
+
</button>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
{/* Home indicator */}
|
|
401
|
+
<div className="flex justify-center pb-2">
|
|
402
|
+
<div className="w-32 h-1 bg-zinc-900 rounded-full" />
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
408
|
+
{/* CSS for animations */}
|
|
409
|
+
<style jsx>{`
|
|
410
|
+
@keyframes slideUp {
|
|
411
|
+
from {
|
|
412
|
+
opacity: 0;
|
|
413
|
+
transform: translateY(10px);
|
|
414
|
+
}
|
|
415
|
+
to {
|
|
416
|
+
opacity: 1;
|
|
417
|
+
transform: translateY(0);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
`}</style>
|
|
421
|
+
</div>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PhoneDemo } from './PhoneDemo';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"apiCount": 22392,
|
|
3
3
|
"openApiCount": 1636,
|
|
4
|
-
"directCallCount":
|
|
4
|
+
"directCallCount": 18,
|
|
5
5
|
"categoryCount": 13,
|
|
6
6
|
"lastUpdated": "2026-02-27T09:10:41.344767",
|
|
7
|
-
"generatedAt": "2026-03-
|
|
7
|
+
"generatedAt": "2026-03-01T16:05:56.209Z",
|
|
8
8
|
"categoryBreakdown": {
|
|
9
9
|
"Finance": 1179,
|
|
10
10
|
"Auth & Security": 491,
|
|
@@ -28,8 +28,18 @@ const config: Config = {
|
|
|
28
28
|
'tighter': '-0.03em',
|
|
29
29
|
'widest': '0.15em',
|
|
30
30
|
},
|
|
31
|
+
animation: {
|
|
32
|
+
'fade-in': 'fadeIn 0.3s ease-out forwards',
|
|
33
|
+
},
|
|
34
|
+
keyframes: {
|
|
35
|
+
fadeIn: {
|
|
36
|
+
'0%': { opacity: '0', transform: 'translateY(8px)' },
|
|
37
|
+
'100%': { opacity: '1', transform: 'translateY(0)' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
31
40
|
},
|
|
32
41
|
},
|
|
33
42
|
plugins: [],
|
|
34
43
|
};
|
|
44
|
+
|
|
35
45
|
export default config;
|
package/package.json
CHANGED
package/src/bin.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - setup/doctor/restore/uninstall → Run CLI
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
const cliCommands = ['setup', 'doctor', 'restore', 'uninstall', 'help', '--help', '-h', '--version', '-V'];
|
|
9
|
+
const cliCommands = ['setup', 'mcp-install', 'doctor', 'restore', 'uninstall', 'help', '--help', '-h', '--version', '-V'];
|
|
10
10
|
|
|
11
11
|
const firstArg = process.argv[2];
|
|
12
12
|
|