@fraczled/sdk 1.0.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.
@@ -0,0 +1,560 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Fraczled SDK - Start Your Trial</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
+ <link
11
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
12
+ rel="stylesheet"
13
+ />
14
+ <script>
15
+ tailwind.config = {
16
+ theme: {
17
+ extend: {
18
+ fontFamily: {
19
+ sans: ["Inter", "sans-serif"]
20
+ },
21
+ colors: {
22
+ brand: {
23
+ 50: "#eef2ff",
24
+ 100: "#e0e7ff",
25
+ 500: "#6366f1",
26
+ 600: "#4f46e5",
27
+ 700: "#4338ca",
28
+ 800: "#3730a3",
29
+ 900: "#312e81"
30
+ }
31
+ }
32
+ }
33
+ }
34
+ };
35
+ </script>
36
+ <style>
37
+ body {
38
+ font-family: "Inter", sans-serif;
39
+ }
40
+
41
+ @keyframes blob {
42
+ 0% {
43
+ transform: translate(0px, 0px) scale(1);
44
+ }
45
+ 33% {
46
+ transform: translate(30px, -50px) scale(1.1);
47
+ }
48
+ 66% {
49
+ transform: translate(-20px, 20px) scale(0.9);
50
+ }
51
+ 100% {
52
+ transform: translate(0px, 0px) scale(1);
53
+ }
54
+ }
55
+
56
+ .animate-blob {
57
+ animation: blob 7s infinite;
58
+ }
59
+
60
+ .animation-delay-2000 {
61
+ animation-delay: 2s;
62
+ }
63
+ </style>
64
+ </head>
65
+ <body>
66
+ <div class="min-h-screen flex flex-col lg:flex-row bg-white">
67
+ <div class="w-full lg:w-1/2 lg:min-h-screen relative overflow-hidden order-1 lg:order-none">
68
+ <div class="relative w-full h-full bg-brand-900 text-white p-10 lg:p-20 flex flex-col justify-between overflow-hidden">
69
+ <div class="absolute top-0 right-0 -mr-20 -mt-20 w-96 h-96 bg-brand-600 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob"></div>
70
+ <div class="absolute bottom-0 left-0 -ml-20 -mb-20 w-96 h-96 bg-purple-600 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-2000"></div>
71
+
72
+ <div class="relative z-10">
73
+ <div class="flex items-center gap-3 mb-8">
74
+ <div class="p-2 bg-white/10 rounded-lg backdrop-blur-sm">
75
+ <i data-lucide="cpu" class="w-8 h-8 text-brand-100"></i>
76
+ </div>
77
+ <span class="text-2xl font-bold tracking-tight">Fraczled SDK</span>
78
+ </div>
79
+
80
+ <h1 class="text-4xl lg:text-5xl font-bold leading-tight mb-6">
81
+ Build design-driven apps <br />
82
+ <span class="text-transparent bg-clip-text bg-gradient-to-r from-blue-200 to-indigo-300">
83
+ faster than ever.
84
+ </span>
85
+ </h1>
86
+
87
+ <p class="text-lg text-brand-100 max-w-md leading-relaxed">
88
+ Unlock the power of generative design directly in your codebase.
89
+ Seamless integration, production-ready assets, and infinite scalability.
90
+ </p>
91
+ </div>
92
+
93
+ <div class="relative z-10 mt-12 hidden lg:block">
94
+ <div class="bg-gray-900 rounded-xl border border-gray-700 shadow-2xl overflow-hidden transform rotate-1 hover:rotate-0 transition-transform duration-500">
95
+ <div class="flex items-center gap-2 px-4 py-3 bg-gray-800 border-b border-gray-700">
96
+ <div class="w-3 h-3 rounded-full bg-red-500"></div>
97
+ <div class="w-3 h-3 rounded-full bg-yellow-500"></div>
98
+ <div class="w-3 h-3 rounded-full bg-green-500"></div>
99
+ <span class="ml-2 text-xs text-gray-400 font-mono">example.tsx</span>
100
+ </div>
101
+ <div class="p-6 font-mono text-sm">
102
+ <div class="flex">
103
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">1</span>
104
+ <span class="text-gray-400">// $ npm install</span>
105
+ <span class="text-green-300 ml-2">@fraczled/sdk</span>
106
+ </div>
107
+ <div class="flex mt-1">
108
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">2</span>
109
+ </div>
110
+ <div class="flex">
111
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">3</span>
112
+ <span class="text-pink-400">import</span>
113
+ <span class="text-white mx-2">{</span>
114
+ <span class="text-yellow-300">createFraczledEditor</span>
115
+ <span class="text-white mx-2">}</span>
116
+ <span class="text-pink-400">from</span>
117
+ <span class="text-green-300 mx-2">'@fraczled/sdk'</span>;
118
+ </div>
119
+ <div class="flex mt-1">
120
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">4</span>
121
+ </div>
122
+ <div class="flex">
123
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">5</span>
124
+ <span class="text-yellow-300">createFraczledEditor</span>
125
+ <span class="text-white">({</span>
126
+ </div>
127
+ <div class="flex mt-1">
128
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">6</span>
129
+ <span class="text-blue-300 ml-4">apiKey</span>
130
+ <span class="text-white">:</span>
131
+ <span class="text-green-300 ml-2">'FRACZLED_API_KEY'</span>
132
+ <span class="text-white">,</span>
133
+ </div>
134
+ <div class="flex mt-1">
135
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">7</span>
136
+ <span class="text-blue-300 ml-4">projectId</span>
137
+ <span class="text-white">:</span>
138
+ <span class="text-green-300 ml-2">'PROJECT_ID'</span>
139
+ <span class="text-white">,</span>
140
+ </div>
141
+ <div class="flex mt-1">
142
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">8</span>
143
+ <span class="text-blue-300 ml-4">container</span>
144
+ <span class="text-white">:</span>
145
+ <span class="text-yellow-300 ml-2">document</span>
146
+ <span class="text-white">.</span>
147
+ <span class="text-blue-300">getElementById</span>
148
+ <span class="text-white">(</span>
149
+ <span class="text-green-300">'editor'</span>
150
+ <span class="text-white">)</span>
151
+ </div>
152
+ <div class="flex mt-1">
153
+ <span class="text-gray-500 w-6 text-right mr-4 select-none">9</span>
154
+ <span class="text-white">});</span>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+
160
+ <div class="relative z-10 mt-12 grid grid-cols-2 gap-6 pt-8 border-t border-white/10">
161
+ <div class="flex items-center gap-3">
162
+ <div class="p-2 bg-brand-800 rounded-full">
163
+ <i data-lucide="zap" class="w-4 h-4 text-yellow-300"></i>
164
+ </div>
165
+ <span class="text-sm font-medium text-brand-50">Zero Latency</span>
166
+ </div>
167
+ <div class="flex items-center gap-3">
168
+ <div class="p-2 bg-brand-800 rounded-full">
169
+ <i data-lucide="layers" class="w-4 h-4 text-blue-300"></i>
170
+ </div>
171
+ <span class="text-sm font-medium text-brand-50">Full Stack Support</span>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+
177
+ <div class="w-full lg:w-1/2 min-h-screen flex items-center justify-center p-6 sm:p-12 lg:p-16 order-2 lg:order-none bg-white">
178
+ <div class="w-full max-w-md">
179
+ <div class="mb-10">
180
+ <h2 class="text-3xl font-bold text-gray-900 mb-2">Get started for free</h2>
181
+ <p class="text-gray-600">Start your 100-day free trial. No credit card required.</p>
182
+ </div>
183
+
184
+ <form id="signup-form" class="space-y-6" novalidate>
185
+ <div>
186
+ <label for="email" class="block text-sm font-medium text-gray-700 mb-1.5">Work Email</label>
187
+ <div class="relative">
188
+ <input
189
+ id="email"
190
+ name="email"
191
+ type="email"
192
+ placeholder="you@company.com"
193
+ class="appearance-none block w-full px-3 py-3 border rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 sm:text-sm transition-all border-gray-300 focus:ring-brand-500 focus:border-brand-500"
194
+ aria-invalid="false"
195
+ />
196
+ <div
197
+ class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none hidden"
198
+ id="email-error-icon"
199
+ >
200
+ <i data-lucide="alert-circle" class="h-5 w-5 text-red-500"></i>
201
+ </div>
202
+ </div>
203
+ <p
204
+ class="mt-2 text-sm text-red-600 flex items-center gap-1 animate-pulse hidden"
205
+ id="email-error"
206
+ ></p>
207
+ </div>
208
+
209
+ <div>
210
+ <label for="companyName" class="block text-sm font-medium text-gray-700 mb-1.5">Company Name</label>
211
+ <div class="relative">
212
+ <input
213
+ id="companyName"
214
+ name="companyName"
215
+ type="text"
216
+ placeholder="Acme Inc."
217
+ class="appearance-none block w-full px-3 py-3 border rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 sm:text-sm transition-all border-gray-300 focus:ring-brand-500 focus:border-brand-500"
218
+ aria-invalid="false"
219
+ />
220
+ <div
221
+ class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none hidden"
222
+ id="companyName-error-icon"
223
+ >
224
+ <i data-lucide="alert-circle" class="h-5 w-5 text-red-500"></i>
225
+ </div>
226
+ </div>
227
+ <p
228
+ class="mt-2 text-sm text-red-600 flex items-center gap-1 animate-pulse hidden"
229
+ id="companyName-error"
230
+ ></p>
231
+ </div>
232
+
233
+ <div>
234
+ <label for="projectName" class="block text-sm font-medium text-gray-700 mb-1.5">Project Name</label>
235
+ <div class="relative">
236
+ <input
237
+ id="projectName"
238
+ name="projectName"
239
+ type="text"
240
+ placeholder="My Design App"
241
+ class="appearance-none block w-full px-3 py-3 border rounded-lg shadow-sm placeholder-gray-400 focus:outline-none focus:ring-2 sm:text-sm transition-all border-gray-300 focus:ring-brand-500 focus:border-brand-500"
242
+ aria-invalid="false"
243
+ />
244
+ <div
245
+ class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none hidden"
246
+ id="projectName-error-icon"
247
+ >
248
+ <i data-lucide="alert-circle" class="h-5 w-5 text-red-500"></i>
249
+ </div>
250
+ </div>
251
+ <p
252
+ class="mt-2 text-sm text-red-600 flex items-center gap-1 animate-pulse hidden"
253
+ id="projectName-error"
254
+ ></p>
255
+ </div>
256
+
257
+ <button
258
+ type="submit"
259
+ id="submit-btn"
260
+ class="w-full flex items-center justify-center py-3.5 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-brand-600 hover:bg-brand-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brand-500 transition-colors disabled:opacity-70 disabled:cursor-not-allowed"
261
+ >
262
+ <span id="btn-idle" class="inline-flex items-center">
263
+ Start Free Trial
264
+ <i data-lucide="arrow-right" class="ml-2 h-4 w-4"></i>
265
+ </span>
266
+ <span id="btn-loading" class="hidden inline-flex items-center">
267
+ <i data-lucide="loader-2" class="animate-spin -ml-1 mr-2 h-4 w-4"></i>
268
+ Creating Account...
269
+ </span>
270
+ </button>
271
+ </form>
272
+
273
+ <div id="result" class="hidden mt-6"></div>
274
+
275
+ <div class="mt-10 pt-6 border-t border-gray-100">
276
+ <h3 class="text-sm font-semibold text-gray-900 mb-4">Everything you get inside:</h3>
277
+ <ul class="space-y-3">
278
+ <li class="flex items-start">
279
+ <i data-lucide="check-circle-2" class="h-5 w-5 text-green-500 mr-3 shrink-0"></i>
280
+ <span class="text-sm text-gray-600">100-day development trial</span>
281
+ </li>
282
+ <li class="flex items-start">
283
+ <i data-lucide="check-circle-2" class="h-5 w-5 text-green-500 mr-3 shrink-0"></i>
284
+ <span class="text-sm text-gray-600">Full feature access on localhost</span>
285
+ </li>
286
+ <li class="flex items-start">
287
+ <i data-lucide="check-circle-2" class="h-5 w-5 text-green-500 mr-3 shrink-0"></i>
288
+ <span class="text-sm text-gray-600">AI-powered design generation</span>
289
+ </li>
290
+ <li class="flex items-start">
291
+ <i data-lucide="check-circle-2" class="h-5 w-5 text-green-500 mr-3 shrink-0"></i>
292
+ <span class="text-sm text-gray-600">Export to Web & Print formats</span>
293
+ </li>
294
+ <li class="flex items-start">
295
+ <i data-lucide="check-circle-2" class="h-5 w-5 text-green-500 mr-3 shrink-0"></i>
296
+ <span class="text-sm text-gray-600">Priority community support</span>
297
+ </li>
298
+ </ul>
299
+
300
+ <p class="mt-8 text-xs text-center text-gray-400">
301
+ Need production access immediately? <br />
302
+ <a
303
+ href="#"
304
+ class="text-brand-600 hover:text-brand-500 underline decoration-1 underline-offset-2"
305
+ >
306
+ Contact Sales
307
+ </a>
308
+ for plans starting at $150/month.
309
+ </p>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+
315
+ <script src="https://unpkg.com/lucide@latest"></script>
316
+ <script>
317
+ (function () {
318
+ if (window.lucide && window.lucide.createIcons) {
319
+ window.lucide.createIcons();
320
+ }
321
+
322
+ const form = document.getElementById("signup-form");
323
+ const submitBtn = document.getElementById("submit-btn");
324
+ const btnIdle = document.getElementById("btn-idle");
325
+ const btnLoading = document.getElementById("btn-loading");
326
+ const resultDiv = document.getElementById("result");
327
+
328
+ if (!form || !submitBtn || !btnIdle || !btnLoading || !resultDiv) {
329
+ return;
330
+ }
331
+
332
+ const fields = {
333
+ email: {
334
+ input: document.getElementById("email"),
335
+ errorText: document.getElementById("email-error"),
336
+ errorIcon: document.getElementById("email-error-icon")
337
+ },
338
+ companyName: {
339
+ input: document.getElementById("companyName"),
340
+ errorText: document.getElementById("companyName-error"),
341
+ errorIcon: document.getElementById("companyName-error-icon")
342
+ },
343
+ projectName: {
344
+ input: document.getElementById("projectName"),
345
+ errorText: document.getElementById("projectName-error"),
346
+ errorIcon: document.getElementById("projectName-error-icon")
347
+ }
348
+ };
349
+
350
+ const touched = {
351
+ email: false,
352
+ companyName: false,
353
+ projectName: false
354
+ };
355
+
356
+ const errors = {
357
+ email: undefined,
358
+ companyName: undefined,
359
+ projectName: undefined
360
+ };
361
+
362
+ const defaultClasses = ["border-gray-300", "focus:ring-brand-500", "focus:border-brand-500"];
363
+ const errorClasses = [
364
+ "border-red-300",
365
+ "text-red-900",
366
+ "focus:ring-red-500",
367
+ "focus:border-red-500",
368
+ "pr-10"
369
+ ];
370
+
371
+ const validateField = (name, value) => {
372
+ switch (name) {
373
+ case "email":
374
+ if (!value) return "Email is required";
375
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
376
+ return "Please enter a valid email address";
377
+ }
378
+ return undefined;
379
+ case "companyName":
380
+ if (!value) return "Company name is required";
381
+ if (value.length < 2) return "Company name must be at least 2 characters";
382
+ return undefined;
383
+ case "projectName":
384
+ if (!value) return "Project name is required";
385
+ if (value.length < 2) return "Project name must be at least 2 characters";
386
+ return undefined;
387
+ default:
388
+ return undefined;
389
+ }
390
+ };
391
+
392
+ const applyError = (name, message) => {
393
+ const field = fields[name];
394
+ if (!field || !field.input || !field.errorText || !field.errorIcon) {
395
+ return;
396
+ }
397
+
398
+ const hasError = Boolean(message);
399
+
400
+ field.errorText.textContent = message || "";
401
+ field.errorText.classList.toggle("hidden", !hasError);
402
+ field.errorIcon.classList.toggle("hidden", !hasError);
403
+
404
+ defaultClasses.forEach((cls) => field.input.classList.toggle(cls, !hasError));
405
+ errorClasses.forEach((cls) => field.input.classList.toggle(cls, hasError));
406
+
407
+ field.input.setAttribute("aria-invalid", hasError ? "true" : "false");
408
+ if (hasError) {
409
+ field.input.setAttribute("aria-describedby", `${name}-error`);
410
+ } else {
411
+ field.input.removeAttribute("aria-describedby");
412
+ }
413
+ };
414
+
415
+ const handleInput = (event) => {
416
+ const target = event.target;
417
+ if (!target || !target.name) return;
418
+
419
+ const name = target.name;
420
+ if (!(name in fields)) return;
421
+
422
+ if (touched[name] || errors[name]) {
423
+ const message = validateField(name, target.value.trim());
424
+ errors[name] = message;
425
+ applyError(name, message);
426
+ }
427
+ };
428
+
429
+ const handleBlur = (event) => {
430
+ const target = event.target;
431
+ if (!target || !target.name) return;
432
+
433
+ const name = target.name;
434
+ if (!(name in fields)) return;
435
+
436
+ touched[name] = true;
437
+ const message = validateField(name, target.value.trim());
438
+ errors[name] = message;
439
+ applyError(name, message);
440
+ };
441
+
442
+ Object.keys(fields).forEach((name) => {
443
+ const input = fields[name].input;
444
+ if (!input) return;
445
+ input.addEventListener("input", handleInput);
446
+ input.addEventListener("blur", handleBlur);
447
+ });
448
+
449
+ const setLoading = (isLoading) => {
450
+ submitBtn.disabled = isLoading;
451
+ btnIdle.classList.toggle("hidden", isLoading);
452
+ btnLoading.classList.toggle("hidden", !isLoading);
453
+ };
454
+
455
+ form.addEventListener("submit", async (event) => {
456
+ event.preventDefault();
457
+
458
+ const emailValue = fields.email.input.value.trim();
459
+ const companyValue = fields.companyName.input.value.trim();
460
+ const projectValue = fields.projectName.input.value.trim();
461
+
462
+ const emailError = validateField("email", emailValue);
463
+ const companyError = validateField("companyName", companyValue);
464
+ const projectError = validateField("projectName", projectValue);
465
+
466
+ errors.email = emailError;
467
+ errors.companyName = companyError;
468
+ errors.projectName = projectError;
469
+
470
+ applyError("email", emailError);
471
+ applyError("companyName", companyError);
472
+ applyError("projectName", projectError);
473
+
474
+ touched.email = true;
475
+ touched.companyName = true;
476
+ touched.projectName = true;
477
+
478
+ if (emailError || companyError || projectError) {
479
+ return;
480
+ }
481
+
482
+ setLoading(true);
483
+ resultDiv.classList.add("hidden");
484
+ resultDiv.innerHTML = "";
485
+
486
+ try {
487
+ const response = await fetch("/api/signup", {
488
+ method: "POST",
489
+ headers: { "Content-Type": "application/json" },
490
+ body: JSON.stringify({
491
+ email: emailValue,
492
+ companyName: companyValue,
493
+ projectName: projectValue
494
+ })
495
+ });
496
+
497
+ const result = await response.json();
498
+
499
+ if (!response.ok || !result.success) {
500
+ const message = result?.message || result?.error || "Signup failed";
501
+ throw new Error(message);
502
+ }
503
+
504
+ const expiresLabel = result.expiresAt
505
+ ? new Date(result.expiresAt).toLocaleDateString()
506
+ : "—";
507
+ const successMessage = result.message || "Your development key is ready.";
508
+ const quickstartBlock = result.quickstart
509
+ ? `
510
+ <div class="mt-4 rounded-lg bg-gray-900 text-green-200 text-xs font-mono whitespace-pre overflow-x-auto p-3">
511
+ ${result.quickstart.install}
512
+
513
+ ${result.quickstart.usage}
514
+ </div>
515
+ `
516
+ : "";
517
+
518
+ resultDiv.innerHTML = `
519
+ <div class="rounded-xl border border-green-200 bg-green-50 p-4 text-sm">
520
+ <div class="flex items-start gap-3">
521
+ <div class="mt-0.5">
522
+ <i data-lucide="check-circle-2" class="h-5 w-5 text-green-500"></i>
523
+ </div>
524
+ <div>
525
+ <h3 class="font-semibold text-green-900">Trial Created!</h3>
526
+ <p class="text-xs text-green-700 mt-1">${successMessage}</p>
527
+ </div>
528
+ </div>
529
+ <div class="mt-4 space-y-2 text-xs text-gray-700">
530
+ <p><span class="font-semibold text-gray-700">API Key:</span> <code class="bg-white/70 px-2 py-1 rounded">${result.key}</code></p>
531
+ <p><span class="font-semibold text-gray-700">Project ID:</span> <code class="bg-white/70 px-2 py-1 rounded">${result.projectId}</code></p>
532
+ <p><span class="font-semibold text-gray-700">Expires:</span> ${expiresLabel}</p>
533
+ </div>
534
+ ${quickstartBlock}
535
+ <p class="mt-3 text-[11px] text-gray-600">Save your API key - we've also sent it to your email.</p>
536
+ </div>
537
+ `;
538
+
539
+ resultDiv.classList.remove("hidden");
540
+ form.classList.add("hidden");
541
+ setLoading(false);
542
+
543
+ if (window.lucide && window.lucide.createIcons) {
544
+ window.lucide.createIcons();
545
+ }
546
+ } catch (error) {
547
+ const message = error instanceof Error ? error.message : "Signup failed";
548
+ resultDiv.innerHTML = `
549
+ <div class="rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">
550
+ ${message}
551
+ </div>
552
+ `;
553
+ resultDiv.classList.remove("hidden");
554
+ setLoading(false);
555
+ }
556
+ });
557
+ })();
558
+ </script>
559
+ </body>
560
+ </html>