changeset 9:46097e607701

romaji
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 21 Jul 2025 15:16:47 -0600
parents 2b7dcf355a78
children b0b325565d30
files src/chat.css src/chat.html.luan src/chat.js src/lib/Chat.luan src/lib/Shared.luan src/lib/Utils.luan src/lib/ai/claude/Chat.luan src/lib/ai/claude/Claude.luan src/private/tools/chat.html.luan src/site.css src/site.js
diffstat 11 files changed, 102 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
diff -r 2b7dcf355a78 -r 46097e607701 src/chat.css
--- a/src/chat.css	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/chat.css	Mon Jul 21 15:16:47 2025 -0600
@@ -36,3 +36,7 @@
 dialog[rename] input {
 	width: 300px;
 }
+
+div[system_prompt] {
+	white-space-collapse: preserve;
+}
diff -r 2b7dcf355a78 -r 46097e607701 src/chat.html.luan
--- a/src/chat.html.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/chat.html.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -41,7 +41,6 @@
 		<script>
 			let chatId = <%= chat.id %>;
 		</script>
-		<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
 		<script src="/chat.js?s=<%=started%>"></script>
 	</head>
 	<body>
@@ -54,6 +53,7 @@
 					<div>
 						<span onclick="renameChat()">Rename Chat</span>
 						<span onclick="deleteChat()">Delete Chat</span>
+						<span onclick="systemPrompt()">System Prompt</span>
 					</div>
 				</span>
 			</div>
@@ -88,6 +88,15 @@
 				<button onclick="doDeleteChat(this)">Delete</button>
 			</div>
 		</dialog>
+		<dialog system_prompt>
+			<h2>System Prompt</h2>
+			<div system_prompt>
+<%				chat.output_system_prompt() %>
+			</div>
+			<div buttons>
+				<button onclick="closeModal(this)">Close</button>
+			</div>
+		</dialog>
 		<script>
 			handleMarkdown();
 		</script>
diff -r 2b7dcf355a78 -r 46097e607701 src/chat.js
--- a/src/chat.js	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/chat.js	Mon Jul 21 15:16:47 2025 -0600
@@ -27,6 +27,11 @@
 	ajax(`delete_chat.js?chat=${chatId}`);
 }
 
+function systemPrompt() {
+	let dialog = document.querySelector('dialog[system_prompt]');
+	dialog.showModal();
+}
+
 function showWaitingAiIcon() {
 	document.querySelector('[waiting-ai-icon]').style.display = 'block';
 }
@@ -35,15 +40,6 @@
 	document.querySelector('[waiting-ai-icon]').style.display = 'none';
 }
 
-// requires markdown-it
-function handleMarkdown() {
-	let converter = window.markdownit();
-	let divs = document.querySelectorAll('[role]');
-	for( let div of divs ) {
-		div.innerHTML = converter.render(div.textContent);
-	}
-}
-
 function updateAi(html) {
 	hideWaitingAiIcon();
 	document.querySelector('div[messages]').innerHTML = html;
diff -r 2b7dcf355a78 -r 46097e607701 src/lib/Chat.luan
--- a/src/lib/Chat.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/lib/Chat.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -61,6 +61,10 @@
 		return html_encode(chat.name)
 	end
 
+	function chat.output_system_prompt()
+		chat.ai.output_system_prompt(chat.ai_thread)
+	end
+
 	function chat.output_messages_html()
 		chat.ai.output_messages_html(chat.ai_thread)
 	end
diff -r 2b7dcf355a78 -r 46097e607701 src/lib/Shared.luan
--- a/src/lib/Shared.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/lib/Shared.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -21,6 +21,7 @@
 		<style>
 			@import "/site.css?s=<%=started%>";
 		</style>
+		<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
 		<script src="/site.js?s=<%=started%>"></script>
 <%
 end
diff -r 2b7dcf355a78 -r 46097e607701 src/lib/Utils.luan
--- a/src/lib/Utils.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/lib/Utils.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -1,5 +1,6 @@
 local Luan = require "luan:Luan.luan"
 local error = Luan.error
+local pairs = Luan.pairs or error()
 local Http = require "luan:http/Http.luan"
 
 
@@ -10,4 +11,12 @@
 	return request.scheme.."://"..request.headers["Host"]
 end
 
+function Utils.shallow_copy(t)
+	local rtn = {}
+	for key, val in pairs(t) do
+		rtn[key] = val
+	end
+	return rtn
+end
+
 return Utils
diff -r 2b7dcf355a78 -r 46097e607701 src/lib/ai/claude/Chat.luan
--- a/src/lib/ai/claude/Chat.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/lib/ai/claude/Chat.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -18,8 +18,22 @@
 
 local Chat = {}
 
+function Chat.output_system_prompt(thread)
+	if thread == nil then
+		return
+	end
+	thread = json_parse(thread)
+	local system_prompt = thread.system or error
+	system_prompt = html_encode(system_prompt)
+	%><%=system_prompt%><%
+end
+
 function Chat.output_messages_html(thread)
-	local messages = thread and json_parse(thread) or {nil}
+	if thread == nil then
+		return
+	end
+	thread = json_parse(thread)
+	local messages = thread.messages or error
 	for _, message in ipairs(messages) do
 		local role = message.role or error()
 		local who
@@ -31,9 +45,10 @@
 			error(role)
 		end
 		local function output(text)
+			text = html_encode(text)
 %>
 			<h3><%=who%></h3>
-			<div role="<%=role%>"><%=html_encode(text)%></div>
+			<div markdown role="<%=role%>"><%=text%></div>
 <%
 		end
 		local content = message.content or error()
@@ -50,8 +65,30 @@
 	end_for
 end
 
+
+local system_prompt = [[
+CRITICAL REQUIREMENT: When writing Japanese, use ruby markdown syntax {japanese|romaji} for pronunciation guidance.
+
+Apply ruby tags to meaningful pronunciation units:
+- Individual kanji or kanji compounds: {私|watashi}, {学生|gakusei}
+- Hiragana/katakana words and particles: {は|wa}, {です|desu}, {ありがとう|arigatō}
+- Grammatical elements: {ました|mashita}, {ません|masen}
+
+The romaji must reflect ACTUAL PRONUNCIATION, not character-by-character readings.
+Use macrons for long vowels: ā, ī, ū, ē, ō
+
+APPLIES TO ALL JAPANESE TEXT: Example sentences, grammar explanations, vocabulary lists, casual mentions - ANY Japanese characters in your response need ruby tags.
+
+VERIFICATION STEP: Before sending, scan your ENTIRE response for any Japanese characters (hiragana, katakana, kanji) and ensure they all have ruby tags.
+]]
+
+
 function Chat.ask(thread,input)
-	local messages = thread and json_parse(thread) or {nil}
+	thread = thread and json_parse(thread) or {
+		system = system_prompt
+		messages = {nil}
+	}
+	local messages = thread.messages or error
 	messages[#messages+1] = {
 		role = "user"
 		content = input
@@ -74,12 +111,10 @@
 	Thread.sleep(5000)
 	}
 	if true then
-		return json_string(messages)
+		return json_string(thread)
 	end
 --]=]
