Skip to main content
Glama

LINE Bot MCP Server

by tndfame
tmp_ai.tsx7.45 kB
"use client"; import { useEffect, useState } from "react"; type StyleConfig = { personaName: string; tone: string; // friendly, formal, playful, concise language: string; // th, en, th-en emojiLevel: number; // 0..3 replyLength: string; // short, medium, long includeSignature: boolean; extraGuidelines: string; politeParticle?: string; // "", "ครับ", "ค่ะ" greetWithName?: boolean; }; const defaults: StyleConfig = { personaName: "Default", tone: "friendly", language: "th", emojiLevel: 1, replyLength: "medium", includeSignature: false, extraGuidelines: "", politeParticle: "", greetWithName: true, }; export default function AiStylePage() { const [cfg, setCfg] = useState<StyleConfig>(defaults); const [loading, setLoading] = useState(false); const [error, setError] = useState<string>(""); const [ok, setOk] = useState<string>(""); async function load() { setError(""); try { const res = await fetch(`/api/ai-style`); const data = await res.json(); setCfg({ ...defaults, ...(data || {}) }); } catch (e: any) { setError(e?.message || String(e)); } } async function save() { setLoading(true); setError(""); setOk(""); try { const res = await fetch(`/api/ai-style`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(cfg), }); const data = await res.json(); if (!res.ok) throw new Error(data?.error || "Failed to save"); setOk("Saved"); } catch (e: any) { setError(e?.message || String(e)); } finally { setLoading(false); } } useEffect(() => { load(); }, []); const preview = `Personal: ${cfg.personaName}\nTone: ${cfg.tone}\nLanguage: ${cfg.language}\nEmoji: ${cfg.emojiLevel}\nLength: ${cfg.replyLength}\nSignature: ${cfg.includeSignature ? "Yes" : "No"}\nPolite: ${cfg.politeParticle || "-"}\nGreet with name: ${cfg.greetWithName ? "Yes" : "No"}\n\nGuidelines:\n${cfg.extraGuidelines || "-"}`; return ( <section className="card panel" style={{ width: "100%", maxHeight: "93vh", overflowY: "auto" }}> <div className="section"> <div className="card" style={{ padding: 12 }}> <div className="field" style={{ marginBottom: 0 }}> <div className="header"> <div className="title">Custom Style</div> </div> <div className="row" style={{ gap: 8, marginBottom: 8 }}> <button className="btn" onClick={save} disabled={loading}>{loading ? "Saving..." : "Save"}</button> <button className="btn secondary" onClick={() => setCfg(defaults)} disabled={loading}>Reset</button> {ok ? <span className="badge">{ok}</span> : null} {error ? <span className="badge" style={{ background: '#fee2e2', color: '#991b1b' }}>Error: {error}</span> : null} </div> <div className="split"> <div className="field"> <label>Personal name</label> <input value={cfg.personaName} onChange={e => setCfg({ ...cfg, personaName: e.target.value })} placeholder="e.g., Friendly Assistant" /> </div> <div className="field"> <label>Tone</label> <select value={cfg.tone} onChange={e => setCfg({ ...cfg, tone: e.target.value })}> <option value="friendly">friendly</option> <option value="formal">formal</option> <option value="playful">playful</option> <option value="concise">concise</option> </select> </div> <div className="field"> <label>Language</label> <select value={cfg.language} onChange={e => setCfg({ ...cfg, language: e.target.value })}> <option value="th">Thai</option> <option value="en">English</option> <option value="th-en">Thai-English mix</option> </select> </div> <div className="field"> <label>Polite Particle</label> <select value={cfg.politeParticle || ""} onChange={e => setCfg({ ...cfg, politeParticle: e.target.value })}> <option value="">(none)</option> <option value="ครับ">ครับ</option> <option value="ค่ะ">ค่ะ</option> </select> </div> </div> <div className="split"> <div className="field"> <label>Emoji Level</label> <input type="range" min={0} max={3} value={cfg.emojiLevel} onChange={e => setCfg({ ...cfg, emojiLevel: parseInt(e.target.value, 10) })} /> <span className="badge" style={{ marginLeft: 8 }}>{cfg.emojiLevel}</span> </div> <div className="field"> <label>Reply Length</label> <select value={cfg.replyLength} onChange={e => setCfg({ ...cfg, replyLength: e.target.value })}> <option value="short">short</option> <option value="medium">medium</option> <option value="long">long</option> </select> </div> </div> <div className="split"> <div className="field"> <label>Signature</label> <div className="switch"> <input id="toggle-signature" type="checkbox" checked={cfg.includeSignature} onChange={e => setCfg({ ...cfg, includeSignature: e.target.checked })} /> <label htmlFor="toggle-signature">Include Signature</label> </div> <input placeholder="Signature text (supports {name})" value={(cfg as any).signatureText || ""} onChange={e => setCfg({ ...cfg, signatureText: e.target.value })} disabled={!cfg.includeSignature} /> </div> <div className="field"> <label>Greeting</label> <div className="switch"> <input id="toggle-greet" type="checkbox" checked={!!cfg.greetWithName} onChange={e => setCfg({ ...cfg, greetWithName: e.target.checked })} /> <label htmlFor="toggle-greet">Greet with user's display name</label> </div> </div> </div> <div className="field"> <label>Extra Guidelines (Markdown)</label> <textarea value={cfg.extraGuidelines} onChange={e => setCfg({ ...cfg, extraGuidelines: e.target.value })} placeholder="- หลีกเลี่ยงคำแสลง - เพิ่มคำลงท้ายสุภาพในภาษาไทย" style={{ minHeight: 200 }} /> </div> </div> <div className="section"> <label>Preview (what we’ll pass as style chunk)</label> <pre style={{ whiteSpace: "pre-wrap", margin: 0 }}>{preview}</pre> </div> </div> </div> </section> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tndfame/mcp_management'

If you have feedback or need assistance with the MCP directory API, please join our Discord server