feat(robin): M1 System Presence — journald/kmsg/inotify watcher, pattern classifier, tray badge, chat panel #2

Open
pyr0ball wants to merge 21 commits from feat/m1-system-presence into main
3 changed files with 41 additions and 1 deletions
Showing only changes of commit 88924aa593 - Show all commits

11
package-lock.json generated
View file

@ -8,6 +8,7 @@
"name": "robin",
"version": "0.0.0",
"dependencies": {
"@tauri-apps/api": "^2.11.0",
"vue": "^3.5.34"
},
"devDependencies": {
@ -398,6 +399,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@tauri-apps/api": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz",
"integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==",
"license": "Apache-2.0 OR MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",

View file

@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
"@tauri-apps/api": "^2.11.0",
"vue": "^3.5.34"
},
"devDependencies": {

View file

@ -33,13 +33,41 @@
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue'
import { ref, nextTick, onMounted, onUnmounted } from 'vue'
import { invoke } from '@tauri-apps/api/core'
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
interface Message { role: 'user' | 'robin'; content: string }
interface RobinEvent { pattern_id: string; title: string; body: string; severity: string; timestamp: number }
const messages = ref<Message[]>([])
const input = ref('')
const messagesEl = ref<HTMLElement | null>(null)
let unlisten: UnlistenFn | null = null
onMounted(async () => {
// Drain any events that fired while the panel was closed
const pending = await invoke<RobinEvent[]>('get_pending_events')
for (const e of pending) {
pushRobinEvent(e)
}
// Listen for live events while panel is open
unlisten = await listen<RobinEvent>('robin:event', ({ payload }) => {
pushRobinEvent(payload)
})
})
onUnmounted(() => {
unlisten?.()
})
function pushRobinEvent(e: RobinEvent) {
messages.value.push({ role: 'robin', content: `**${e.title}**\n\n${e.body}` })
nextTick(() => {
messagesEl.value?.scrollTo({ top: messagesEl.value.scrollHeight, behavior: 'smooth' })
})
}
async function send() {
const text = input.value.trim()