Mercurial Hosting > lang
changeset 1:1c87f785eb42
start chat
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 08 Jul 2025 14:18:25 -0600 |
parents | 9845dcb9f5fc |
children | 78708fa556a0 |
files | src/error_log.js.luan src/images/send.svg src/images/spinner_green.gif src/index.html.luan src/lib/Shared.luan src/site.css src/site.js |
diffstat | 7 files changed, 130 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/error_log.js.luan Tue Jul 08 14:18:25 2025 -0600 @@ -0,0 +1,26 @@ +local Luan = require "luan:Luan.luan" +local error = Luan.error +local String = require "luan:String.luan" +local trim = String.trim or error() +local regex = String.regex or error() +local contains = String.contains or error() +local Table = require "luan:Table.luan" +local concat = Table.concat or error() +local Http = require "luan:http/Http.luan" +local Logging = require "luan:logging/Logging.luan" +local logger = Logging.logger "error_log.js" + + + +local function priority(err) + return "error" +end + +return function() + local err = Http.request.parameters.err + if err == nil then + return -- stupid bots + end + local call = priority(err) + logger[call](trim(err).."\n"..trim(Http.request.raw_head).."\n") +end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/images/send.svg Tue Jul 08 14:18:25 2025 -0600 @@ -0,0 +1,1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M120-160v-640l760 320-760 320Zm80-120 474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z"/></svg> \ No newline at end of file
--- a/src/index.html.luan Tue Jul 08 10:40:25 2025 -0600 +++ b/src/index.html.luan Tue Jul 08 14:18:25 2025 -0600 @@ -8,6 +8,7 @@ return function() + local ai_key = "whatever" Io.stdout = Http.response.text_writer() %> <!doctype html> @@ -22,6 +23,19 @@ <% header() %> <div content> <h1>Lang</h1> + <div ai_container="<%=ai_key%>" > + <div flex> + <div scroll> + <h2>Let's chat</h2> + <div messages></div> + </div> + <div ask> + <textarea autofocus oninput="fixTextarea(event)" onkeydown="textareaKey('<%=ai_key%>',event)"></textarea> + <button onclick="askAi('<%=ai_key%>')" title="Send"><img src="/images/send.svg"></button> + </div> + </div> + <img waiting-ai-icon src="/images/spinner_green.gif"> + </div> </div> </body> </html>
--- a/src/lib/Shared.luan Tue Jul 08 10:40:25 2025 -0600 +++ b/src/lib/Shared.luan Tue Jul 08 14:18:25 2025 -0600 @@ -1,15 +1,19 @@ local Luan = require "luan:Luan.luan" local error = Luan.error +local Time = require "luan:Time.luan" local Shared = {} +local started = Time.now() + function Shared.head() %> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> - @import "/site.css"; + @import "/site.css?s=<%=started%>"; </style> + <script src="/site.js?s=<%=started%>"></script> <% end
--- a/src/site.css Tue Jul 08 10:40:25 2025 -0600 +++ b/src/site.css Tue Jul 08 14:18:25 2025 -0600 @@ -19,3 +19,28 @@ margin-right: 3%; margin-bottom: 2em; } + +[waiting-ai-icon] { + width: 100px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%,-50%); + z-index: 3000; + display: none; +} + +[ai_container] div[ask] { + padding-top: 1em; + padding-bottom: 1em; + padding-left: 12px; + padding-right: 12px; + display: flex; + gap: 8px; + align-items: flex-end; +} +[ai_container] textarea { + flex-grow: 1; + max-height: 150px; + resize: none; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/site.js Tue Jul 08 14:18:25 2025 -0600 @@ -0,0 +1,59 @@ +'use strict'; + +function ajax(url,postData) { + let request = new XMLHttpRequest(); + let method = postData ? "POST" : "GET"; + request.open( method, url ); + if( typeof(postData)==='string' ) + request.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" ); + request.onload = function() { + if( request.status !== 200 ) { + let err = 'sr_ajax failed: ' + request.status; + if( request.responseText ) { + err += '\n' + request.responseText; + document.write('<pre>'+request.responseText+'</pre>'); + } + err += '\npage = ' + window.location; + ajax( '/error_log.js', 'err='+encodeURIComponent(err) ); + return; + } + try { + //console.log(request.responseText); + eval( request.responseText ); + } catch(e) { + window.sr_err = '\najax-url = ' + url; + //window.sr_err += '\n'+('ajax-response =\n' + request.responseText).trim(); + throw e; + } + }; + request.send(postData); +} + +function showWaitingAiIcon() { + document.querySelector('[waiting-ai-icon]').style.display = 'block'; +} + +const isMobile = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0; + +function textareaKey(aiKey,event) { + if( event.keyCode===13 && !event.shiftKey && !event.ctrlKey && !isMobile ) { + event.preventDefault(); + askAi(aiKey); + } +} + +function fixTextarea(event) { + let textarea = event.target; + textarea.style.height = 'initial'; + textarea.style.height = (textarea.scrollHeight+2) + 'px'; + textarea.scrollIntoViewIfNeeded(false); +} + +function askAi(aiKey) { + let aiDiv = document.querySelector(`[ai_container="${aiKey}"]`); + let input = aiDiv.querySelector('textarea'); + let url = `ai_ask.js?key=${aiKey}&input=${encodeURIComponent(input.value)}`; + ajax(url); + input.value = ''; + showWaitingAiIcon(); +}