feat: v0.1.0 - geolocation capture, calendar, search, Starlight docs site

- Automatic browser geolocation capture on event creation
- Reverse geocoding via Nominatim API for place names
- Full-text search with SQLite FTS5
- Calendar view for browsing past entries
- DateNavigator component for day navigation
- SearchModal with Ctrl+K shortcut
- QuickAddWidget with Ctrl+J shortcut
- Starlight documentation site with GitHub Pages deployment
- Multiple AI provider support (Groq, OpenAI, Anthropic, Ollama, LM Studio)
- Multi-user registration support

BREAKING: Events now include latitude/longitude/placeName fields
This commit is contained in:
lotherk
2026-03-27 02:27:55 +00:00
parent deaf496a7d
commit 0bdd71a4ed
67 changed files with 15201 additions and 355 deletions

View File

@@ -12,7 +12,7 @@ export class GroqProvider implements AIProvider {
this.baseUrl = config.baseUrl || 'https://api.groq.com/openai/v1';
}
async generate(prompt: string, systemPrompt?: string): Promise<AIProviderResult> {
async generate(prompt: string, systemPrompt?: string, options?: { jsonMode?: boolean }): Promise<AIProviderResult> {
const messages: Array<{ role: string; content: string }> = [];
if (systemPrompt) {
@@ -21,13 +21,17 @@ export class GroqProvider implements AIProvider {
messages.push({ role: 'user', content: prompt });
const requestBody = {
const requestBody: Record<string, unknown> = {
model: this.model,
messages,
temperature: 0.7,
max_tokens: 2000,
};
if (options?.jsonMode) {
requestBody.response_format = { type: 'json_object' };
}
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
@@ -44,10 +48,22 @@ export class GroqProvider implements AIProvider {
}
const responseData = JSON.parse(responseText);
const content = responseData.choices?.[0]?.message?.content || '';
let content = responseData.choices?.[0]?.message?.content || '';
let title: string | undefined;
if (options?.jsonMode) {
try {
const parsed = JSON.parse(content);
title = parsed.title;
content = parsed.content || content;
} catch {
// If JSON parsing fails, use content as-is
}
}
return {
content,
title,
request: requestBody,
response: responseData,
};