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:
@@ -46,10 +46,6 @@ class ApiClient {
|
||||
return response.json() as Promise<ApiResponse<T>>;
|
||||
}
|
||||
|
||||
async register(email: string, password: string) {
|
||||
return this.request<{ user: { id: string; email: string } }>('POST', '/auth/register', { email, password });
|
||||
}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
return this.request<{ token: string; userId: string }>('POST', '/auth/login', { email, password });
|
||||
}
|
||||
@@ -68,7 +64,7 @@ class ApiClient {
|
||||
}
|
||||
|
||||
async getDays() {
|
||||
return this.request<Array<{ date: string; eventCount: number; hasJournal: boolean }>>('GET', '/days');
|
||||
return this.request<Array<{ date: string; eventCount: number; hasJournal: boolean; journalTitle?: string; journalExcerpt?: string }>>('GET', '/days');
|
||||
}
|
||||
|
||||
async getDay(date: string) {
|
||||
@@ -79,8 +75,8 @@ class ApiClient {
|
||||
return this.request<{ deleted: boolean }>('DELETE', `/days/${date}`);
|
||||
}
|
||||
|
||||
async createEvent(date: string, type: string, content: string, metadata?: object) {
|
||||
return this.request<Event>('POST', '/events', { date, type, content, metadata });
|
||||
async createEvent(date: string, type: string, content: string, metadata?: object, location?: { latitude: number; longitude: number; placeName?: string }) {
|
||||
return this.request<Event>('POST', '/events', { date, type, content, metadata, ...location });
|
||||
}
|
||||
|
||||
async updateEvent(id: string, content: string, metadata?: object) {
|
||||
@@ -129,14 +125,18 @@ class ApiClient {
|
||||
return response.json() as Promise<ApiResponse<{ mediaPath: string }>>;
|
||||
}
|
||||
|
||||
async generateJournal(date: string) {
|
||||
return this.request<{ journal: Journal; task: Task }>('POST', `/journal/generate/${date}`);
|
||||
async generateJournal(date: string, instructions?: string) {
|
||||
return this.request<{ journal: Journal; task: Task }>('POST', `/journal/generate/${date}`, { instructions: instructions || '' });
|
||||
}
|
||||
|
||||
async getJournal(date: string) {
|
||||
return this.request<Journal>('GET', `/journal/${date}`);
|
||||
}
|
||||
|
||||
async getJournals(page: number = 1, limit: number = 10) {
|
||||
return this.request<{ journals: Journal[]; total: number; page: number; limit: number; totalPages: number }>('GET', `/journals?page=${page}&limit=${limit}`);
|
||||
}
|
||||
|
||||
async getJournalTasks(date: string) {
|
||||
return this.request<Task[]>('GET', `/journal/${date}/tasks`);
|
||||
}
|
||||
@@ -149,9 +149,37 @@ class ApiClient {
|
||||
return this.request<Settings>('GET', '/settings');
|
||||
}
|
||||
|
||||
async search(query: string) {
|
||||
return this.request<{ journals: Array<{ date: string; title: string; excerpt: string }>; events: Array<{ date: string; type: string; content: string }> }>('GET', `/search?q=${encodeURIComponent(query)}`);
|
||||
}
|
||||
|
||||
async updateSettings(settings: Partial<Settings>) {
|
||||
return this.request<Settings>('PUT', '/settings', settings);
|
||||
}
|
||||
|
||||
async exportData() {
|
||||
return this.request<ExportData>('GET', '/export');
|
||||
}
|
||||
|
||||
async importData(data: ExportData) {
|
||||
return this.request<ImportResult>('POST', '/import', data);
|
||||
}
|
||||
|
||||
async deleteAccount() {
|
||||
return this.request<{ deleted: boolean }>('DELETE', '/account');
|
||||
}
|
||||
|
||||
async resetAccount() {
|
||||
return this.request<{ reset: boolean }>('POST', '/account/reset');
|
||||
}
|
||||
|
||||
async changePassword(currentPassword: string, newPassword: string) {
|
||||
return this.request<{ changed: boolean }>('POST', '/account/password', { currentPassword, newPassword });
|
||||
}
|
||||
|
||||
async register(email: string, password: string) {
|
||||
return this.request<{ apiKey: string; userId: string }>('POST', '/auth/register', { email, password });
|
||||
}
|
||||
}
|
||||
|
||||
export interface Event {
|
||||
@@ -161,12 +189,16 @@ export interface Event {
|
||||
content: string;
|
||||
mediaPath?: string;
|
||||
metadata?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
placeName?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Journal {
|
||||
id: string;
|
||||
date: string;
|
||||
title?: string;
|
||||
content: string;
|
||||
eventCount: number;
|
||||
generatedAt: string;
|
||||
@@ -183,6 +215,7 @@ export interface Task {
|
||||
request?: string;
|
||||
response?: string;
|
||||
error?: string;
|
||||
title?: string | null;
|
||||
createdAt: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
@@ -203,4 +236,69 @@ export interface Settings {
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ExportData {
|
||||
version: string;
|
||||
exportedAt: string;
|
||||
settings: {
|
||||
aiProvider: string;
|
||||
aiApiKey?: string;
|
||||
aiModel?: string;
|
||||
aiBaseUrl?: string;
|
||||
journalPrompt?: string;
|
||||
language?: string;
|
||||
timezone?: string;
|
||||
providerSettings?: string;
|
||||
journalContextDays?: number;
|
||||
};
|
||||
events: Array<{
|
||||
id: string;
|
||||
date: string;
|
||||
type: string;
|
||||
content: string;
|
||||
mediaPath?: string;
|
||||
metadata?: string;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
placeName?: string;
|
||||
createdAt: string;
|
||||
}>;
|
||||
journals: Array<{
|
||||
id: string;
|
||||
date: string;
|
||||
title?: string;
|
||||
content: string;
|
||||
eventCount: number;
|
||||
generatedAt: string;
|
||||
}>;
|
||||
tasks: Array<{
|
||||
id: string;
|
||||
journalId: string;
|
||||
date: string;
|
||||
type: string;
|
||||
status: string;
|
||||
provider: string;
|
||||
model?: string;
|
||||
prompt?: string;
|
||||
request?: string;
|
||||
response?: string;
|
||||
error?: string;
|
||||
title?: string;
|
||||
createdAt: string;
|
||||
completedAt?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ImportResult {
|
||||
compatible: boolean;
|
||||
importedEvents: number;
|
||||
importedJournals: number;
|
||||
importedTasks: number;
|
||||
skippedEvents: number;
|
||||
skippedJournals: number;
|
||||
totalEvents: number;
|
||||
totalJournals: number;
|
||||
totalTasks: number;
|
||||
warning?: string;
|
||||
}
|
||||
|
||||
export const api = new ApiClient();
|
||||
|
||||
Reference in New Issue
Block a user