@rubytech/create-maxy 1.0.0

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.
Files changed (181) hide show
  1. package/dist/index.js +428 -0
  2. package/package.json +31 -0
  3. package/payload/maxy/.env.example +12 -0
  4. package/payload/maxy/app/admin/components/ActivityTimeline.tsx +348 -0
  5. package/payload/maxy/app/admin/components/MarkdownMessage.tsx +40 -0
  6. package/payload/maxy/app/api/admin/chat/route.ts +72 -0
  7. package/payload/maxy/app/api/admin/logs/route.ts +40 -0
  8. package/payload/maxy/app/api/admin/session/route.ts +74 -0
  9. package/payload/maxy/app/api/chat/route.ts +72 -0
  10. package/payload/maxy/app/api/health/route.ts +26 -0
  11. package/payload/maxy/app/api/onboarding/claude-auth/route.ts +216 -0
  12. package/payload/maxy/app/api/onboarding/set-pin/route.ts +44 -0
  13. package/payload/maxy/app/api/session/route.ts +51 -0
  14. package/payload/maxy/app/api/telegram/webhook/route.ts +107 -0
  15. package/payload/maxy/app/apple-icon.png +0 -0
  16. package/payload/maxy/app/bot/page.tsx +373 -0
  17. package/payload/maxy/app/favicon.ico +0 -0
  18. package/payload/maxy/app/globals.css +1681 -0
  19. package/payload/maxy/app/layout.tsx +58 -0
  20. package/payload/maxy/app/lib/claude-agent.ts +503 -0
  21. package/payload/maxy/app/og/layout.tsx +15 -0
  22. package/payload/maxy/app/og/page.tsx +252 -0
  23. package/payload/maxy/app/page.tsx +594 -0
  24. package/payload/maxy/app/privacy/page.tsx +72 -0
  25. package/payload/maxy/app/public/page.tsx +266 -0
  26. package/payload/maxy/next.config.mjs +26 -0
  27. package/payload/maxy/package-lock.json +2198 -0
  28. package/payload/maxy/package.json +25 -0
  29. package/payload/maxy/proxy.ts +41 -0
  30. package/payload/maxy/public/brand/claude.png +0 -0
  31. package/payload/maxy/public/brand/maxy-black.png +0 -0
  32. package/payload/maxy/public/brand/maxy.png +0 -0
  33. package/payload/maxy/public/favicon.ico +0 -0
  34. package/payload/maxy/public/og-landscape.png +0 -0
  35. package/payload/maxy/public/og-portrait.png +0 -0
  36. package/payload/maxy/public/og-square.png +0 -0
  37. package/payload/maxy/public/pi-5.jpg +0 -0
  38. package/payload/maxy/public/robots.txt +5 -0
  39. package/payload/maxy/tsconfig.json +41 -0
  40. package/payload/maxy/tsconfig.tsbuildinfo +1 -0
  41. package/payload/maxy/ui.md +28 -0
  42. package/payload/platform/config/cloudflared.yml +17 -0
  43. package/payload/platform/knowledge/maxy.md +161 -0
  44. package/payload/platform/neo4j/schema.cypher +108 -0
  45. package/payload/platform/package-lock.json +1835 -0
  46. package/payload/platform/package.json +17 -0
  47. package/payload/platform/plugins/admin/PLUGIN.md +24 -0
  48. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +56 -0
  49. package/payload/platform/plugins/admin/hooks/session-start.sh +20 -0
  50. package/payload/platform/plugins/admin/mcp/dist/index.d.ts +2 -0
  51. package/payload/platform/plugins/admin/mcp/dist/index.d.ts.map +1 -0
  52. package/payload/platform/plugins/admin/mcp/dist/index.js +149 -0
  53. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -0
  54. package/payload/platform/plugins/admin/mcp/package.json +18 -0
  55. package/payload/platform/plugins/anthropic/PLUGIN.md +30 -0
  56. package/payload/platform/plugins/anthropic/references/setup-guide.md +146 -0
  57. package/payload/platform/plugins/business-assistant/PLUGIN.md +46 -0
  58. package/payload/platform/plugins/business-assistant/references/crm.md +112 -0
  59. package/payload/platform/plugins/business-assistant/references/document-management.md +96 -0
  60. package/payload/platform/plugins/business-assistant/references/escalation.md +126 -0
  61. package/payload/platform/plugins/business-assistant/references/invoicing.md +163 -0
  62. package/payload/platform/plugins/business-assistant/references/quoting.md +56 -0
  63. package/payload/platform/plugins/business-assistant/references/scheduling.md +127 -0
  64. package/payload/platform/plugins/cloudflare/PLUGIN.md +31 -0
  65. package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts +2 -0
  66. package/payload/platform/plugins/cloudflare/mcp/dist/index.d.ts.map +1 -0
  67. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +174 -0
  68. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -0
  69. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +45 -0
  70. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -0
  71. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +256 -0
  72. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -0
  73. package/payload/platform/plugins/cloudflare/mcp/package.json +18 -0
  74. package/payload/platform/plugins/cloudflare/references/setup-guide.md +110 -0
  75. package/payload/platform/plugins/contacts/PLUGIN.md +18 -0
  76. package/payload/platform/plugins/contacts/mcp/dist/index.d.ts +2 -0
  77. package/payload/platform/plugins/contacts/mcp/dist/index.d.ts.map +1 -0
  78. package/payload/platform/plugins/contacts/mcp/dist/index.js +182 -0
  79. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -0
  80. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts +5 -0
  81. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.d.ts.map +1 -0
  82. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js +34 -0
  83. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js.map +1 -0
  84. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +19 -0
  85. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -0
  86. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +68 -0
  87. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -0
  88. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts +22 -0
  89. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.d.ts.map +1 -0
  90. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js +46 -0
  91. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-list.js.map +1 -0
  92. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +20 -0
  93. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -0
  94. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +56 -0
  95. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -0
  96. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts +13 -0
  97. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.d.ts.map +1 -0
  98. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js +54 -0
  99. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-update.js.map +1 -0
  100. package/payload/platform/plugins/contacts/mcp/package.json +19 -0
  101. package/payload/platform/plugins/documents/PLUGIN.md +12 -0
  102. package/payload/platform/plugins/documents/mcp/dist/index.d.ts +2 -0
  103. package/payload/platform/plugins/documents/mcp/dist/index.d.ts.map +1 -0
  104. package/payload/platform/plugins/documents/mcp/dist/index.js +82 -0
  105. package/payload/platform/plugins/documents/mcp/dist/index.js.map +1 -0
  106. package/payload/platform/plugins/documents/mcp/package.json +20 -0
  107. package/payload/platform/plugins/memory/PLUGIN.md +17 -0
  108. package/payload/platform/plugins/memory/mcp/dist/index.d.ts +2 -0
  109. package/payload/platform/plugins/memory/mcp/dist/index.d.ts.map +1 -0
  110. package/payload/platform/plugins/memory/mcp/dist/index.js +164 -0
  111. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -0
  112. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts +3 -0
  113. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.d.ts.map +1 -0
  114. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js +29 -0
  115. package/payload/platform/plugins/memory/mcp/dist/lib/embeddings.js.map +1 -0
  116. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts +5 -0
  117. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.d.ts.map +1 -0
  118. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +34 -0
  119. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -0
  120. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts +8 -0
  121. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.d.ts.map +1 -0
  122. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js +71 -0
  123. package/payload/platform/plugins/memory/mcp/dist/tools/memory-reindex.js.map +1 -0
  124. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts +24 -0
  125. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.d.ts.map +1 -0
  126. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js +125 -0
  127. package/payload/platform/plugins/memory/mcp/dist/tools/memory-search.js.map +1 -0
  128. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +18 -0
  129. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -0
  130. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +56 -0
  131. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -0
  132. package/payload/platform/plugins/memory/mcp/package.json +19 -0
  133. package/payload/platform/plugins/sales/PLUGIN.md +65 -0
  134. package/payload/platform/plugins/sales/references/close-tracking.md +76 -0
  135. package/payload/platform/plugins/sales/references/closing-framework.md +108 -0
  136. package/payload/platform/plugins/sales/references/comparisons.md +99 -0
  137. package/payload/platform/plugins/sales/references/competitive-positioning.md +51 -0
  138. package/payload/platform/plugins/sales/references/faq.md +62 -0
  139. package/payload/platform/plugins/sales/references/objection-handling.md +157 -0
  140. package/payload/platform/plugins/sales/references/pricing.md +71 -0
  141. package/payload/platform/plugins/sales/references/waitlist.md +23 -0
  142. package/payload/platform/plugins/scheduling/PLUGIN.md +12 -0
  143. package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts +2 -0
  144. package/payload/platform/plugins/scheduling/mcp/dist/index.d.ts.map +1 -0
  145. package/payload/platform/plugins/scheduling/mcp/dist/index.js +13 -0
  146. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -0
  147. package/payload/platform/plugins/scheduling/mcp/package.json +18 -0
  148. package/payload/platform/plugins/telegram/PLUGIN.md +31 -0
  149. package/payload/platform/plugins/telegram/mcp/dist/index.d.ts +2 -0
  150. package/payload/platform/plugins/telegram/mcp/dist/index.d.ts.map +1 -0
  151. package/payload/platform/plugins/telegram/mcp/dist/index.js +101 -0
  152. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -0
  153. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts +27 -0
  154. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.d.ts.map +1 -0
  155. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js +41 -0
  156. package/payload/platform/plugins/telegram/mcp/dist/lib/telegram.js.map +1 -0
  157. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts +16 -0
  158. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.d.ts.map +1 -0
  159. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +62 -0
  160. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -0
  161. package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts +20 -0
  162. package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -0
  163. package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +34 -0
  164. package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -0
  165. package/payload/platform/plugins/telegram/mcp/package.json +19 -0
  166. package/payload/platform/plugins/telegram/references/setup-guide.md +50 -0
  167. package/payload/platform/plugins/web/PLUGIN.md +12 -0
  168. package/payload/platform/plugins/web/mcp/dist/index.d.ts +2 -0
  169. package/payload/platform/plugins/web/mcp/dist/index.d.ts.map +1 -0
  170. package/payload/platform/plugins/web/mcp/dist/index.js +12 -0
  171. package/payload/platform/plugins/web/mcp/dist/index.js.map +1 -0
  172. package/payload/platform/plugins/web/mcp/package.json +18 -0
  173. package/payload/platform/scripts/seed-neo4j.sh +73 -0
  174. package/payload/platform/scripts/setup.sh +177 -0
  175. package/payload/platform/scripts/start.sh +62 -0
  176. package/payload/platform/templates/account.json +4 -0
  177. package/payload/platform/templates/agents/admin/IDENTITY.md +28 -0
  178. package/payload/platform/templates/agents/admin/SOUL.md +1 -0
  179. package/payload/platform/templates/agents/public/IDENTITY.md +21 -0
  180. package/payload/platform/templates/agents/public/SOUL.md +1 -0
  181. package/payload/platform/tsconfig.base.json +18 -0
