fix config

This commit is contained in:
2026-01-20 20:03:36 +09:00
parent e671723806
commit b6476a386b
7 changed files with 77 additions and 42 deletions

View File

@@ -2,9 +2,15 @@
"title": "syui.ai", "title": "syui.ai",
"did": "did:plc:vzsvtbtbnwn22xjqhcu3vd6y", "did": "did:plc:vzsvtbtbnwn22xjqhcu3vd6y",
"handle": "syui.syui.ai", "handle": "syui.syui.ai",
"bot": {
"did": "did:plc:6qyecktefllvenje24fcxnie",
"handle": "ai.syui.ai"
},
"collection": "ai.syui.log.post", "collection": "ai.syui.log.post",
"chatCollection": "ai.syui.log.chat",
"network": "syu.is", "network": "syu.is",
"color": "#EF454A", "color": "#EF454A",
"siteUrl": "https://syui.ai", "siteUrl": "https://syui.ai",
"repoUrl": "https://git.syui.ai/ai/log",
"oauth": true "oauth": true
} }

View File

@@ -2,11 +2,13 @@
"bsky.social": { "bsky.social": {
"plc": "https://plc.directory", "plc": "https://plc.directory",
"bsky": "https://public.api.bsky.app", "bsky": "https://public.api.bsky.app",
"web": "https://bsky.app" "web": "https://bsky.app",
"handleDomains": ["bsky.social"]
}, },
"syu.is": { "syu.is": {
"plc": "https://plc.syu.is", "plc": "https://plc.syu.is",
"bsky": "https://bsky.syu.is", "bsky": "https://bsky.syu.is",
"web": "https://syu.is" "web": "https://syu.is",
"handleDomains": ["syu.is", "syui.ai"]
} }
} }

View File

@@ -223,14 +223,16 @@ fn handle_chat_save(params: ChatSaveParams) -> Result<String> {
}); });
// Get user DID from token.json // Get user DID from token.json
let user_did = token::load_session() let user_did = match token::load_session() {
.map(|s| s.did) Ok(s) => s.did,
.unwrap_or_else(|_| "did:plc:unknown".to_string()); Err(_) => return Err(anyhow::anyhow!("User not logged in. Run: ailog login <handle> -p <password>")),
};
// Get bot DID from bot.json // Get bot DID from bot.json
let bot_did = token::load_bot_session() let bot_did = match token::load_bot_session() {
.map(|s| s.did) Ok(s) => s.did,
.unwrap_or_else(|_| "did:plc:6qyecktefllvenje24fcxnie".to_string()); Err(_) => return Err(anyhow::anyhow!("Bot not logged in. Run: ailog login <handle> -p <password> --bot")),
};
// Reset session if new_thread requested // Reset session if new_thread requested
if params.new_thread { if params.new_thread {
@@ -278,9 +280,10 @@ fn handle_chat_list() -> Result<String> {
.to_string() .to_string()
}); });
let user_did = token::load_session() let user_did = match token::load_session() {
.map(|s| s.did) Ok(s) => s.did,
.unwrap_or_else(|_| "did:plc:unknown".to_string()); Err(_) => return Err(anyhow::anyhow!("User not logged in. Run: ailog login <handle> -p <password>")),
};
let collection_dir = std::path::Path::new(&output_dir) let collection_dir = std::path::Path::new(&output_dir)
.join(&user_did) .join(&user_did)

View File

