feat(traces): integrate traces app into monorepo with NestJS backend and AI city guides

Restructure standalone traces app into monorepo pattern with mobile + backend + shared types.
Add NestJS backend with Drizzle ORM schema for locations, cities, places, POIs, and AI guides.
Add mobile sync layer, cities tab, and guide generation UI. Fix pre-existing type errors across
mobile codebase, matrix-mana-bot (sendDirectMessage), llm-playground, and all web auth stores
(signUp call signature).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-15 08:12:42 +01:00
parent 402e135179
commit bd1178edf8
125 changed files with 14626 additions and 831 deletions

View file

@ -90,7 +90,7 @@ export const authStore = {
try {
const sourceAppUrl = browser ? window.location.origin : undefined;
const result = await authService.signUp(email, password, undefined, sourceAppUrl);
const result = await authService.signUp(email, password, sourceAppUrl);
if (!result.success) {
return { success: false, error: result.error || 'Signup failed', needsVerification: false };

View file

@ -191,13 +191,15 @@ export class MatrixService extends BaseMatrixService {
let userMessage: string;
switch (error.code) {
case 'STT_UNAVAILABLE':
userMessage = '🎤 Spracherkennung momentan nicht verfügbar. Bitte schreibe deine Nachricht.';
userMessage =
'🎤 Spracherkennung momentan nicht verfügbar. Bitte schreibe deine Nachricht.';
break;
case 'TTS_UNAVAILABLE':
userMessage = '🔊 Sprachausgabe momentan nicht verfügbar.';
break;
case 'TIMEOUT':
userMessage = '⏱️ Die Verarbeitung dauert zu lange. Bitte versuche eine kürzere Nachricht.';
userMessage =
'⏱️ Die Verarbeitung dauert zu lange. Bitte versuche eine kürzere Nachricht.';
break;
case 'INVALID_AUDIO':
userMessage = `🎤 ${error.message}`;
@ -302,6 +304,37 @@ export class MatrixService extends BaseMatrixService {
.replace(/\n/g, '<br>');
}
/**
* Send a direct message to a user by finding or creating a DM room
*/
async sendDirectMessage(userId: string, message: string): Promise<void> {
const roomId = await this.getOrCreateDmRoom(userId);
await this.sendMessage(roomId, message);
}
private async getOrCreateDmRoom(userId: string): Promise<string> {
// Check existing joined rooms for a DM with this user
const joinedRooms = await this.client.getJoinedRooms();
for (const roomId of joinedRooms) {
try {
const members = await this.client.getJoinedRoomMembers(roomId);
if (members.length === 2 && members.includes(userId)) {
return roomId;
}
} catch {
// Skip rooms we can't inspect
}
}
// Create a new DM room
const roomId = await this.client.createRoom({
invite: [userId],
is_direct: true,
preset: 'trusted_private_chat',
});
return roomId;
}
getClient() {
return this.client;
}