agent-drawer.html

118 lines
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
<!-- Agent Drawer -->
<div id="agent-drawer" class="fixed top-0 right-0 h-full w-full sm:w-[420px] z-[60] translate-x-full transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)]">
    <div class="flex flex-col h-full bg-white/95 backdrop-blur-xl border-l border-slate-200/40 shadow-2xl shadow-indigo-900/10">
        <!-- Header -->
        <div class="flex items-center justify-between px-4 py-3 border-b border-slate-100/80 bg-gradient-to-r from-white/80 to-indigo-50/30">
            <div class="flex items-center gap-2.5">
                <div class="w-7 h-7 rounded-lg bg-gradient-to-br from-indigo-500 to-violet-600 flex items-center justify-center shadow-sm shadow-indigo-500/20">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09zM18.259 8.715L18 9.75l-.259-1.035a3.375 3.375 0 00-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 002.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 002.455 2.456L21.75 6l-1.036.259a3.375 3.375 0 00-2.455 2.456z" /></svg>
                </div>
                <h3 class="text-sm font-semibold text-slate-700">Agent</h3>
            </div>
            <div class="flex items-center gap-1">
                <div class="dropdown dropdown-end">
                    <div tabindex="0" role="button" class="inline-flex items-center justify-center w-7 h-7 rounded-md text-slate-400 hover:bg-slate-100 transition-colors" title="History">
                        <svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
                    </div>
                    <div tabindex="0" class="dropdown-content menu bg-white rounded-lg z-[1] w-56 p-1.5 shadow-lg border border-slate-200 max-h-72 overflow-y-auto">
                        <button class="btn btn-ghost btn-xs w-full mb-1 text-indigo-600 font-medium" hx-post="/agent/conversations/new" hx-target="body">
                            <svg xmlns="http://www.w3.org/2000/svg" class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" /></svg>
                            New Chat
                        </button>
                        {{range $conv := dashboard.Conversations}}
                        <div class="flex items-center group">
                            <a class="flex-1 min-w-0 rounded px-2.5 py-1.5 text-xs text-slate-500 hover:bg-slate-50 hover:text-slate-700 truncate cursor-pointer"
                               hx-get="/agent/conversations/{{$conv.ID}}" hx-target="#drawer-messages" hx-swap="innerHTML">
                                {{$conv.Title}}
                            </a>
                            <button class="shrink-0 w-5 h-5 inline-flex items-center justify-center rounded text-slate-300 hover:text-red-400 hover:bg-red-50 opacity-0 group-hover:opacity-100 transition-all"
                                hx-post="/agent/conversations/{{$conv.ID}}/delete" hx-confirm="Delete this conversation?" hx-target="body" title="Delete">
                                <svg xmlns="http://www.w3.org/2000/svg" class="w-2.5 h-2.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
                            </button>
                        </div>
                        {{end}}
                        {{if not dashboard.Conversations}}
                        <p class="text-[10px] text-slate-300 text-center py-2">No conversations yet</p>
                        {{end}}
                    </div>
                </div>
                <button class="inline-flex items-center justify-center w-7 h-7 rounded-md text-slate-400 hover:bg-slate-100 transition-colors" onclick="document.body.classList.remove('has-drawer')" title="Close (Esc)">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
                </button>
            </div>
        </div>

        {{if not dashboard.HasAgent}}
        <div class="flex-1 flex items-center justify-center p-6">
            <div class="text-center">
                <div class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-amber-50 mb-4">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" /></svg>
                </div>
                <p class="text-sm text-slate-600 mb-1.5 font-medium">Claude Code not authenticated</p>
                <p class="text-xs text-slate-400 leading-relaxed">Open the IDE terminal and run <code class="text-[11px] bg-slate-100 text-indigo-600 px-1.5 py-0.5 rounded font-mono">claude</code> to sign in</p>
            </div>
        </div>
        {{else}}
        <div id="drawer-messages" class="flex-1 overflow-y-auto px-4 py-4 space-y-3 bg-gradient-to-b from-white/0 to-indigo-50/20">
            <div class="text-center py-16">
                <div class="inline-flex items-center justify-center w-12 h-12 rounded-2xl bg-gradient-to-br from-indigo-50 to-violet-50 mb-3 shadow-sm shadow-indigo-100/50">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-indigo-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z" /></svg>
                </div>
                <p class="text-slate-500 text-xs font-medium mb-1">Ask the agent anything</p>
                <p class="text-slate-300 text-[10px]">Manage services, edit code, deploy apps</p>
            </div>
        </div>

        <div id="drawer-loading" class="hidden px-4 pb-2">
            <div class="flex items-center gap-2.5 px-3 py-2 bg-indigo-50/50 rounded-xl">
                <span class="loading loading-dots loading-xs text-indigo-400"></span>
                <span class="text-xs text-indigo-400">Thinking...</span>
            </div>
        </div>

        <div class="border-t border-slate-100/80 p-3 bg-white/80">
            <form id="drawer-chat-form" hx-post="/agent/chat" hx-target="#drawer-messages" hx-swap="beforeend" class="flex gap-2">
                <input type="hidden" name="conversation_id" id="drawer-conversation-id" value="" />
                <textarea name="message" id="agent-input"
                    class="flex-1 min-h-[40px] max-h-24 resize-none text-sm bg-slate-50/80 border border-slate-200/60 rounded-xl px-3 py-2.5 focus:border-indigo-300 focus:ring-2 focus:ring-indigo-100 focus:outline-none transition-all placeholder:text-slate-300"
                    placeholder="Ask anything..."
                    rows="1"
                    onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();this.form.requestSubmit()}"
                    required></textarea>
                <button type="submit" class="self-end w-9 h-9 rounded-xl bg-gradient-to-br from-indigo-500 to-indigo-600 hover:from-indigo-600 hover:to-indigo-700 text-white flex items-center justify-center shadow-sm shadow-indigo-500/20 transition-all btn-press shrink-0">
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /></svg>
                </button>
            </form>
            <p class="text-[9px] text-slate-300 mt-1.5 text-center">Press <kbd class="font-mono bg-slate-100 px-1 rounded text-slate-400">/</kbd> to focus &middot; Shift+Enter for newline</p>
        </div>
        {{end}}
    </div>
</div>

<script>
document.addEventListener('htmx:beforeRequest', function(e) {
    if (e.detail.elt.id === 'drawer-chat-form') {
        var input = document.getElementById('agent-input');
        var msg = input.value.trim();
        if (msg) {
            var messagesDiv = document.getElementById('drawer-messages');
            var placeholder = messagesDiv.querySelector('.text-center');
            if (placeholder) placeholder.remove();
            messagesDiv.insertAdjacentHTML('beforeend',
                '<div class="flex justify-end"><div class="max-w-[80%] user-bubble px-3.5 py-2.5 text-sm whitespace-pre-wrap">' +
                msg.replace(/</g, '&lt;').replace(/>/g, '&gt;') +
                '</div></div>');
            document.getElementById('drawer-loading').classList.remove('hidden');
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }
    }
});
document.addEventListener('htmx:afterRequest', function(e) {
    if (e.detail.elt.id === 'drawer-chat-form') {
        document.getElementById('drawer-loading').classList.add('hidden');
        document.getElementById('agent-input').value = '';
        var messagesDiv = document.getElementById('drawer-messages');
        messagesDiv.scrollTop = messagesDiv.scrollHeight;
    }
});
</script>