@@ -171,10 +171,11 @@ export function renderChatThread(
botHandle: string, botHandle: string,
userProfile?: Profile | null, userProfile?: Profile | null,
botProfile?: Profile | null, botProfile?: Profile | null,
pds?: string pds?: string,
chatCollection: string = 'ai.syui.log.chat'
): string { ): string {
// Find root message // Find root message
const rootUri = `at://${userDid}/ai.syui.log.chat/${rootRkey}` const rootUri = `at://${userDid}/${chatCollection}/${rootRkey}`
const rootMsg = messages.find(m => m.uri === rootUri) const rootMsg = messages.find(m => m.uri === rootUri)
if (!rootMsg) { if (!rootMsg) {
@@ -222,7 +223,7 @@ export function renderChatThread(
const displayContent = getTranslatedContent(msg) const displayContent = getTranslatedContent(msg)
const content = renderMarkdown(displayContent) const content = renderMarkdown(displayContent)
const recordLink = `/@${author.handle}/at/collection/ai.syui.log.chat/${rkey}` const recordLink = `/@${author.handle}/at/collection/${chatCollection}/${rkey}`
return ` return `
<article class="chat-message"> <article class="chat-message">
@@ -268,8 +269,9 @@ export function renderChatThreadPage(
botHandle: string, botHandle: string,
userProfile?: Profile | null, userProfile?: Profile | null,
botProfile?: Profile | null, botProfile?: Profile | null,
pds?: string pds?: string,
chatCollection: string = 'ai.syui.log.chat'
): string { ): string {
const thread = renderChatThread(messages, rootRkey, userDid, userHandle, botDid, botHandle, userProfile, botProfile, pds) const thread = renderChatThread(messages, rootRkey, userDid, userHandle, botDid, botHandle, userProfile, botProfile, pds, chatCollection)
return `<div class="chat-container">${thread}</div>` return `<div class="chat-container">${thread}</div>`
} }

View File

@@ -1,11 +1,12 @@
export function renderFooter(handle: string): string { export function renderFooter(handle: string, repoUrl?: string): string {
// Extract username from handle: {username}.{name}.{domain} -> username // Extract username from handle: {username}.{name}.{domain} -> username
const username = handle.split('.')[0] || handle const username = handle.split('.')[0] || handle
const repo = repoUrl || '#'
return ` return `
<footer id="footer" class="footer"> <footer id="footer" class="footer">
<div class="license"> <div class="license">
<a href="https://git.syui.ai/ai/log" target="_blank" rel="noopener"> <a href="${repo}" target="_blank" rel="noopener">
<img src="/ai.svg" alt="ai" class="license-icon"> <img src="/ai.svg" alt="ai" class="license-icon">
</a> </a>
</div> </div>

View File

@@ -34,21 +34,21 @@ function filterCollectionsByService(collections: string[], service: string): str
async function getWebUrl(handle: string): Promise<string | undefined> { async function getWebUrl(handle: string): Promise<string | undefined> {
const networks = await getNetworks() const networks = await getNetworks()
// Check each network for matching handle domain // Check each network for matching handle domain
for (const [domain, network] of Object.entries(networks)) { for (const [_domain, network] of Object.entries(networks)) {
// Direct domain match (e.g., handle.syu.is -> syu.is) // Check handleDomains if configured
if (handle.endsWith(`.${domain}`)) { if (network.handleDomains) {
return network.web for (const hd of network.handleDomains) {
if (handle.endsWith(`.${hd}`)) {
return network.web
}
}
} }
// Check if handle domain matches network's web domain (e.g., syui.syui.ai -> syu.is via web: syu.is) // Check if handle domain matches network's web domain
const webDomain = network.web?.replace(/^https?:\/\//, '') const webDomain = network.web?.replace(/^https?:\/\//, '')
if (webDomain && handle.endsWith(`.${webDomain}`)) { if (webDomain && handle.endsWith(`.${webDomain}`)) {
return network.web return network.web
} }
} }
// Check for syui.ai handles -> syu.is network
if (handle.endsWith('.syui.ai')) {
return networks['syu.is']?.web
}
// Default to first network's web // Default to first network's web
const firstNetwork = Object.values(networks)[0] const firstNetwork = Object.values(networks)[0]
return firstNetwork?.web return firstNetwork?.web
@@ -127,7 +127,7 @@ async function render(route: Route): Promise<void> {
app.innerHTML = ` app.innerHTML = `
${renderHeader(handle, oauthEnabled)} ${renderHeader(handle, oauthEnabled)}
<div class="error">Could not resolve handle: ${handle}</div> <div class="error">Could not resolve handle: ${handle}</div>
${renderFooter(handle)} ${renderFooter(handle, config.repoUrl)}
` `
setupEventHandlers() setupEventHandlers()
return return
@@ -230,13 +230,18 @@ async function render(route: Route): Promise<void> {
} else if (route.type === 'chat') { } else if (route.type === 'chat') {
// Chat list page - show threads started by this user // Chat list page - show threads started by this user
const aiDid = 'did:plc:6qyecktefllvenje24fcxnie' // ai.syui.ai if (!config.bot) {
const aiHandle = 'ai.syui.ai' html += `<div id="content" class="error">Bot not configured in config.json</div>`
html += `<nav class="back-nav"><a href="/@${handle}">${handle}</a></nav>`
} else {
const botDid = config.bot.did
const botHandle = config.bot.handle
const chatCollection = config.chatCollection || 'ai.syui.log.chat'
// Load messages and profiles in parallel // Load messages and profiles in parallel
const [chatMessages, aiProfile, pds] = await Promise.all([ const [chatMessages, botProfile, pds] = await Promise.all([
getChatMessages(did, aiDid, 'ai.syui.log.chat'), getChatMessages(did, botDid, chatCollection),
getProfile(aiDid, false), getProfile(botDid, false),
getPds(did) getPds(did)
]) ])
@@ -254,18 +259,24 @@ async function render(route: Route): Promise<void> {
langList = Array.from(chatLangs) langList = Array.from(chatLangs)
html += renderLangSelector(langList) html += renderLangSelector(langList)
html += `<div id="content">${renderChatListPage(chatMessages, did, handle, aiDid, aiHandle, profile, aiProfile, pds || undefined)}</div>` html += `<div id="content">${renderChatListPage(chatMessages, did, handle, botDid, botHandle, profile, botProfile, pds || undefined)}</div>`
html += `<nav class="back-nav"><a href="/@${handle}">${handle}</a></nav>` html += `<nav class="back-nav"><a href="/@${handle}">${handle}</a></nav>`
}
} else if (route.type === 'chat-thread' && route.rkey) { } else if (route.type === 'chat-thread' && route.rkey) {
// Chat thread page - show full conversation // Chat thread page - show full conversation
const aiDid = 'did:plc:6qyecktefllvenje24fcxnie' // ai.syui.ai if (!config.bot) {
const aiHandle = 'ai.syui.ai' html += `<div id="content" class="error">Bot not configured in config.json</div>`
html += `<nav class="back-nav"><a href="/@${handle}">${handle}</a></nav>`
} else {
const botDid = config.bot.did
const botHandle = config.bot.handle
const chatCollection = config.chatCollection || 'ai.syui.log.chat'
// Load messages and profiles in parallel // Load messages and profiles in parallel
const [chatMessages, aiProfile, pds] = await Promise.all([ const [chatMessages, botProfile, pds] = await Promise.all([
getChatMessages(did, aiDid, 'ai.syui.log.chat'), getChatMessages(did, botDid, chatCollection),
getProfile(aiDid, false), getProfile(botDid, false),
getPds(did) getPds(did)
]) ])
@@ -283,8 +294,9 @@ async function render(route: Route): Promise<void> {
langList = Array.from(chatLangs) langList = Array.from(chatLangs)
html += renderLangSelector(langList) html += renderLangSelector(langList)
html += `<div id="content">${renderChatThreadPage(chatMessages, route.rkey, did, handle, aiDid, aiHandle, profile, aiProfile, pds || undefined)}</div>` html += `<div id="content">${renderChatThreadPage(chatMessages, route.rkey, did, handle, botDid, botHandle, profile, botProfile, pds || undefined, chatCollection)}</div>`
html += `<nav class="back-nav"><a href="/@${handle}/at/chat">chat</a></nav>` html += `<nav class="back-nav"><a href="/@${handle}/at/chat">chat</a></nav>`
}
} else { } else {
// User page: compact collection buttons + posts // User page: compact collection buttons + posts
@@ -298,7 +310,7 @@ async function render(route: Route): Promise<void> {
html += `<div id="content">${renderPostList(posts, handle)}</div>` html += `<div id="content">${renderPostList(posts, handle)}</div>`
} }
html += renderFooter(handle) html += renderFooter(handle, config.repoUrl)
app.innerHTML = html app.innerHTML = html
hideLoading(app) hideLoading(app)
@@ -349,7 +361,7 @@ async function render(route: Route): Promise<void> {
app.innerHTML = ` app.innerHTML = `
${renderHeader(currentHandle, false)} ${renderHeader(currentHandle, false)}
<div class="error">Error: ${error}</div> <div class="error">Error: ${error}</div>
${renderFooter(currentHandle)} ${renderFooter(currentHandle, undefined)}
` `
hideLoading(app) hideLoading(app)
setupEventHandlers() setupEventHandlers()

View File

@@ -1,12 +1,20 @@
// Config types // Config types
export interface BotConfig {
did: string
handle: string
}
export interface AppConfig { export interface AppConfig {
title: string title: string
did?: string did?: string
handle: string handle: string
bot?: BotConfig
collection: string collection: string
chatCollection?: string
network: string network: string
color: string color: string
siteUrl: string siteUrl: string
repoUrl?: string
oauth?: boolean oauth?: boolean
} }
@@ -15,6 +23,7 @@ export interface Networks {
plc: string plc: string
bsky: string bsky: string
web: string web: string
handleDomains?: string[]
} }
} }