chore: remove CLAUDE.md and stale config files

This commit is contained in:
Dorian
2026-04-12 12:11:00 -04:00
parent 29ff413559
commit 18284e1592
13 changed files with 198 additions and 249 deletions

View File

@@ -398,7 +398,7 @@ class RPCClient {
})
}
async getReceivedMessages(): Promise<{ messages: Array<{ from_pubkey: string; message: string; timestamp: string }> }> {
async getReceivedMessages(): Promise<{ messages: Array<{ from_pubkey: string; from_name?: string; message: string; timestamp: string; direction?: string }> }> {
return this.call({
method: 'node-messages-received',
params: {},

View File

@@ -276,6 +276,31 @@ export const useMeshStore = defineStore('mesh', () => {
return result
}
async function sendChannelMessage(channel: number, message: string) {
const doSend = async () => {
try {
sending.value = true
error.value = null
const res = await rpcClient.call<{ sent: boolean; message_id: number; channel: number }>({
method: 'mesh.send-channel',
params: { channel, message: message.trim() },
})
if (res.sent) {
await fetchMessages()
}
return res
} catch (err: unknown) {
error.value = err instanceof Error ? err.message : 'Failed to send channel message'
throw err
} finally {
sending.value = false
}
}
const result = sendQueue.then(doSend, doSend)
sendQueue = result.then(() => {}, () => {})
return result
}
async function broadcastIdentity() {
try {
error.value = null
@@ -456,6 +481,7 @@ export const useMeshStore = defineStore('mesh', () => {
fetchPeers,
fetchMessages,
sendMessage,
sendChannelMessage,
broadcastIdentity,
configure,
refreshAll,

View File

@@ -37,18 +37,34 @@ let pollInterval: ReturnType<typeof setInterval> | null = null
// The Public channel (always available on Meshcore)
const publicChannel = { index: 0, name: 'Public' }
// Channel contact_id convention: matches backend u32::MAX - channel_index
function channelContactId(channelIndex: number): number {
return 4294967295 - channelIndex // u32::MAX - index
}
// Archipelago Channel — Tor-based messaging to all federated/peered nodes
const archChannelActive = ref(false)
const archMessages = ref<Array<{ from_pubkey: string; message: string; timestamp: string }>>([])
const archMessages = ref<Array<{ from_pubkey: string; from_name?: string; message: string; timestamp: string; direction?: string }>>([])
const archUnread = ref(0)
let archPollInterval: ReturnType<typeof setInterval> | null = null
// Federation node name cache: pubkey -> node name
const fedNodeNames = ref<Record<string, string>>({})
function openArchChannel() {
async function openArchChannel() {
activeChatPeer.value = null
activeChatChannel.value = null
archChannelActive.value = true
archUnread.value = 0
mobileShowChat.value = true
// Load federation node names for resolving pubkeys to names
try {
const res = await rpcClient.federationListNodes()
const names: Record<string, string> = {}
for (const node of res.nodes) {
if (node.pubkey) names[node.pubkey] = node.name || node.did.slice(0, 12) + '...'
}
fedNodeNames.value = names
} catch { /* non-fatal */ }
loadArchMessages()
if (!archPollInterval) {
archPollInterval = setInterval(loadArchMessages, 15000)
@@ -58,7 +74,18 @@ function openArchChannel() {
async function loadArchMessages() {
try {
const res = await rpcClient.getReceivedMessages()
archMessages.value = res.messages || []
const newMessages = res.messages || []
// Track unread: count new received messages since last load
if (archMessages.value.length > 0 && !archChannelActive.value) {
const newReceived = newMessages.filter(
m => m.direction !== 'sent' && m.from_pubkey !== 'me'
&& !archMessages.value.some(existing =>
existing.from_pubkey === m.from_pubkey && existing.timestamp === m.timestamp
)
)
archUnread.value += newReceived.length
}
archMessages.value = newMessages
} catch { /* silent */ }
}
@@ -142,6 +169,11 @@ async function handleToggleOffGrid() {
onMounted(async () => {
window.addEventListener('resize', handleResize)
await Promise.all([mesh.refreshAll(), transport.fetchStatus()])
// Start background polling for Archipelago (Tor) messages so unread count works
loadArchMessages()
if (!archPollInterval) {
archPollInterval = setInterval(loadArchMessages, 15000)
}
pollInterval = setInterval(() => {
mesh.fetchStatus()
mesh.fetchPeers()
@@ -154,6 +186,7 @@ onMounted(async () => {
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
if (pollInterval) clearInterval(pollInterval)
if (archPollInterval) { clearInterval(archPollInterval); archPollInterval = null }
})
// Active chat name for the header
@@ -177,11 +210,21 @@ const hasActiveChat = computed(() => !!activeChatPeer.value || !!activeChatChann
const chatMessages = computed(() => {
if (archChannelActive.value) {
return archMessages.value.map((m, i) => {
const isSent = (m as Record<string, unknown>).direction === 'sent' || m.from_pubkey === 'me'
const isSent = m.direction === 'sent' || m.from_pubkey === 'me'
let peerName = 'Unknown'
if (isSent) {
peerName = 'You'
} else if (m.from_name) {
peerName = m.from_name
} else if (fedNodeNames.value[m.from_pubkey]) {
peerName = fedNodeNames.value[m.from_pubkey]
} else {
peerName = m.from_pubkey.slice(0, 12) + '...'
}
return {
id: i,
peer_contact_id: -99,
peer_name: isSent ? 'You' : (m.from_pubkey.slice(0, 12) + '...'),
peer_name: peerName,
direction: (isSent ? 'sent' : 'received') as 'sent' | 'received',
plaintext: m.message,
timestamp: m.timestamp,
@@ -193,7 +236,7 @@ const chatMessages = computed(() => {
})
}
if (activeChatChannel.value) {
const chanId = -(activeChatChannel.value.index + 1)
const chanId = channelContactId(activeChatChannel.value.index)
return mesh.messages.filter(m => m.peer_contact_id === chanId)
}
if (activeChatPeer.value) {
@@ -236,6 +279,7 @@ function openChannelChat(channel: { index: number; name: string }) {
messageText.value = ''
activeTab.value = 'chat'
mobileShowChat.value = true
mesh.markChatRead(channelContactId(channel.index))
nextTick(() => scrollChatToBottom())
}
@@ -254,12 +298,18 @@ async function handleSendMessage() {
nextTick(() => scrollChatToBottom())
return
}
if (!activeChatPeer.value || !messageText.value.trim()) return
if (!messageText.value.trim()) return
sendError.value = ''
try {
await mesh.sendMessage(activeChatPeer.value.contact_id, messageText.value)
messageText.value = ''
nextTick(() => scrollChatToBottom())
if (activeChatChannel.value) {
await mesh.sendChannelMessage(activeChatChannel.value.index, messageText.value)
messageText.value = ''
nextTick(() => scrollChatToBottom())
} else if (activeChatPeer.value) {
await mesh.sendMessage(activeChatPeer.value.contact_id, messageText.value)
messageText.value = ''
nextTick(() => scrollChatToBottom())
}
} catch (err: unknown) {
sendError.value = err instanceof Error ? err.message : 'Send failed'
}
@@ -467,6 +517,7 @@ function truncatePubkey(hex: string | null): string {
<div class="mesh-peer-name">Public</div>
<div class="mesh-peer-sub">Mesh radio</div>
</div>
<span v-if="mesh.unreadCounts[channelContactId(0)]" class="ml-auto text-[10px] px-1.5 py-0.5 rounded-full bg-orange-500/30 text-orange-300">{{ mesh.unreadCounts[channelContactId(0)] }}</span>
</div>
<div
v-for="peer in sortedPeers" :key="peer.contact_id"

View File

@@ -162,7 +162,7 @@ export function resolveAppUrl(id: string, routeQueryPath?: string): string {
if (httpsProxy) return `${window.location.origin}${httpsProxy}`
}
// HTTP: direct port access (iframes break with proxy paths due to root-relative assets)
// HTTP: direct port access
const port = APP_PORTS[id]
if (!port) return ''
let base = `http://${window.location.hostname}:${port}`