-	local resultJson = claude_chat{
-		messages = messages
-	}
+	local resultJson = claude_chat(thread)
 	local result = json_parse(resultJson)
 	-- logger.info(json_string(result))
 	result.type == "message" or error()
@@ -90,7 +125,7 @@
 		role = "assistant"
 		content = content
 	}
-	return json_string(messages)
+	return json_string(thread)
 end
 
 return Chat
diff -r 2b7dcf355a78 -r 46097e607701 src/lib/ai/claude/Claude.luan
--- a/src/lib/ai/claude/Claude.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/lib/ai/claude/Claude.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -5,6 +5,8 @@
 local Parsers = require "luan:Parsers.luan"
 local json_string = Parsers.json_string or error()
 local Config = require "site:/private/Config.luan"
+local Utils = require "site:/lib/Utils.luan"
+local shallow_copy = Utils.shallow_copy or error()
 local Logging = require "luan:logging/Logging.luan"
 local logger = Logging.logger "claude/Claude"
 
@@ -21,6 +23,7 @@
 local max_tokens = 8192
 
 function Claude.chat(content)
+	content = shallow_copy(content)
 	content.model = content.model or model
 	content.max_tokens = content.max_tokens or max_tokens
 	local options = {
diff -r 2b7dcf355a78 -r 46097e607701 src/private/tools/chat.html.luan
--- a/src/private/tools/chat.html.luan	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/private/tools/chat.html.luan	Mon Jul 21 15:16:47 2025 -0600
@@ -21,18 +21,14 @@
 	<head>
 <%		head() %>
 		<style>
-			@import "/chat.css?s=<%=started%>";
-
 <%	if not process_markdown then %>
-			[ai_container] [role] {
+			[markdown] {
 				white-space-collapse: preserve;
 			}
 <%	end %>
 		</style>
-		<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
-		<script src="/chat.js?s=<%=started%>"></script>
 	</head>
-	<body ai_container>
+	<body>
 <%		header() %>
 		<div content>
 			<h1>Chat <%=chat_id%></h1>
diff -r 2b7dcf355a78 -r 46097e607701 src/site.css
--- a/src/site.css	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/site.css	Mon Jul 21 15:16:47 2025 -0600
@@ -78,3 +78,12 @@
 	color: #ffffff;
 	background-color: #428bca;
 }
+
+ruby rt {
+	user-select: none;
+}
+
+pre,
+code {
+	text-wrap: wrap;
+}
diff -r 2b7dcf355a78 -r 46097e607701 src/site.js
--- a/src/site.js	Fri Jul 18 23:46:48 2025 -0600
+++ b/src/site.js	Mon Jul 21 15:16:47 2025 -0600
@@ -119,3 +119,15 @@
 	window.scrollTo( 0, lastY );
 }
 
+// requires markdown-it
+function handleMarkdown() {
+	let converter = window.markdownit({html: true});
+	let divs = document.querySelectorAll('[markdown]');
+	for( let div of divs ) {
+		let text = div.textContent;
+		text = text.replace(/\{([^|}]+)\|([^|}]+)\}/g, '<ruby>$1<rt>$2</rt></ruby>');
+		text = converter.render(text);
+		div.innerHTML = text;
+		div.removeAttribute('markdown');
+	}
+}