@causyne/agent 1.10.20 → 1.10.21
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/docs/THIRD_PARTY_NOTICES.md +44 -0
- package/package.json +1 -1
- package/src/computerUse/visionGrounding.js +249 -0
- package/src/computerUse/visionGrounding.ts +331 -0
- package/src/sandbox.js +4 -1
- package/src/toolPacks.js +5 -3
- package/src/tools/computerUse.js +139 -0
- package/src/tools/computerUse.ts +147 -0
- package/src/tools/desktopAct.d.ts +10 -0
- package/src/tools/registry.js +4 -1
- package/src/tools/uiForge.js +132 -0
- package/src/tools/uiForge.ts +145 -0
- package/src/uiEngine/uiForge.js +211 -0
- package/src/uiEngine/uiForge.ts +258 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2011-2024 Thesys Inc.
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Adapted for Causyne UI Forge — Generative Component & Design Engine.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Generate semantic, modern HTML + CSS component code under Causyne UI Forge.
|
|
9
|
+
*/
|
|
10
|
+
export function generateHtmlComponent(spec) {
|
|
11
|
+
const isDark = spec.theme !== 'light';
|
|
12
|
+
const cardBg = isDark ? 'rgba(30, 41, 59, 0.75)' : 'rgba(255, 255, 255, 0.95)';
|
|
13
|
+
const border = isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.1)';
|
|
14
|
+
const text = isDark ? '#f8fafc' : '#0f172a';
|
|
15
|
+
const textMuted = isDark ? '#94a3b8' : '#64748b';
|
|
16
|
+
const accent = '#6366f1';
|
|
17
|
+
const accentHover = '#4f46e5';
|
|
18
|
+
const css = `
|
|
19
|
+
/* Causyne UI Forge Generated Styles - Responsive, Accessible & Performant */
|
|
20
|
+
.causyne-ui-root {
|
|
21
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
22
|
+
color: ${text};
|
|
23
|
+
line-height: 1.5;
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
}
|
|
26
|
+
.causyne-ui-root * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
27
|
+
.causyne-ui-card {
|
|
28
|
+
background: ${cardBg};
|
|
29
|
+
backdrop-filter: blur(12px);
|
|
30
|
+
border: 1px solid ${border};
|
|
31
|
+
border-radius: 12px;
|
|
32
|
+
padding: 24px;
|
|
33
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
34
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
35
|
+
}
|
|
36
|
+
.causyne-ui-card:hover {
|
|
37
|
+
transform: translateY(-2px);
|
|
38
|
+
box-shadow: 0 20px 30px -10px rgba(0, 0, 0, 0.3);
|
|
39
|
+
}
|
|
40
|
+
.causyne-ui-header { margin-bottom: 16px; }
|
|
41
|
+
.causyne-ui-title { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em; }
|
|
42
|
+
.causyne-ui-desc { font-size: 0.875rem; color: ${textMuted}; margin-top: 4px; }
|
|
43
|
+
.causyne-ui-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin: 16px 0; }
|
|
44
|
+
.causyne-ui-stat-box {
|
|
45
|
+
background: rgba(255, 255, 255, 0.03);
|
|
46
|
+
border: 1px solid ${border};
|
|
47
|
+
border-radius: 8px;
|
|
48
|
+
padding: 16px;
|
|
49
|
+
}
|
|
50
|
+
.causyne-ui-stat-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: ${textMuted}; }
|
|
51
|
+
.causyne-ui-stat-value { font-size: 1.75rem; font-weight: 700; color: ${accent}; margin-top: 4px; }
|
|
52
|
+
.causyne-ui-btn {
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
padding: 8px 16px;
|
|
57
|
+
border-radius: 6px;
|
|
58
|
+
font-size: 0.875rem;
|
|
59
|
+
font-weight: 500;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
border: 1px solid transparent;
|
|
62
|
+
transition: all 0.15s ease;
|
|
63
|
+
}
|
|
64
|
+
.causyne-ui-btn-primary { background: ${accent}; color: #ffffff; }
|
|
65
|
+
.causyne-ui-btn-primary:hover { background: ${accentHover}; }
|
|
66
|
+
.causyne-ui-btn-secondary { background: transparent; border-color: ${border}; color: ${text}; }
|
|
67
|
+
.causyne-ui-btn-secondary:hover { background: rgba(255, 255, 255, 0.05); }
|
|
68
|
+
.causyne-ui-actions { display: flex; gap: 8px; margin-top: 20px; justify-content: flex-end; }
|
|
69
|
+
`.trim();
|
|
70
|
+
let html = '';
|
|
71
|
+
const title = spec.title || 'Component';
|
|
72
|
+
const desc = spec.description || '';
|
|
73
|
+
if (spec.type === 'dashboard-stats' || (spec.items && spec.items.length > 0)) {
|
|
74
|
+
const itemsHtml = (spec.items || [])
|
|
75
|
+
.map((item) => `
|
|
76
|
+
<div class="causyne-ui-stat-box">
|
|
77
|
+
<div class="causyne-ui-stat-label">${escapeHtml(item.label)}</div>
|
|
78
|
+
<div class="causyne-ui-stat-value">${escapeHtml(String(item.value ?? '—'))}</div>
|
|
79
|
+
${item.description ? `<div class="causyne-ui-desc">${escapeHtml(item.description)}</div>` : ''}
|
|
80
|
+
</div>`)
|
|
81
|
+
.join('\n');
|
|
82
|
+
html = `
|
|
83
|
+
<div class="causyne-ui-root">
|
|
84
|
+
<div class="causyne-ui-card">
|
|
85
|
+
<div class="causyne-ui-header">
|
|
86
|
+
<h3 class="causyne-ui-title">${escapeHtml(title)}</h3>
|
|
87
|
+
${desc ? `<p class="causyne-ui-desc">${escapeHtml(desc)}</p>` : ''}
|
|
88
|
+
</div>
|
|
89
|
+
<div class="causyne-ui-grid">
|
|
90
|
+
${itemsHtml}
|
|
91
|
+
</div>
|
|
92
|
+
${renderActions(spec.actions)}
|
|
93
|
+
</div>
|
|
94
|
+
</div>`.trim();
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
html = `
|
|
98
|
+
<div class="causyne-ui-root">
|
|
99
|
+
<div class="causyne-ui-card">
|
|
100
|
+
<div class="causyne-ui-header">
|
|
101
|
+
<h3 class="causyne-ui-title">${escapeHtml(title)}</h3>
|
|
102
|
+
${desc ? `<p class="causyne-ui-desc">${escapeHtml(desc)}</p>` : ''}
|
|
103
|
+
</div>
|
|
104
|
+
${renderActions(spec.actions)}
|
|
105
|
+
</div>
|
|
106
|
+
</div>`.trim();
|
|
107
|
+
}
|
|
108
|
+
return { html, css };
|
|
109
|
+
}
|
|
110
|
+
function renderActions(actions) {
|
|
111
|
+
if (!actions || actions.length === 0)
|
|
112
|
+
return '';
|
|
113
|
+
const btns = actions
|
|
114
|
+
.map((a) => `<button class="causyne-ui-btn ${a.primary ? 'causyne-ui-btn-primary' : 'causyne-ui-btn-secondary'}" data-action="${escapeHtml(a.action)}">${escapeHtml(a.label)}</button>`)
|
|
115
|
+
.join('\n ');
|
|
116
|
+
return `
|
|
117
|
+
<div class="causyne-ui-actions">
|
|
118
|
+
${btns}
|
|
119
|
+
</div>`;
|
|
120
|
+
}
|
|
121
|
+
function escapeHtml(str) {
|
|
122
|
+
return String(str || '')
|
|
123
|
+
.replace(/&/g, '&')
|
|
124
|
+
.replace(/</g, '<')
|
|
125
|
+
.replace(/>/g, '>')
|
|
126
|
+
.replace(/"/g, '"');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Generate full self-contained preview document.
|
|
130
|
+
*/
|
|
131
|
+
export function generatePreviewHtml(html, css, theme = 'dark') {
|
|
132
|
+
const bg = theme === 'dark' ? '#0b0f19' : '#f1f5f9';
|
|
133
|
+
return `<!DOCTYPE html>
|
|
134
|
+
<html lang="en">
|
|
135
|
+
<head>
|
|
136
|
+
<meta charset="UTF-8">
|
|
137
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
138
|
+
<title>Causyne UI Forge Preview</title>
|
|
139
|
+
<style>
|
|
140
|
+
body {
|
|
141
|
+
margin: 0;
|
|
142
|
+
padding: 40px 20px;
|
|
143
|
+
min-height: 100vh;
|
|
144
|
+
background: ${bg};
|
|
145
|
+
display: flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
box-sizing: border-box;
|
|
149
|
+
}
|
|
150
|
+
${css}
|
|
151
|
+
</style>
|
|
152
|
+
</head>
|
|
153
|
+
<body>
|
|
154
|
+
${html}
|
|
155
|
+
</body>
|
|
156
|
+
</html>`;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Generate React + Tailwind component code under Causyne UI Forge.
|
|
160
|
+
*/
|
|
161
|
+
export function generateReactComponent(spec) {
|
|
162
|
+
const compName = (spec.name || 'CausyneCard').replace(/[^a-zA-Z0-9]/g, '');
|
|
163
|
+
const title = spec.title || 'Component';
|
|
164
|
+
const desc = spec.description || '';
|
|
165
|
+
return `import React from 'react';
|
|
166
|
+
|
|
167
|
+
export interface ${compName}Props {
|
|
168
|
+
title?: string;
|
|
169
|
+
description?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const ${compName}: React.FC<${compName}Props> = ({
|
|
173
|
+
title = "${title}",
|
|
174
|
+
description = "${desc}"
|
|
175
|
+
}) => {
|
|
176
|
+
return (
|
|
177
|
+
<div className="rounded-xl border border-slate-700/60 bg-slate-900/80 p-6 backdrop-blur-md shadow-xl transition-all hover:shadow-2xl">
|
|
178
|
+
<div className="mb-4">
|
|
179
|
+
<h3 className="text-xl font-semibold tracking-tight text-white">{title}</h3>
|
|
180
|
+
{description && <p className="mt-1 text-sm text-slate-400">{description}</p>}
|
|
181
|
+
</div>
|
|
182
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 my-4">
|
|
183
|
+
${(spec.items || [])
|
|
184
|
+
.map((item) => `
|
|
185
|
+
<div key="${item.id || item.label}" className="rounded-lg border border-slate-800 bg-slate-800/40 p-4">
|
|
186
|
+
<div className="text-xs font-medium uppercase tracking-wider text-slate-400">${item.label}</div>
|
|
187
|
+
<div className="mt-1 text-2xl font-bold text-indigo-400">${item.value ?? '—'}</div>
|
|
188
|
+
${item.description ? `<p className="mt-1 text-xs text-slate-500">${item.description}</p>` : ''}
|
|
189
|
+
</div>`)
|
|
190
|
+
.join('')}
|
|
191
|
+
</div>
|
|
192
|
+
${(spec.actions && spec.actions.length > 0)
|
|
193
|
+
? `
|
|
194
|
+
<div className="mt-6 flex justify-end gap-3">
|
|
195
|
+
${spec.actions
|
|
196
|
+
.map((a) => `
|
|
197
|
+
<button className="px-4 py-2 text-sm font-medium rounded-lg transition-colors ${a.primary
|
|
198
|
+
? 'bg-indigo-600 text-white hover:bg-indigo-500'
|
|
199
|
+
: 'border border-slate-700 text-slate-300 hover:bg-slate-800'}">
|
|
200
|
+
${a.label}
|
|
201
|
+
</button>`)
|
|
202
|
+
.join('')}
|
|
203
|
+
</div>`
|
|
204
|
+
: ''}
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default ${compName};
|
|
210
|
+
`;
|
|
211
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2011-2024 Thesys Inc.
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Adapted for Causyne UI Forge — Generative Component & Design Engine.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type UIFramework = 'html-css' | 'react' | 'vue' | 'svelte';
|
|
9
|
+
|
|
10
|
+
export interface UIComponentItem {
|
|
11
|
+
id?: string;
|
|
12
|
+
label: string;
|
|
13
|
+
value?: string | number;
|
|
14
|
+
description?: string;
|
|
15
|
+
variant?: 'primary' | 'secondary' | 'accent' | 'danger';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface UIComponentSpec {
|
|
19
|
+
name?: string;
|
|
20
|
+
type: 'card' | 'navbar' | 'form' | 'dashboard-stats' | 'table' | 'modal' | 'custom';
|
|
21
|
+
title?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
theme?: 'dark' | 'light';
|
|
24
|
+
items?: UIComponentItem[];
|
|
25
|
+
actions?: Array<{ label: string; action: string; primary?: boolean }>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface GeneratedUI {
|
|
29
|
+
framework: UIFramework;
|
|
30
|
+
code: string;
|
|
31
|
+
css?: string;
|
|
32
|
+
previewHtml: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate semantic, modern HTML + CSS component code under Causyne UI Forge.
|
|
37
|
+
*/
|
|
38
|
+
export function generateHtmlComponent(spec: UIComponentSpec): { html: string; css: string } {
|
|
39
|
+
const isDark = spec.theme !== 'light';
|
|
40
|
+
const cardBg = isDark ? 'rgba(30, 41, 59, 0.75)' : 'rgba(255, 255, 255, 0.95)';
|
|
41
|
+
const border = isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.1)';
|
|
42
|
+
const text = isDark ? '#f8fafc' : '#0f172a';
|
|
43
|
+
const textMuted = isDark ? '#94a3b8' : '#64748b';
|
|
44
|
+
const accent = '#6366f1';
|
|
45
|
+
const accentHover = '#4f46e5';
|
|
46
|
+
|
|
47
|
+
const css = `
|
|
48
|
+
/* Causyne UI Forge Generated Styles - Responsive, Accessible & Performant */
|
|
49
|
+
.causyne-ui-root {
|
|
50
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
51
|
+
color: ${text};
|
|
52
|
+
line-height: 1.5;
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
}
|
|
55
|
+
.causyne-ui-root * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
56
|
+
.causyne-ui-card {
|
|
57
|
+
background: ${cardBg};
|
|
58
|
+
backdrop-filter: blur(12px);
|
|
59
|
+
border: 1px solid ${border};
|
|
60
|
+
border-radius: 12px;
|
|
61
|
+
padding: 24px;
|
|
62
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
63
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
64
|
+
}
|
|
65
|
+
.causyne-ui-card:hover {
|
|
66
|
+
transform: translateY(-2px);
|
|
67
|
+
box-shadow: 0 20px 30px -10px rgba(0, 0, 0, 0.3);
|
|
68
|
+
}
|
|
69
|
+
.causyne-ui-header { margin-bottom: 16px; }
|
|
70
|
+
.causyne-ui-title { font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em; }
|
|
71
|
+
.causyne-ui-desc { font-size: 0.875rem; color: ${textMuted}; margin-top: 4px; }
|
|
72
|
+
.causyne-ui-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin: 16px 0; }
|
|
73
|
+
.causyne-ui-stat-box {
|
|
74
|
+
background: rgba(255, 255, 255, 0.03);
|
|
75
|
+
border: 1px solid ${border};
|
|
76
|
+
border-radius: 8px;
|
|
77
|
+
padding: 16px;
|
|
78
|
+
}
|
|
79
|
+
.causyne-ui-stat-label { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: ${textMuted}; }
|
|
80
|
+
.causyne-ui-stat-value { font-size: 1.75rem; font-weight: 700; color: ${accent}; margin-top: 4px; }
|
|
81
|
+
.causyne-ui-btn {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
padding: 8px 16px;
|
|
86
|
+
border-radius: 6px;
|
|
87
|
+
font-size: 0.875rem;
|
|
88
|
+
font-weight: 500;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
border: 1px solid transparent;
|
|
91
|
+
transition: all 0.15s ease;
|
|
92
|
+
}
|
|
93
|
+
.causyne-ui-btn-primary { background: ${accent}; color: #ffffff; }
|
|
94
|
+
.causyne-ui-btn-primary:hover { background: ${accentHover}; }
|
|
95
|
+
.causyne-ui-btn-secondary { background: transparent; border-color: ${border}; color: ${text}; }
|
|
96
|
+
.causyne-ui-btn-secondary:hover { background: rgba(255, 255, 255, 0.05); }
|
|
97
|
+
.causyne-ui-actions { display: flex; gap: 8px; margin-top: 20px; justify-content: flex-end; }
|
|
98
|
+
`.trim();
|
|
99
|
+
|
|
100
|
+
let html = '';
|
|
101
|
+
const title = spec.title || 'Component';
|
|
102
|
+
const desc = spec.description || '';
|
|
103
|
+
|
|
104
|
+
if (spec.type === 'dashboard-stats' || (spec.items && spec.items.length > 0)) {
|
|
105
|
+
const itemsHtml = (spec.items || [])
|
|
106
|
+
.map(
|
|
107
|
+
(item) => `
|
|
108
|
+
<div class="causyne-ui-stat-box">
|
|
109
|
+
<div class="causyne-ui-stat-label">${escapeHtml(item.label)}</div>
|
|
110
|
+
<div class="causyne-ui-stat-value">${escapeHtml(String(item.value ?? '—'))}</div>
|
|
111
|
+
${item.description ? `<div class="causyne-ui-desc">${escapeHtml(item.description)}</div>` : ''}
|
|
112
|
+
</div>`
|
|
113
|
+
)
|
|
114
|
+
.join('\n');
|
|
115
|
+
|
|
116
|
+
html = `
|
|
117
|
+
<div class="causyne-ui-root">
|
|
118
|
+
<div class="causyne-ui-card">
|
|
119
|
+
<div class="causyne-ui-header">
|
|
120
|
+
<h3 class="causyne-ui-title">${escapeHtml(title)}</h3>
|
|
121
|
+
${desc ? `<p class="causyne-ui-desc">${escapeHtml(desc)}</p>` : ''}
|
|
122
|
+
</div>
|
|
123
|
+
<div class="causyne-ui-grid">
|
|
124
|
+
${itemsHtml}
|
|
125
|
+
</div>
|
|
126
|
+
${renderActions(spec.actions)}
|
|
127
|
+
</div>
|
|
128
|
+
</div>`.trim();
|
|
129
|
+
} else {
|
|
130
|
+
html = `
|
|
131
|
+
<div class="causyne-ui-root">
|
|
132
|
+
<div class="causyne-ui-card">
|
|
133
|
+
<div class="causyne-ui-header">
|
|
134
|
+
<h3 class="causyne-ui-title">${escapeHtml(title)}</h3>
|
|
135
|
+
${desc ? `<p class="causyne-ui-desc">${escapeHtml(desc)}</p>` : ''}
|
|
136
|
+
</div>
|
|
137
|
+
${renderActions(spec.actions)}
|
|
138
|
+
</div>
|
|
139
|
+
</div>`.trim();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { html, css };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function renderActions(actions?: Array<{ label: string; action: string; primary?: boolean }>): string {
|
|
146
|
+
if (!actions || actions.length === 0) return '';
|
|
147
|
+
const btns = actions
|
|
148
|
+
.map(
|
|
149
|
+
(a) =>
|
|
150
|
+
`<button class="causyne-ui-btn ${a.primary ? 'causyne-ui-btn-primary' : 'causyne-ui-btn-secondary'}" data-action="${escapeHtml(a.action)}">${escapeHtml(a.label)}</button>`
|
|
151
|
+
)
|
|
152
|
+
.join('\n ');
|
|
153
|
+
return `
|
|
154
|
+
<div class="causyne-ui-actions">
|
|
155
|
+
${btns}
|
|
156
|
+
</div>`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function escapeHtml(str: string): string {
|
|
160
|
+
return String(str || '')
|
|
161
|
+
.replace(/&/g, '&')
|
|
162
|
+
.replace(/</g, '<')
|
|
163
|
+
.replace(/>/g, '>')
|
|
164
|
+
.replace(/"/g, '"');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Generate full self-contained preview document.
|
|
169
|
+
*/
|
|
170
|
+
export function generatePreviewHtml(html: string, css: string, theme: 'dark' | 'light' = 'dark'): string {
|
|
171
|
+
const bg = theme === 'dark' ? '#0b0f19' : '#f1f5f9';
|
|
172
|
+
return `<!DOCTYPE html>
|
|
173
|
+
<html lang="en">
|
|
174
|
+
<head>
|
|
175
|
+
<meta charset="UTF-8">
|
|
176
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
177
|
+
<title>Causyne UI Forge Preview</title>
|
|
178
|
+
<style>
|
|
179
|
+
body {
|
|
180
|
+
margin: 0;
|
|
181
|
+
padding: 40px 20px;
|
|
182
|
+
min-height: 100vh;
|
|
183
|
+
background: ${bg};
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
justify-content: center;
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
}
|
|
189
|
+
${css}
|
|
190
|
+
</style>
|
|
191
|
+
</head>
|
|
192
|
+
<body>
|
|
193
|
+
${html}
|
|
194
|
+
</body>
|
|
195
|
+
</html>`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Generate React + Tailwind component code under Causyne UI Forge.
|
|
200
|
+
*/
|
|
201
|
+
export function generateReactComponent(spec: UIComponentSpec): string {
|
|
202
|
+
const compName = (spec.name || 'CausyneCard').replace(/[^a-zA-Z0-9]/g, '');
|
|
203
|
+
const title = spec.title || 'Component';
|
|
204
|
+
const desc = spec.description || '';
|
|
205
|
+
|
|
206
|
+
return `import React from 'react';
|
|
207
|
+
|
|
208
|
+
export interface ${compName}Props {
|
|
209
|
+
title?: string;
|
|
210
|
+
description?: string;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const ${compName}: React.FC<${compName}Props> = ({
|
|
214
|
+
title = "${title}",
|
|
215
|
+
description = "${desc}"
|
|
216
|
+
}) => {
|
|
217
|
+
return (
|
|
218
|
+
<div className="rounded-xl border border-slate-700/60 bg-slate-900/80 p-6 backdrop-blur-md shadow-xl transition-all hover:shadow-2xl">
|
|
219
|
+
<div className="mb-4">
|
|
220
|
+
<h3 className="text-xl font-semibold tracking-tight text-white">{title}</h3>
|
|
221
|
+
{description && <p className="mt-1 text-sm text-slate-400">{description}</p>}
|
|
222
|
+
</div>
|
|
223
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 my-4">
|
|
224
|
+
${(spec.items || [])
|
|
225
|
+
.map(
|
|
226
|
+
(item) => `
|
|
227
|
+
<div key="${item.id || item.label}" className="rounded-lg border border-slate-800 bg-slate-800/40 p-4">
|
|
228
|
+
<div className="text-xs font-medium uppercase tracking-wider text-slate-400">${item.label}</div>
|
|
229
|
+
<div className="mt-1 text-2xl font-bold text-indigo-400">${item.value ?? '—'}</div>
|
|
230
|
+
${item.description ? `<p className="mt-1 text-xs text-slate-500">${item.description}</p>` : ''}
|
|
231
|
+
</div>`
|
|
232
|
+
)
|
|
233
|
+
.join('')}
|
|
234
|
+
</div>
|
|
235
|
+
${(spec.actions && spec.actions.length > 0)
|
|
236
|
+
? `
|
|
237
|
+
<div className="mt-6 flex justify-end gap-3">
|
|
238
|
+
${spec.actions
|
|
239
|
+
.map(
|
|
240
|
+
(a) => `
|
|
241
|
+
<button className="px-4 py-2 text-sm font-medium rounded-lg transition-colors ${
|
|
242
|
+
a.primary
|
|
243
|
+
? 'bg-indigo-600 text-white hover:bg-indigo-500'
|
|
244
|
+
: 'border border-slate-700 text-slate-300 hover:bg-slate-800'
|
|
245
|
+
}">
|
|
246
|
+
${a.label}
|
|
247
|
+
</button>`
|
|
248
|
+
)
|
|
249
|
+
.join('')}
|
|
250
|
+
</div>`
|
|
251
|
+
: ''}
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export default ${compName};
|
|
257
|
+
`;
|
|
258
|
+
}
|