@@ -0,0 +1,266 @@
1
+ 'use client'
2
+
3
+ import { useState, useRef, useEffect, useCallback, FormEvent } from 'react'
4
+
5
+ interface Message {
6
+ role: 'maxy' | 'visitor'
7
+ content: string
8
+ }
9
+
10
+ const suggestions: string[] = []
11
+
12
+ /** Generate a random session ID (Taskmaster prepends its own anon- prefix). */
13
+ function generateSessionId(): string {
14
+ return crypto.randomUUID()
15
+ }
16
+
17
+ export default function ChatPage() {
18
+ const [messages, setMessages] = useState<Message[]>([])
19
+ const [remainingSuggestions, setRemainingSuggestions] = useState(suggestions)
20
+ const [input, setInput] = useState('')
21
+ const [isStreaming, setIsStreaming] = useState(false)
22
+ const messagesEndRef = useRef<HTMLDivElement>(null)
23
+ const inputRef = useRef<HTMLInputElement>(null)
24
+ const nudgeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
25
+ const sessionKeyRef = useRef<string | null>(null)
26
+ const sessionPendingRef = useRef<Promise<string | null> | null>(null)
27
+
28
+ // Scroll to bottom when messages change
29
+ useEffect(() => {
30
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
31
+ }, [messages])
32
+
33
+ // Nudge timer — if no input after 15s, show gentle prompt
34
+ useEffect(() => {
35
+ nudgeTimerRef.current = setTimeout(() => {
36
+ setMessages(prev => {
37
+ if (prev.length > 0) return prev // user already started chatting
38
+ return [{ role: 'maxy', content: 'No wrong answers. Just tell me what\'s on your mind.' }]
39
+ })
40
+ }, 15000)
41
+ return () => {
42
+ if (nudgeTimerRef.current) clearTimeout(nudgeTimerRef.current)
43
+ }
44
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
45
+
46
+ // Focus input on mount
47
+ useEffect(() => {
48
+ inputRef.current?.focus()
49
+ }, [])
50
+
51
+ /** Ensure we have a Taskmaster session, creating one if needed. */
52
+ const ensureSession = useCallback(async (): Promise<string | null> => {
53
+ if (sessionKeyRef.current) return sessionKeyRef.current
54
+
55
+ // Deduplicate concurrent calls
56
+ if (sessionPendingRef.current) return sessionPendingRef.current
57
+
58
+ const promise = (async () => {
59
+ try {
60
+ const res = await fetch('/api/session', {
61
+ method: 'POST',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({ session_id: generateSessionId() }),
64
+ })
65
+ if (!res.ok) return null
66
+ const data = await res.json()
67
+ sessionKeyRef.current = data.session_key
68
+ return data.session_key as string
69
+ } catch {
70
+ return null
71
+ } finally {
72
+ sessionPendingRef.current = null
73
+ }
74
+ })()
75
+
76
+ sessionPendingRef.current = promise
77
+ return promise
78
+ }, [])
79
+
80
+ async function sendMessage(text: string) {
81
+ if (!text || isStreaming) return
82
+
83
+ // Clear nudge timer
84
+ if (nudgeTimerRef.current) {
85
+ clearTimeout(nudgeTimerRef.current)
86
+ nudgeTimerRef.current = null
87
+ }
88
+
89
+ // Remove this suggestion if it was from the list
90
+ setRemainingSuggestions(prev => prev.filter(s => s !== text))
91
+
92
+ // Add visitor message
93
+ const visitorMessage: Message = { role: 'visitor', content: text }
94
+ const updatedMessages = [...messages, visitorMessage]
95
+ setMessages(updatedMessages)
96
+ setInput('')
97
+ setIsStreaming(true)
98
+
99
+ // Add placeholder for Maxy's response
100
+ const placeholderIndex = updatedMessages.length
101
+ setMessages(prev => [...prev, { role: 'maxy', content: '' }])
102
+
103
+ try {
104
+ // Ensure we have a session before sending
105
+ const sessionKey = await ensureSession()
106
+ if (!sessionKey) {
107
+ throw new Error('session')
108
+ }
109
+
110
+ const res = await fetch('/api/chat', {
111
+ method: 'POST',
112
+ headers: { 'Content-Type': 'application/json' },
113
+ body: JSON.stringify({ message: text, session_key: sessionKey }),
114
+ })
115
+
116
+ if (!res.ok) {
117
+ const errorData = await res.json().catch(() => ({}))
118
+ throw new Error(errorData.error || 'Something went wrong')
119
+ }
120
+
121
+ const reader = res.body?.getReader()
122
+ const decoder = new TextDecoder()
123
+
124
+ if (!reader) throw new Error('No response stream')
125
+
126
+ let accumulated = ''
127
+ let sseBuffer = ''
128
+
129
+ while (true) {
130
+ const { done, value } = await reader.read()
131
+ if (done) break
132
+
133
+ sseBuffer += decoder.decode(value, { stream: true })
134
+ const parts = sseBuffer.split('\n')
135
+ sseBuffer = parts.pop()! // keep incomplete trailing segment
136
+
137
+ for (const line of parts) {
138
+ if (!line.startsWith('data: ')) continue
139
+ const data = line.slice(6)
140
+ if (data === '[DONE]') continue
141
+
142
+ let parsed
143
+ try { parsed = JSON.parse(data) } catch { continue }
144
+
145
+ if (parsed.error) throw new Error(parsed.error)
146
+ if (parsed.text) {
147
+ accumulated += parsed.text
148
+ setMessages(prev => {
149
+ const updated = [...prev]
150
+ updated[placeholderIndex] = {
151
+ role: 'maxy',
152
+ content: accumulated,
153
+ }
154
+ return updated
155
+ })
156
+ }
157
+ }
158
+ }
159
+ } catch (err) {
160
+ const errorMessage = err instanceof Error ? err.message : 'Something went wrong'
161
+ setMessages(prev => {
162
+ const updated = [...prev]
163
+ updated[placeholderIndex] = {
164
+ role: 'maxy',
165
+ content: errorMessage === 'session'
166
+ ? "I'm having trouble connecting right now. Try refreshing the page."
167
+ : "Sorry, I hit a snag. Try again in a moment.",
168
+ }
169
+ return updated
170
+ })
171
+ } finally {
172
+ setIsStreaming(false)
173
+ inputRef.current?.focus()
174
+ }
175
+ }
176
+
177
+ function handleSubmit(e: FormEvent) {
178
+ e.preventDefault()
179
+ sendMessage(input.trim())
180
+ }
181
+
182
+ return (
183
+ <div className="chat-page">
184
+ <header className="chat-header">
185
+ <img src="/brand/maxy-black.png" alt="Maxy" className="chat-logo" />
186
+ <h1 className="chat-tagline">No Stress. Quiet Life.</h1>
187
+ <p className="chat-intro">
188
+ Hey, I{'\u2019'}m Maxy. I{'\u2019'}m a personal AI that handles the
189
+ stuff you don{'\u2019'}t want to think about. I{'\u2019'}m not a product
190
+ yet{'\u2009'}{'\u2014'}{'\u2009'}I{'\u2019'}m learning what stresses people
191
+ most so I can be built right. Tell me what{'\u2019'}s on your mind.
192
+ No advice, no judgment, just a conversation.
193
+ </p>
194
+ <div className="chat-trust">
195
+ <span className="chat-trust-item">Anonymous</span>
196
+ <span className="chat-trust-sep">{'\u00B7'}</span>
197
+ <span className="chat-trust-item">Private</span>
198
+ <span className="chat-trust-sep">{'\u00B7'}</span>
199
+ <span className="chat-trust-item">No sign-up</span>
200
+ <span className="chat-trust-sep">{'\u00B7'}</span>
201
+ <span className="chat-trust-item">Free</span>
202
+ </div>
203
+ </header>
204
+
205
+ <div className="chat-messages">
206
+ {messages.map((msg, i) => (
207
+ <div key={i} className={`message ${msg.role}`}>
208
+ <div className="bubble">
209
+ {msg.content || (
210
+ <span className="typing-indicator">
211
+ <span className="typing-dot" />
212
+ <span className="typing-dot" />
213
+ <span className="typing-dot" />
214
+ </span>
215
+ )}
216
+ </div>
217
+ </div>
218
+ ))}
219
+ {!isStreaming && remainingSuggestions.length > 0 && (
220
+ <div className="chat-suggestions">
221
+ {remainingSuggestions.map(s => (
222
+ <button
223
+ key={s}
224
+ className="chat-suggestion"
225
+ onClick={() => sendMessage(s)}
226
+ >
227
+ {s}
228
+ </button>
229
+ ))}
230
+ </div>
231
+ )}
232
+ <div ref={messagesEndRef} />
233
+ </div>
234
+
235
+ <div className="chat-input-area">
236
+ <form className="chat-form" onSubmit={handleSubmit}>
237
+ <input
238
+ ref={inputRef}
239
+ className="chat-input"
240
+ type="text"
241
+ value={input}
242
+ onChange={e => setInput(e.target.value)}
243
+ placeholder="Tell me what you need taking care of in your life!"
244
+ disabled={isStreaming}
245
+ aria-label="Type your message"
246
+ />
247
+ <button
248
+ className="chat-send"
249
+ type="submit"
250
+ disabled={isStreaming || !input.trim()}
251
+ aria-label="Send message"
252
+ >
253
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
254
+ <line x1="5" y1="12" x2="19" y2="12" />
255
+ <polyline points="12 5 19 12 12 19" />
256
+ </svg>
257
+ </button>
258
+ </form>
259
+ </div>
260
+
261
+ <footer className="chat-footer">
262
+ <a href="https://maxy.bot">maxy.bot {'\u2197'}</a>
263
+ </footer>
264
+ </div>
265
+ )
266
+ }
@@ -0,0 +1,26 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ poweredByHeader: false,
4
+ devIndicators: { appIsrStatus: false },
5
+ async headers() {
6
+ return [
7
+ {
8
+ source: '/(.*)',
9
+ headers: [
10
+ { key: 'X-Content-Type-Options', value: 'nosniff' },
11
+ { key: 'X-Frame-Options', value: 'DENY' },
12
+ { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
13
+ ],
14
+ },
15
+ {
16
+ // VNC viewer is loaded in an iframe on the same origin — allow same-origin framing
17
+ source: '/vnc-viewer.html',
18
+ headers: [
19
+ { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
20
+ ],
21
+ },
22
+ ]
23
+ },
24
+ }
25
+
26
+ export default nextConfig