agentgui 1.0.61 → 1.0.63

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/acp-launcher.js CHANGED
@@ -1,14 +1,27 @@
1
1
  import { query } from '@anthropic-ai/claude-code';
2
- import { default as SYSTEM_PROMPT } from './system-prompt.js';
3
2
 
3
+ /**
4
+ * ACPConnection - Uses @anthropic-ai/claude-code SDK directly
5
+ *
6
+ * This uses the real Claude Code SDK which handles:
7
+ * - Plugin execution with real system prompt
8
+ * - Actual filesystem operations
9
+ * - Streaming responses with onUpdate callbacks
10
+ * - No subprocess spawning needed (SDK handles it)
11
+ */
4
12
  export default class ACPConnection {
5
13
  constructor() {
6
14
  this.sessionId = null;
7
15
  this.onUpdate = null;
16
+ this.cwd = process.cwd();
8
17
  }
9
18
 
10
19
  async connect(agentType, cwd) {
11
- console.log(`[ACP] Using Claude Code SDK (${agentType})`);
20
+ console.log(`[ACP] Using @anthropic-ai/claude-code SDK (${agentType})`);
21
+ if (cwd) {
22
+ this.cwd = cwd;
23
+ }
24
+ return { connected: true };
12
25
  }
13
26
 
14
27
  async initialize() {
@@ -17,6 +30,10 @@ export default class ACPConnection {
17
30
 
18
31
  async newSession(cwd) {
19
32
  this.sessionId = Math.random().toString(36).substring(7);
33
+ if (cwd) {
34
+ this.cwd = cwd;
35
+ }
36
+ console.log(`[ACP] Session ${this.sessionId} in ${this.cwd}`);
20
37
  return { sessionId: this.sessionId };
21
38
  }
22
39
 
@@ -29,49 +46,63 @@ export default class ACPConnection {
29
46
  }
30
47
 
31
48
  async injectSystemContext() {
32
- return { context: SYSTEM_PROMPT };
49
+ return { context: 'Using Claude Code SDK with real plugins' };
33
50
  }
34
51
 
35
52
  async sendPrompt(prompt) {
36
- let fullResponse = '';
53
+ const promptText = typeof prompt === 'string' ? prompt : prompt.map(p => p.text).join('\n');
37
54
 
38
55
  try {
39
- const promptText = typeof prompt === 'string' ? prompt : prompt.map(p => p.text).join('\n');
40
- const systemMessage = `${SYSTEM_PROMPT}\n\nUser Request: ${promptText}`;
41
-
42
- const response = query({
43
- prompt: systemMessage,
44
- options: {}
56
+ console.log(`[ACP] Sending prompt (${promptText.length} chars) in ${this.cwd}`);
57
+
58
+ // Use the SDK directly to execute the prompt
59
+ // The SDK handles plugins, system prompt, and all real execution
60
+ const session = await query({
61
+ prompt: promptText,
62
+ options: {
63
+ cwd: this.cwd
64
+ }
45
65
  });
46
66
 
47
- // query() returns an async iterable
48
- // Iterate through all messages and collect the response
49
- for await (const message of response) {
50
- // Try multiple content paths: direct .content or .message.content
51
- let content = message.content || message.message?.content;
52
-
53
- if (content && Array.isArray(content)) {
54
- const textChunks = content.map(c => c.text || '').join('');
55
- fullResponse += textChunks;
56
-
57
- // Emit update for real-time display
58
- if (this.onUpdate && textChunks) {
59
- this.onUpdate({
60
- update: {
61
- sessionUpdate: 'agent_message_chunk',
62
- content: { text: textChunks }
63
- }
64
- });
67
+ let fullResponse = '';
68
+
69
+ // Stream messages and collect the final result
70
+ for await (const message of session.sdkMessages) {
71
+ if (message.type === 'result') {
72
+ // Extract the actual result from the final message
73
+ if (message.result) {
74
+ fullResponse = String(message.result);
75
+ console.log(`[ACP] Got result: ${fullResponse.length} chars`);
76
+
77
+ // Emit update callback if provided
78
+ if (this.onUpdate && fullResponse) {
79
+ this.onUpdate({
80
+ update: {
81
+ sessionUpdate: 'agent_message_chunk',
82
+ content: { text: fullResponse }
83
+ }
84
+ });
85
+ }
65
86
  }
87
+
88
+ return {
89
+ content: fullResponse,
90
+ stopReason: 'end_turn',
91
+ result: fullResponse,
92
+ sessionId: this.sessionId,
93
+ usage: message.usage
94
+ };
66
95
  }
67
96
  }
68
97
 
69
- // Fallback if nothing was collected
70
- if (!fullResponse) {
71
- fullResponse = 'No response from agent';
72
- }
98
+ // Fallback if no result message found
99
+ return {
100
+ content: fullResponse,
101
+ stopReason: 'end_turn',
102
+ result: fullResponse,
103
+ sessionId: this.sessionId
104
+ };
73
105
 
74
- return { content: fullResponse };
75
106
  } catch (err) {
76
107
  console.error(`[ACP] Query error: ${err.message}`);
77
108
  throw err;
@@ -79,10 +110,11 @@ export default class ACPConnection {
79
110
  }
80
111
 
81
112
  isRunning() {
82
- return true;
113
+ return true; // SDK manages process lifecycle
83
114
  }
84
115
 
85
116
  async terminate() {
86
- return;
117
+ console.log(`[ACP] Terminating session ${this.sessionId}`);
118
+ return { terminated: true };
87
119
  }
88
120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.61",
3
+ "version": "1.0.63",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/html-wrapper.js DELETED
@@ -1,117 +0,0 @@
1
- /**
2
- * HTML Wrapper for Claude Responses
3
- * Converts plain text/markdown responses into beautiful HTML
4
- */
5
-
6
- export class HTMLWrapper {
7
- /**
8
- * Wrap plain text response in HTML with RippleUI styling
9
- */
10
- static wrapResponse(text) {
11
- if (!text || typeof text !== 'string') return text;
12
-
13
- // If already HTML, return as-is
14
- if (text.trim().startsWith('<')) return text;
15
-
16
- // Parse markdown-style text and convert to HTML
17
- const lines = text.split('\n');
18
- const html = this.parseMarkdownToHTML(lines);
19
-
20
- // Wrap in container
21
- return `<div class="space-y-4 p-6 max-w-4xl">${html}</div>`;
22
- }
23
-
24
- static parseMarkdownToHTML(lines) {
25
- let html = '';
26
- let inCodeBlock = false;
27
- let codeLanguage = 'text';
28
- let codeContent = '';
29
- let listItems = [];
30
- let inList = false;
31
-
32
- for (let i = 0; i < lines.length; i++) {
33
- const line = lines[i];
34
-
35
- // Code blocks
36
- if (line.match(/^```(\w+)?$/)) {
37
- if (!inCodeBlock) {
38
- // Start code block
39
- inCodeBlock = true;
40
- codeLanguage = line.match(/```(\w+)?/)?.[1] || 'text';
41
- codeContent = '';
42
- } else {
43
- // End code block
44
- inCodeBlock = false;
45
- html += `<pre class="bg-gray-900 text-white p-4 rounded-lg overflow-x-auto"><code class="language-${codeLanguage}">${this.escapeHtml(codeContent)}</code></pre>`;
46
- codeContent = '';
47
- }
48
- continue;
49
- }
50
-
51
- if (inCodeBlock) {
52
- codeContent += (codeContent ? '\n' : '') + line;
53
- continue;
54
- }
55
-
56
- // Headings
57
- if (line.match(/^#+\s/)) {
58
- const level = line.match(/^#+/)[0].length;
59
- const heading = line.replace(/^#+\s/, '');
60
- html += `<h${level} class="text-${level === 1 ? '3xl' : level === 2 ? '2xl' : 'xl'} font-bold text-gray-900 mt-4">${this.escapeHtml(heading)}</h${level}>`;
61
- continue;
62
- }
63
-
64
- // Lists
65
- if (line.match(/^[-*•]\s/) || line.match(/^\d+\.\s/)) {
66
- const itemText = line.replace(/^[-*•\d+.]\s+/, '');
67
- if (!inList) {
68
- inList = true;
69
- listItems = [];
70
- }
71
- listItems.push(itemText);
72
- continue;
73
- } else if (inList && line.trim()) {
74
- // End list
75
- html += `<ul class="list-none space-y-2 ml-0"><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">${listItems.map(item => `• ${this.escapeHtml(item)}`).join('</li><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">')}</li></ul>`;
76
- inList = false;
77
- listItems = [];
78
- }
79
-
80
- // Empty lines
81
- if (!line.trim()) {
82
- if (!html.endsWith('</p>')) html += '<br>';
83
- continue;
84
- }
85
-
86
- // Regular paragraphs
87
- if (line.trim()) {
88
- // Format inline markdown
89
- let formatted = this.escapeHtml(line);
90
- formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong class="font-bold">$1</strong>');
91
- formatted = formatted.replace(/\*(.*?)\*/g, '<em class="italic">$1</em>');
92
- formatted = formatted.replace(/`([^`]+)`/g, '<code class="bg-gray-200 px-2 py-1 rounded font-mono text-sm">$1</code>');
93
-
94
- html += `<p class="text-gray-700 leading-relaxed">${formatted}</p>`;
95
- }
96
- }
97
-
98
- // Close any remaining list
99
- if (inList) {
100
- html += `<ul class="list-none space-y-2 ml-0"><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">${listItems.map(item => `• ${this.escapeHtml(item)}`).join('</li><li class="p-3 bg-gray-100 rounded border-l-4 border-blue-500">')}</li></ul>`;
101
- }
102
-
103
- return html;
104
- }
105
-
106
- static escapeHtml(text) {
107
- if (typeof text !== 'string') return '';
108
- return text
109
- .replace(/&/g, '&amp;')
110
- .replace(/</g, '&lt;')
111
- .replace(/>/g, '&gt;')
112
- .replace(/"/g, '&quot;')
113
- .replace(/'/g, '&#039;');
114
- }
115
- }
116
-
117
- export default HTMLWrapper;
@@ -1,329 +0,0 @@
1
- /**
2
- * Response formatter for Claude Code outputs
3
- * Handles segmentation of text, code blocks, tool calls, thinking blocks, etc.
4
- */
5
-
6
- export class ResponseFormatter {
7
- /**
8
- * Parse Claude Code response into structured segments
9
- */
10
- static parseResponse(text) {
11
- if (!text || typeof text !== 'string') return [];
12
-
13
- const segments = [];
14
- const lines = text.split('\n');
15
- let current = null;
16
- let codeBlockLang = null;
17
- let inCodeBlock = false;
18
-
19
- for (let i = 0; i < lines.length; i++) {
20
- const line = lines[i];
21
-
22
- // Check for code block markers
23
- const codeBlockMatch = line.match(/^```(\w+)?$/);
24
- if (codeBlockMatch) {
25
- if (!inCodeBlock) {
26
- // Starting a code block
27
- if (current && current.type === 'text' && current.content.trim()) {
28
- segments.push(current);
29
- current = null;
30
- }
31
- inCodeBlock = true;
32
- codeBlockLang = codeBlockMatch[1] || 'text';
33
- current = { type: 'code', language: codeBlockLang, content: '' };
34
- } else {
35
- // Ending a code block
36
- if (current) {
37
- segments.push(current);
38
- current = null;
39
- }
40
- inCodeBlock = false;
41
- codeBlockLang = null;
42
- }
43
- continue;
44
- }
45
-
46
- if (inCodeBlock) {
47
- current.content += (current.content ? '\n' : '') + line;
48
- } else {
49
- // Check for markdown formatting
50
- if (line.match(/^#+\s/)) {
51
- // Heading
52
- if (current && current.type === 'text' && current.content.trim()) {
53
- segments.push(current);
54
- }
55
- segments.push({ type: 'heading', level: line.match(/^#+/)[0].length, content: line.replace(/^#+\s/, '') });
56
- current = null;
57
- } else if (line.match(/^>\s/) || line.match(/^-\s/) || line.match(/^\d+\.\s/)) {
58
- // Quote, bullet, or numbered list
59
- if (current && current.type === 'text') {
60
- segments.push(current);
61
- }
62
- if (line.match(/^>\s/)) {
63
- segments.push({ type: 'blockquote', content: line.replace(/^>\s/, '') });
64
- } else {
65
- segments.push({ type: 'list_item', content: line.replace(/^[-\d+.]\s+/, '') });
66
- }
67
- current = null;
68
- } else if (line.trim()) {
69
- // Regular text
70
- if (!current || current.type !== 'text') {
71
- if (current) segments.push(current);
72
- current = { type: 'text', content: line };
73
- } else {
74
- current.content += '\n' + line;
75
- }
76
- } else if (current && current.type === 'text' && current.content.trim()) {
77
- // Empty line - could indicate paragraph break
78
- current.content += '\n\n';
79
- }
80
- }
81
- }
82
-
83
- if (current) {
84
- segments.push(current);
85
- }
86
-
87
- return segments;
88
- }
89
-
90
- /**
91
- * Extract tool calls, thinking blocks, and task information
92
- */
93
- static extractMetadata(text) {
94
- if (!text || typeof text !== 'string') return { tools: [], thinking: [], tasks: [] };
95
-
96
- const metadata = {
97
- tools: [],
98
- thinking: [],
99
- tasks: [],
100
- subagents: []
101
- };
102
-
103
- // Find tool call patterns
104
- const toolPattern = /(?:^|\n)\s*(?:Using|Calling|Invoking|Running)\s+(?:the\s+)?(\w+(?:\s+\w+)*?)(?:\s+(?:tool|command|function))?\s*(?:with|to)?\s*(.+?)(?:\n|$)/gi;
105
- let match;
106
- while ((match = toolPattern.exec(text)) !== null) {
107
- metadata.tools.push({
108
- name: match[1].trim(),
109
- description: match[2]?.trim() || ''
110
- });
111
- }
112
-
113
- // Find thinking/reasoning blocks
114
- const thinkingPattern = /(?:thinking|reasoning|analyzing|considering)[\s:]+(.+?)(?:\n\n|$)/gi;
115
- while ((match = thinkingPattern.exec(text)) !== null) {
116
- metadata.thinking.push(match[1].trim());
117
- }
118
-
119
- // Find task references
120
- const taskPattern = /(?:task|step|doing)[\s:]+(.+?)(?:\n|$)/gi;
121
- while ((match = taskPattern.exec(text)) !== null) {
122
- metadata.tasks.push(match[1].trim());
123
- }
124
-
125
- // Find subagent references
126
- const subagentPattern = /(?:using|with|via)\s+(?:the\s+)?(\w+)\s+(?:subagent|agent)/gi;
127
- while ((match = subagentPattern.exec(text)) !== null) {
128
- metadata.subagents.push(match[1].trim());
129
- }
130
-
131
- return metadata;
132
- }
133
-
134
- /**
135
- * Segment a response into logical parts
136
- * Splits on natural boundaries like tool calls, thinking, etc.
137
- */
138
- static segmentResponse(text) {
139
- if (!text || typeof text !== 'string') return [];
140
-
141
- // First, extract XML tags
142
- const xmlSegments = this.extractXMLTags(text);
143
- if (xmlSegments.length > 0) {
144
- return xmlSegments;
145
- }
146
-
147
- // Otherwise, segment by intent and transitions
148
- const segments = [];
149
- const parts = this.segmentByIntent(text);
150
-
151
- // Parse each part
152
- for (const part of parts) {
153
- segments.push({
154
- ...part,
155
- parsed: this.parseResponse(part.text),
156
- metadata: this.extractMetadata(part.text)
157
- });
158
- }
159
-
160
- return segments;
161
- }
162
-
163
- /**
164
- * Extract XML-tagged content as separate segments
165
- */
166
- static extractXMLTags(text) {
167
- const xmlPattern = /<(thinking|tool_use|tool_result|result|action)[\s>]([\s\S]*?)<\/\1>/gi;
168
- const segments = [];
169
- let lastIndex = 0;
170
- let match;
171
-
172
- while ((match = xmlPattern.exec(text)) !== null) {
173
- const before = text.substring(lastIndex, match.index);
174
- if (before.trim()) {
175
- segments.push({ type: 'text', text: before.trim() });
176
- }
177
-
178
- const tagType = match[1].toLowerCase();
179
- const tagContent = match[2].trim();
180
- segments.push({ type: tagType, text: tagContent });
181
- lastIndex = xmlPattern.lastIndex;
182
- }
183
-
184
- if (lastIndex < text.length) {
185
- const remaining = text.substring(lastIndex).trim();
186
- if (remaining) {
187
- segments.push({ type: 'text', text: remaining });
188
- }
189
- }
190
-
191
- return segments.length > 0 ? segments : [];
192
- }
193
-
194
- /**
195
- * Segment by semantic intent/actions
196
- */
197
- static segmentByIntent(text) {
198
- const segments = [];
199
- const intentPatterns = [
200
- { pattern: /^(Let me|I'll|I'm going to|First,?|Next,?|Now,?|Here's)/im, type: 'action' },
201
- { pattern: /^(Looking|Examining|Analyzing|Reviewing|Checking|Reading)/im, type: 'analysis' },
202
- { pattern: /^(Here'?s|Result|Output|Found|Got|Completed)/im, type: 'result' },
203
- { pattern: /^(The|This|That|These|Those)/im, type: 'explanation' }
204
- ];
205
-
206
- let currentSegment = '';
207
- let currentType = 'text';
208
- const lines = text.split('\n');
209
-
210
- for (const line of lines) {
211
- let newType = currentType;
212
-
213
- // Check for intent pattern match
214
- for (const { pattern, type } of intentPatterns) {
215
- if (pattern.test(line)) {
216
- newType = type;
217
- break;
218
- }
219
- }
220
-
221
- // If type changed and we have content, save segment
222
- if (newType !== currentType && currentSegment.trim()) {
223
- segments.push({ type: currentType, text: currentSegment.trim() });
224
- currentSegment = '';
225
- currentType = newType;
226
- }
227
-
228
- currentSegment += (currentSegment ? '\n' : '') + line;
229
- }
230
-
231
- // Add remaining segment
232
- if (currentSegment.trim()) {
233
- segments.push({ type: currentType, text: currentSegment.trim() });
234
- }
235
-
236
- return segments.length > 0 ? segments : [{ type: 'text', text }];
237
- }
238
-
239
- /**
240
- * Format segments for display with proper HTML
241
- */
242
- static formatForDisplay(segments) {
243
- if (!Array.isArray(segments)) return '';
244
-
245
- const html = [];
246
-
247
- for (const segment of segments) {
248
- if (segment.type === 'code') {
249
- html.push(`<pre class="code-block language-${segment.language}"><code>${this.escapeHtml(segment.content)}</code></pre>`);
250
- } else if (segment.type === 'heading') {
251
- const tag = `h${Math.min(segment.level, 6)}`;
252
- html.push(`<${tag} class="response-heading">${this.escapeHtml(segment.content)}</${tag}>`);
253
- } else if (segment.type === 'blockquote') {
254
- html.push(`<blockquote class="response-quote">${this.escapeHtml(segment.content)}</blockquote>`);
255
- } else if (segment.type === 'list_item') {
256
- html.push(`<li class="response-list-item">${this.escapeHtml(segment.content)}</li>`);
257
- } else if (segment.type === 'text') {
258
- html.push(`<p class="response-text">${this.escapeHtml(segment.content).replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>').replace(/\*(.*?)\*/g, '<em>$1</em>')}</p>`);
259
- }
260
- }
261
-
262
- return html.join('\n');
263
- }
264
-
265
- static escapeHtml(text) {
266
- if (typeof text !== 'string') return '';
267
- return text
268
- .replace(/&/g, '&amp;')
269
- .replace(/</g, '&lt;')
270
- .replace(/>/g, '&gt;')
271
- .replace(/"/g, '&quot;')
272
- .replace(/'/g, '&#039;');
273
- }
274
-
275
- /**
276
- * Create rich metadata display
277
- */
278
- static createMetadataDisplay(metadata) {
279
- if (!metadata || Object.keys(metadata).every(k => !metadata[k] || metadata[k].length === 0)) {
280
- return null;
281
- }
282
-
283
- const html = ['<div class="response-metadata">'];
284
-
285
- if (metadata.tools?.length) {
286
- html.push('<div class="metadata-section tools">');
287
- html.push('<strong>Tools Used:</strong>');
288
- html.push('<ul>');
289
- for (const tool of metadata.tools) {
290
- html.push(`<li><code>${this.escapeHtml(tool.name)}</code>${tool.description ? ': ' + this.escapeHtml(tool.description) : ''}</li>`);
291
- }
292
- html.push('</ul></div>');
293
- }
294
-
295
- if (metadata.thinking?.length) {
296
- html.push('<details class="metadata-section thinking">');
297
- html.push('<summary>Reasoning</summary>');
298
- for (const thought of metadata.thinking) {
299
- html.push(`<p>${this.escapeHtml(thought)}</p>`);
300
- }
301
- html.push('</details>');
302
- }
303
-
304
- if (metadata.subagents?.length) {
305
- html.push('<div class="metadata-section subagents">');
306
- html.push('<strong>Subagents:</strong>');
307
- html.push('<ul>');
308
- for (const agent of metadata.subagents) {
309
- html.push(`<li>${this.escapeHtml(agent)}</li>`);
310
- }
311
- html.push('</ul></div>');
312
- }
313
-
314
- if (metadata.tasks?.length) {
315
- html.push('<div class="metadata-section tasks">');
316
- html.push('<strong>Tasks:</strong>');
317
- html.push('<ul>');
318
- for (const task of metadata.tasks) {
319
- html.push(`<li>${this.escapeHtml(task)}</li>`);
320
- }
321
- html.push('</ul></div>');
322
- }
323
-
324
- html.push('</div>');
325
- return html.join('\n');
326
- }
327
- }
328
-
329
- export default ResponseFormatter;
package/system-prompt.js DELETED
@@ -1,426 +0,0 @@
1
- /**
2
- * Unified System Prompt - Enforced HTML Response Format
3
- *
4
- * This is THE ONLY way to respond in agentgui.
5
- * No alternatives. No exceptions. No fallbacks.
6
- *
7
- * Agent MUST respond with HTML code blocks.
8
- * Agent MUST NOT respond with plain text.
9
- */
10
-
11
- export const SYSTEM_PROMPT = `# CRITICAL: You MUST Respond With HTML Code Blocks
12
-
13
- ## Response Format - MANDATORY
14
-
15
- EVERY SINGLE response must be a valid HTML code block. No exceptions.
16
-
17
- \`\`\`html
18
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem; max-width: 100%;">
19
- <!-- Your content here -->
20
- </div>
21
- \`\`\`
22
-
23
- ## Rules (Non-Negotiable)
24
-
25
- 1. ✓ EVERY response MUST be wrapped in \`\`\`html ... \`\`\`
26
- 2. ✓ The HTML MUST be valid and complete
27
- 3. ✓ Use only semantic HTML: <h1>-<h6>, <p>, <ul>, <ol>, <table>, <pre>, <code>, <div>
28
- 4. ✓ Use inline styles with CSS variables for theme-aware dark/light mode support
29
- 5. ✓ NO Tailwind CSS classes - this breaks the theme system
30
- 6. ✓ NO plain text responses allowed
31
- 7. ✓ NO markdown responses allowed
32
- 8. ✓ NO mixed formats allowed
33
- 9. ✓ HTML code block is the ONLY format
34
-
35
- ## Theme-Aware CSS Variables (CRITICAL)
36
-
37
- The app uses RippleUI with these CSS variables that automatically support dark/light mode:
38
-
39
- - --text-primary: Primary text color (dark text in light mode, light text in dark mode)
40
- - --text-secondary: Secondary text color
41
- - --text-tertiary: Tertiary text color
42
- - --bg-primary: Primary background (white in light mode, dark in dark mode)
43
- - --bg-secondary: Secondary background
44
- - --bg-tertiary: Tertiary background
45
- - --border-color: Border color
46
- - --color-primary: Brand color (blue)
47
- - --color-success: Success green
48
- - --color-warning: Warning yellow/orange
49
- - --color-danger: Error red
50
- - --color-info: Info blue
51
-
52
- ## When You Have Text Content
53
-
54
- For plain text or paragraphs:
55
- \`\`\`html
56
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
57
- <p style="color: var(--text-primary); line-height: 1.6;">Your text here</p>
58
- </div>
59
- \`\`\`
60
-
61
- ## When You Have Code to Show
62
-
63
- \`\`\`html
64
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
65
- <h3 style="font-size: 1.25rem; font-weight: bold; color: var(--text-primary); margin: 0;">Code Example</h3>
66
- <pre style="background: var(--bg-secondary); color: var(--text-primary); padding: 1rem; border-radius: 0.5rem; overflow-x: auto; border: 1px solid var(--border-color);"><code style="font-family: 'Courier New', monospace; font-size: 0.9rem;">// Your code here
67
- function example() {
68
- return "code";
69
- }</code></pre>
70
- </div>
71
- \`\`\`
72
-
73
- ## When You Have Lists
74
-
75
- \`\`\`html
76
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
77
- <h3 style="font-size: 1.25rem; font-weight: bold; color: var(--text-primary); margin: 0;">Items</h3>
78
- <ul style="list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.75rem;">
79
- <li style="padding: 0.75rem; background: var(--bg-secondary); border-left: 4px solid var(--color-primary); border-radius: 0.25rem; color: var(--text-primary);">Item one</li>
80
- <li style="padding: 0.75rem; background: var(--bg-secondary); border-left: 4px solid var(--color-primary); border-radius: 0.25rem; color: var(--text-primary);">Item two</li>
81
- <li style="padding: 0.75rem; background: var(--bg-secondary); border-left: 4px solid var(--color-primary); border-radius: 0.25rem; color: var(--text-primary);">Item three</li>
82
- </ul>
83
- </div>
84
- \`\`\`
85
-
86
- ## Component Examples
87
-
88
- ### Card
89
- \`\`\`html
90
- <div style="background: var(--bg-secondary); padding: 1.5rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
91
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Title</h4>
92
- <p style="color: var(--text-secondary); margin: 0; line-height: 1.6;">Content here</p>
93
- </div>
94
- \`\`\`
95
-
96
- ### Alert/Warning
97
- \`\`\`html
98
- <div style="background: rgba(245, 158, 11, 0.1); border-left: 4px solid var(--color-warning); padding: 1rem; border-radius: 0.25rem;">
99
- <p style="color: var(--color-warning); margin: 0; font-weight: 500;">⚠️ Important message</p>
100
- </div>
101
- \`\`\`
102
-
103
- ### Success
104
- \`\`\`html
105
- <div style="background: rgba(16, 185, 129, 0.1); border-left: 4px solid var(--color-success); padding: 1rem; border-radius: 0.25rem;">
106
- <p style="color: var(--color-success); margin: 0; font-weight: 500;">✓ Success message</p>
107
- </div>
108
- \`\`\`
109
-
110
- ### Error
111
- \`\`\`html
112
- <div style="background: rgba(239, 68, 68, 0.1); border-left: 4px solid var(--color-danger); padding: 1rem; border-radius: 0.25rem;">
113
- <p style="color: var(--color-danger); margin: 0; font-weight: 500;">✗ Error message</p>
114
- </div>
115
- \`\`\`
116
-
117
- ### Table
118
- \`\`\`html
119
- <table style="width: 100%; border-collapse: collapse; border: 1px solid var(--border-color);">
120
- <thead style="background: var(--bg-tertiary);">
121
- <tr>
122
- <th style="padding: 0.75rem; text-align: left; border: 1px solid var(--border-color); color: var(--text-primary); font-weight: bold;">Header 1</th>
123
- <th style="padding: 0.75rem; text-align: left; border: 1px solid var(--border-color); color: var(--text-primary); font-weight: bold;">Header 2</th>
124
- </tr>
125
- </thead>
126
- <tbody>
127
- <tr style="background: var(--bg-secondary);">
128
- <td style="padding: 0.75rem; border: 1px solid var(--border-color); color: var(--text-primary);">Data 1</td>
129
- <td style="padding: 0.75rem; border: 1px solid var(--border-color); color: var(--text-primary);">Data 2</td>
130
- </tr>
131
- </tbody>
132
- </table>
133
- \`\`\`
134
-
135
- ## Advanced RippleUI Components (Use These!)
136
-
137
- Use theme-aware CSS variables to create components that adapt to dark/light mode:
138
-
139
- ### Progress Indicator
140
- \`\`\`html
141
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
142
- <div style="display: flex; flex-direction: column; gap: 0.5rem;">
143
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
144
- <span style="font-size: 0.875rem; font-weight: 500; color: var(--text-primary);">Progress</span>
145
- <span style="font-size: 0.875rem; color: var(--text-secondary);">75%</span>
146
- </div>
147
- <div style="width: 100%; background: var(--bg-tertiary); border-radius: 9999px; height: 0.5rem; overflow: hidden;">
148
- <div style="background: var(--color-primary); height: 100%; border-radius: 9999px; width: 75%; transition: width 0.3s ease;"></div>
149
- </div>
150
- </div>
151
- </div>
152
- \`\`\`
153
-
154
- ### Grid Layout with Cards
155
- \`\`\`html
156
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
157
- <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem;">
158
- <div style="background: var(--bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
159
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Metric 1</h4>
160
- <p style="font-size: 1.5rem; font-weight: bold; color: var(--color-primary); margin: 0;">1,234</p>
161
- </div>
162
- <div style="background: var(--bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
163
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Metric 2</h4>
164
- <p style="font-size: 1.5rem; font-weight: bold; color: var(--color-success); margin: 0;">567</p>
165
- </div>
166
- </div>
167
- </div>
168
- \`\`\`
169
-
170
- ### Collapsible Section
171
- \`\`\`html
172
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
173
- <details style="cursor: pointer;">
174
- <summary style="font-weight: bold; color: var(--text-primary); display: flex; align-items: center; gap: 0.5rem; user-select: none;">
175
- <span style="display: inline-block; transition: transform 0.2s ease;">▶</span>
176
- Click to expand
177
- </summary>
178
- <div style="margin-top: 1rem; padding-left: 1.5rem; color: var(--text-secondary);">
179
- Hidden content here
180
- </div>
181
- </details>
182
- </div>
183
- \`\`\`
184
-
185
- ### Sidebar/Two-Column
186
- \`\`\`html
187
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
188
- <div style="display: flex; gap: 1rem;">
189
- <div style="width: 33.333%; background: var(--bg-tertiary); padding: 1rem; border-radius: 0.5rem;">
190
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Sidebar</h4>
191
- <p style="font-size: 0.875rem; color: var(--text-secondary); margin: 0;">Navigation or info</p>
192
- </div>
193
- <div style="width: 66.667%; background: var(--bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
194
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Main Content</h4>
195
- <p style="color: var(--text-secondary); margin: 0;">Primary content here</p>
196
- </div>
197
- </div>
198
- </div>
199
- \`\`\`
200
-
201
- ### Badge/Pill Labels
202
- \`\`\`html
203
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
204
- <div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
205
- <span style="display: inline-block; background: var(--color-primary); color: white; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 500;">Active</span>
206
- <span style="display: inline-block; background: var(--color-warning); color: white; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 500;">Pending</span>
207
- <span style="display: inline-block; background: var(--color-success); color: white; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 500;">Completed</span>
208
- </div>
209
- </div>
210
- \`\`\`
211
-
212
- ### Timeline
213
- \`\`\`html
214
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
215
- <div style="display: flex; flex-direction: column; gap: 1rem;">
216
- <div style="display: flex; gap: 1rem;">
217
- <div style="width: 0.75rem; height: 0.75rem; border-radius: 50%; background: var(--color-primary); flex-shrink: 0; margin-top: 0.25rem;"></div>
218
- <div>
219
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.25rem 0;">Step 1</h4>
220
- <p style="font-size: 0.875rem; color: var(--text-secondary); margin: 0;">Description of first step</p>
221
- </div>
222
- </div>
223
- <div style="display: flex; gap: 1rem;">
224
- <div style="width: 0.75rem; height: 0.75rem; border-radius: 50%; background: var(--color-success); flex-shrink: 0; margin-top: 0.25rem;"></div>
225
- <div>
226
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.25rem 0;">Step 2</h4>
227
- <p style="font-size: 0.875rem; color: var(--text-secondary); margin: 0;">Description of second step</p>
228
- </div>
229
- </div>
230
- </div>
231
- </div>
232
- \`\`\`
233
-
234
- ### Interactive Forms (Request User Input)
235
-
236
- When you need user input, create an interactive form:
237
-
238
- \`\`\`html
239
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
240
- <h2 style="font-size: 1.5rem; font-weight: bold; color: var(--text-primary); margin: 0;">User Input Required</h2>
241
-
242
- <form style="display: flex; flex-direction: column; gap: 1rem;" onsubmit="return handleFormSubmit(event)">
243
- <!-- Text Input -->
244
- <div style="display: flex; flex-direction: column; gap: 0.25rem;">
245
- <label style="display: block; font-size: 0.875rem; font-weight: 500; color: var(--text-primary);">Name</label>
246
- <input type="text" name="name" style="width: 100%; padding: 0.5rem 0.75rem; border: 1px solid var(--border-color); border-radius: 0.5rem; background: var(--bg-primary); color: var(--text-primary); font-size: 1rem; font-family: inherit;" placeholder="Enter your name" required>
247
- </div>
248
-
249
- <!-- Email Input -->
250
- <div style="display: flex; flex-direction: column; gap: 0.25rem;">
251
- <label style="display: block; font-size: 0.875rem; font-weight: 500; color: var(--text-primary);">Email</label>
252
- <input type="email" name="email" style="width: 100%; padding: 0.5rem 0.75rem; border: 1px solid var(--border-color); border-radius: 0.5rem; background: var(--bg-primary); color: var(--text-primary); font-size: 1rem; font-family: inherit;" placeholder="Enter email" required>
253
- </div>
254
-
255
- <!-- Textarea -->
256
- <div style="display: flex; flex-direction: column; gap: 0.25rem;">
257
- <label style="display: block; font-size: 0.875rem; font-weight: 500; color: var(--text-primary);">Message</label>
258
- <textarea name="message" style="width: 100%; padding: 0.5rem 0.75rem; border: 1px solid var(--border-color); border-radius: 0.5rem; background: var(--bg-primary); color: var(--text-primary); font-size: 1rem; font-family: inherit; resize: vertical;" rows="4" placeholder="Your message here" required></textarea>
259
- </div>
260
-
261
- <!-- Select Dropdown -->
262
- <div style="display: flex; flex-direction: column; gap: 0.25rem;">
263
- <label style="display: block; font-size: 0.875rem; font-weight: 500; color: var(--text-primary);">Option</label>
264
- <select name="option" style="width: 100%; padding: 0.5rem 0.75rem; border: 1px solid var(--border-color); border-radius: 0.5rem; background: var(--bg-primary); color: var(--text-primary); font-size: 1rem; font-family: inherit;" required>
265
- <option value="">Select an option</option>
266
- <option value="option1">Option 1</option>
267
- <option value="option2">Option 2</option>
268
- <option value="option3">Option 3</option>
269
- </select>
270
- </div>
271
-
272
- <!-- Checkbox -->
273
- <div style="display: flex; align-items: center; gap: 0.5rem;">
274
- <input type="checkbox" name="agree" id="agree" required>
275
- <label for="agree" style="font-size: 0.875rem; color: var(--text-primary); margin: 0;">I agree to the terms</label>
276
- </div>
277
-
278
- <!-- Radio Buttons -->
279
- <div style="display: flex; flex-direction: column; gap: 0.5rem;">
280
- <label style="display: block; font-size: 0.875rem; font-weight: 500; color: var(--text-primary); margin: 0;">Choice</label>
281
- <div style="display: flex; flex-direction: column; gap: 0.5rem;">
282
- <div style="display: flex; align-items: center; gap: 0.5rem;">
283
- <input type="radio" name="choice" id="choice1" value="yes" required>
284
- <label for="choice1" style="font-size: 0.875rem; color: var(--text-primary); margin: 0;">Yes</label>
285
- </div>
286
- <div style="display: flex; align-items: center; gap: 0.5rem;">
287
- <input type="radio" name="choice" id="choice2" value="no">
288
- <label for="choice2" style="font-size: 0.875rem; color: var(--text-primary); margin: 0;">No</label>
289
- </div>
290
- </div>
291
- </div>
292
-
293
- <!-- Submit Button -->
294
- <button type="submit" style="width: 100%; background: var(--color-primary); color: white; font-weight: 500; padding: 0.5rem 1rem; border: none; border-radius: 0.5rem; cursor: pointer; font-size: 1rem; font-family: inherit;">
295
- Submit
296
- </button>
297
- </form>
298
-
299
- <script>
300
- function handleFormSubmit(event) {
301
- event.preventDefault();
302
- const formData = new FormData(event.target);
303
- const data = Object.fromEntries(formData);
304
- console.log('Form submitted:', data);
305
- return false;
306
- }
307
- </script>
308
- </div>
309
- \`\`\`
310
-
311
- ## Form Input Guidelines
312
-
313
- - ✓ Always wrap forms in proper semantic HTML
314
- - ✓ Include labels with "for" attributes
315
- - ✓ Use proper input types (text, email, password, number, etc.)
316
- - ✓ Add placeholder text for guidance
317
- - ✓ Include required attributes where needed
318
- - ✓ Style consistently with Tailwind
319
- - ✓ Submit button MUST be included
320
- - ✓ Use onsubmit handler to capture data
321
-
322
- ### Icon + Text Combination
323
- \`\`\`html
324
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
325
- <div style="display: flex; gap: 0.75rem; align-items: flex-start;">
326
- <span style="font-size: 1.5rem; flex-shrink: 0;">⚡</span>
327
- <div>
328
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.25rem 0;">Performance</h4>
329
- <p style="color: var(--text-secondary); margin: 0;">High-speed data processing</p>
330
- </div>
331
- </div>
332
- <div style="display: flex; gap: 0.75rem; align-items: flex-start;">
333
- <span style="font-size: 1.5rem; flex-shrink: 0;">🔒</span>
334
- <div>
335
- <h4 style="font-weight: bold; color: var(--text-primary); margin: 0 0 0.25rem 0;">Security</h4>
336
- <p style="color: var(--text-secondary); margin: 0;">Enterprise-grade protection</p>
337
- </div>
338
- </div>
339
- </div>
340
- \`\`\`
341
-
342
- ### Data Visualization (ASCII-style)
343
- \`\`\`html
344
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
345
- <div style="background: var(--bg-secondary); padding: 1rem; border-radius: 0.5rem; font-family: monospace; font-size: 0.875rem; color: var(--text-primary); border: 1px solid var(--border-color);">
346
- <div>█████████░░░░░░░░░░ 50%</div>
347
- <div style="margin-top: 0.5rem;">████████████████░░░░ 80%</div>
348
- <div style="margin-top: 0.5rem;">██████████░░░░░░░░░░ 50%</div>
349
- </div>
350
- </div>
351
- \`\`\`
352
-
353
- ## Structure Template
354
-
355
- ALWAYS use this structure:
356
-
357
- \`\`\`html
358
- <div style="display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem;">
359
- <!-- Option 1: Just text -->
360
- <p style="color: var(--text-primary); line-height: 1.6; margin: 0;">Your response here</p>
361
-
362
- <!-- Option 2: With heading -->
363
- <h2 style="font-size: 1.5rem; font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Title</h2>
364
- <p style="color: var(--text-secondary); line-height: 1.6; margin: 0;">Content here</p>
365
-
366
- <!-- Option 3: With multiple sections -->
367
- <h2 style="font-size: 1.5rem; font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Title</h2>
368
- <div style="background: var(--bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
369
- <h3 style="font-size: 1.1rem; font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Section 1</h3>
370
- <p style="color: var(--text-secondary); margin: 0; line-height: 1.6;">Content for section 1</p>
371
- </div>
372
- <div style="background: var(--bg-secondary); padding: 1rem; border-radius: 0.5rem; border: 1px solid var(--border-color);">
373
- <h3 style="font-size: 1.1rem; font-weight: bold; color: var(--text-primary); margin: 0 0 0.5rem 0;">Section 2</h3>
374
- <p style="color: var(--text-secondary); margin: 0; line-height: 1.6;">Content for section 2</p>
375
- </div>
376
- </div>
377
- \`\`\`
378
-
379
- ## CRITICAL: CSS Variables Must Be Used
380
-
381
- The app theme system REQUIRES CSS variables. Do NOT use:
382
- - Tailwind CSS classes (they break the theme)
383
- - Hard-coded colors like #ffffff or #000000 (they ignore dark mode)
384
- - Generic color names like "blue" or "gray" (use var(--color-primary) instead)
385
-
386
- ALWAYS use these CSS variables for theme-aware styling:
387
- - var(--text-primary) for main text
388
- - var(--text-secondary) for secondary text
389
- - var(--text-tertiary) for tertiary text
390
- - var(--bg-primary) for main background
391
- - var(--bg-secondary) for secondary background
392
- - var(--bg-tertiary) for tertiary background
393
- - var(--border-color) for borders
394
- - var(--color-primary) for brand blue
395
- - var(--color-success) for success green
396
- - var(--color-warning) for warning orange/yellow
397
- - var(--color-danger) for error red
398
- - var(--color-info) for info blue
399
-
400
- ## Validation
401
-
402
- Before you respond, verify:
403
- - [ ] Response starts with \`\`\`html
404
- - [ ] Response ends with \`\`\`
405
- - [ ] All HTML is valid and balanced
406
- - [ ] Root div uses inline styles with display flex and gap
407
- - [ ] ALL text colors use var(--text-*) variables
408
- - [ ] ALL backgrounds use var(--bg-*) or var(--color-*) variables
409
- - [ ] ALL borders use var(--border-color) variable
410
- - [ ] NO Tailwind CSS classes anywhere
411
- - [ ] NO hard-coded colors anywhere
412
- - [ ] No plain text outside HTML container
413
- - [ ] No markdown formatting
414
-
415
- ## Final Reminder
416
-
417
- You are responding in a web interface with RippleUI dark/light theme support.
418
- The user sees YOUR HTML directly, and it must adapt to their theme preference.
419
-
420
- ALWAYS use CSS variables. They automatically adapt to dark/light mode.
421
- Make it beautiful in both light AND dark mode.
422
- Make it clear. Make it professional.
423
-
424
- NO TAILWIND. CSS VARIABLES ONLY. HTML ONLY.`;
425
-
426
- export default SYSTEM_PROMPT;