import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useEffect, useState } from 'react' import { IconX } from '@tabler/icons-react' import StyledButton from '~/components/StyledButton' import MarkdownEditor from '~/components/MarkdownEditor' import { useNotifications } from '~/context/NotificationContext' import api from '~/lib/api' interface NomadMdModalProps { aiAssistantName?: string onClose: () => void } // Seeded into the editor when no NOMAD.md exists yet. Nothing is written to disk // until the user saves, so this is purely a starting point they can replace. const NOMAD_MD_TEMPLATE = `# NOMAD.md ## About me - (e.g. I'm setting up an off-grid homestead in a cold climate.) ## How the assistant should respond - Be concise and practical. - Prioritize safety and proven methods. ` export default function NomadMdModal({ aiAssistantName, onClose }: NomadMdModalProps) { const queryClient = useQueryClient() const { addNotification } = useNotifications() const [content, setContent] = useState(null) const { data, isLoading } = useQuery({ queryKey: ['nomad-md'], queryFn: () => api.getNomadMd(), }) // Seed the editor once the file loads: existing content, or the template when empty. useEffect(() => { if (data && content === null) { setContent(data.content.trim().length > 0 ? data.content : NOMAD_MD_TEMPLATE) } }, [data, content]) const saveMutation = useMutation({ mutationFn: (value: string) => api.saveNomadMd(value), onSuccess: (result) => { if (!result?.success) { addNotification({ type: 'error', message: 'Failed to save NOMAD.md.' }) return } addNotification({ type: 'success', message: 'NOMAD.md saved. It applies to new messages.' }) queryClient.invalidateQueries({ queryKey: ['nomad-md'] }) onClose() }, onError: (error: any) => { addNotification({ type: 'error', message: error?.message || 'Failed to save NOMAD.md.' }) }, }) const assistantName = aiAssistantName?.trim() || 'your AI assistant' return (

NOMAD.md

Custom instructions passed to {assistantName} as a system prompt on every chat.

{isLoading || content === null ? (
Loading…
) : (
)}

Tip: this file is also stored on disk at{' '} storage/NOMAD.md and can be edited directly.

Cancel content !== null && saveMutation.mutate(content)} loading={saveMutation.isPending} disabled={content === null} > Save
) }