chore(mana): memoro aus unified-App entfernen

Memoro lebt als eigenständiger Stack:
- `memoro.mana.how` (Astro landing :3120)
- `memoro-app.mana.how` (SvelteKit Web SPA)
- `memoro-api.mana.how` (Bun/Hono server)
- `memoro-audio.mana.how` (audio-server)

Quelle: `Code/memoro/`, deployt nach `~/projects/memoro-deploy/` auf
Mac Mini. Phasenstand: Phase 2.5 (Cutover-Endphase), 2.5a/b durch.

Gelöscht / abgebaut:
- Module: apps/mana/.../modules/memoro + Routen + Locales
- Top-Level: apps/memoro/ (Server, Audio-Server, Landing, Mobile)
- docker-compose.macmini.yml memoro-server + memoro-audio-server
  Service-Blocks (alter Container im managarten-Compose, der echte
  Memoro läuft aus ~/projects/memoro-deploy/)
- .github/workflows/cd-macmini.yml memoro-server + memoro-audio-server
  Build-Jobs + Service-Liste + Health-Check-Mapping
- prometheus memoro-server scrape job + mana.how/memoro probe
- shared-branding APP_BRANDING.memoro + APP_ICONS.memoro + MANA_APPS
  Memoro-Entry + MemoroLogo + onboarding-template
- shared-utils analytics: memoro tracker + MemoroEvents-Block
- website-blocks EmbedSourceSchema 'memoro.memos' + Inspector-Option
- Cross-Module: website/embeds.ts resolveMemos + LocalMemo-Import,
  crypto-registry memos+memories + LocalMemo-Import, plaintext-
  allowlist memoSpaces+memoTags+memoroSpaces+spaceMembers,
  exposed-records memoro-Eintrag
- Registries: app-registry/apps.ts (Memoro registerApp + Microphone
  icon + Header), categories, help-content, module-registry,
  splitscreen, hooks.server, data/tools/init
- (app)/+layout.svelte startMemoroLlmWatcher/stopMemoroLlmWatcher
  imports + Calls
- package.json memoro:dev / dev:memoro:* Scripts
- cloudflared: irreführender Comment-Block in unified-app-Sektion
  bereinigt; Standalone-Memoro-Routes (memoro.mana.how,
  memoro-app.mana.how, memoro-api.mana.how, memoro-audio.mana.how)
  bleiben — sie zeigen auf den live Standalone-Stack
- i18n locales apps/{de,en,es,fr,it}.json memoro raus

Dexie v64 Migration:
- droppt memos, memories, memoTags, memoroSpaces, spaceMembers,
  memoSpaces

shared-theme/store.svelte.ts APP_THEME_CONFIGS.memoro bleibt erhalten —
das Package wird auch vom externen Memoro-Web-Repo konsumiert.

Test/Doku:
- module-registry.test.ts: memoro-Eintrag aus SYNC_APP_MAP + memoroSpaces
  aus TABLE_TO_SYNC_NAME entfernt

mana-web svelte-check 0/0, snapshot test 10/10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-18 14:50:47 +02:00
parent 0490ec490e
commit fc616688e3
1333 changed files with 25 additions and 260313 deletions

View file

@ -47,8 +47,6 @@ on:
- mana-web
- mana-api
- manavoxel-web
- memoro-server
- memoro-audio-server
concurrency:
group: cd-macmini
@ -144,8 +142,6 @@ jobs:
"mana-web|apps/mana/apps/web/ apps/mana/packages/"
"mana-api|apps/api/"
"manavoxel-web|apps/manavoxel/apps/web/ apps/manavoxel/packages/"
"memoro-server|apps/memoro/apps/server/ apps/memoro/packages/"
"memoro-audio-server|apps/memoro/apps/audio-server/"
)
echo "Changed files (first 40):"
@ -439,7 +435,6 @@ jobs:
mana-api) echo "http://localhost:3060/health" ;;
mana-web) echo "http://localhost:5000/health" ;;
manavoxel-web) echo "http://localhost:5028/health" ;;
memoro-server) echo "http://localhost:3015/health" ;;
*) echo "" ;;
esac
}

View file

@ -35,10 +35,6 @@ const PUBLIC_SYNC_SERVER_URL_CLIENT =
process.env.PUBLIC_SYNC_SERVER_URL_CLIENT || process.env.PUBLIC_SYNC_SERVER_URL || '';
const PUBLIC_ULOAD_SERVER_URL_CLIENT =
process.env.PUBLIC_ULOAD_SERVER_URL_CLIENT || process.env.PUBLIC_ULOAD_SERVER_URL || '';
// memoro-server is intentionally not injected — the unified web app's memoro
// module is fully local-first (recorder + Dexie + sync) and never calls the
// standalone server. The memoro-server compose service still exists for the
// mobile app, but mana.how does not depend on it.
const PUBLIC_MANA_MEDIA_URL_CLIENT =
process.env.PUBLIC_MANA_MEDIA_URL_CLIENT || process.env.PUBLIC_MANA_MEDIA_URL || '';
const PUBLIC_MANA_LLM_URL_CLIENT =
@ -158,7 +154,6 @@ const APP_SUBDOMAINS = new Set([
'inventory',
'times',
'uload',
'memoro',
'questions',
]);

View file

@ -28,7 +28,6 @@ import {
HardDrives,
Presentation,
Package,
Microphone,
NumberCircleOne,
Binoculars,
ArrowsInCardinal,
@ -103,7 +102,7 @@ import {
// dreams · firsts · lasts · habits · recipes
// Places & ev.: places · events
// Creative: picture · music · photos
// Tools: memoro · uload · calc · inventory ·
// Tools: uload · calc · inventory ·
// storage · skilltree · questions
// Long-tail: quotes · automations · companion · wetter ·
// goals · website · spaces · augur ·
@ -659,17 +658,6 @@ registerApp({
},
});
registerApp({
id: 'memoro',
name: 'Memoro',
color: '#F59E0B',
icon: Microphone,
views: {
list: { load: () => import('$lib/modules/memoro/ListView.svelte') },
detail: { load: () => import('$lib/modules/memoro/views/DetailView.svelte') },
},
});
registerApp({
id: 'questions',
name: 'Questions',

View file

@ -293,21 +293,6 @@ export const MODULE_HELP: Record<string, ModuleHelp> = {
'Standorte können verschachtelt sein (Zuhause → Büro → Schreibtisch)',
],
},
memoro: {
description:
'Sprachnotizen aufnehmen und automatisch transkribieren lassen. Durchsuchbar, zusammengefasst und verschlüsselt.',
features: [
'Aufnahme mit einem Tap',
'AI-Transkription (Whisper — läuft lokal im Browser)',
'Automatische Zusammenfassung & Intro',
'Durchsuchbare Memos im Volltext',
'Verschlüsselter Transkript-Text',
],
tips: [
'Die Transkription läuft direkt im Browser — deine Stimme verlässt nie dein Gerät',
'Nutze Memos als schnelle Gedankennotizen unterwegs',
],
},
questions: {
description:
'Fragen sammeln und Antworten festhalten — ein persönliches Q&A-Archiv für Dinge die du herausfinden möchtest.',

View file

@ -66,9 +66,6 @@ export const PLAINTEXT_ALLOWLIST: readonly string[] = [
'manaLinks', // TODO: audit
'markers', // TODO: audit
'mealTags', // TODO: audit
'memoSpaces', // TODO: audit
'memoTags', // TODO: audit
'memoroSpaces', // TODO: audit
'moodTags', // TODO: audit
'moods', // TODO: audit
'mukkeProjects', // TODO: audit
@ -96,7 +93,6 @@ export const PLAINTEXT_ALLOWLIST: readonly string[] = [
'skillTags', // TODO: audit
'skills', // TODO: audit
'songTags', // TODO: audit
'spaceMembers', // TODO: audit
'storageFolders', // TODO: audit
'taskLabels', // TODO: audit
'timeAlarms', // TODO: audit

View file

@ -77,7 +77,6 @@ import type { LocalMessage, LocalConversation, LocalTemplate } from '../../modul
import type { LocalNote } from '../../modules/notes/types';
import type { LocalDream, LocalDreamSymbol } from '../../modules/dreams/types';
import type { LocalJournalEntry } from '../../modules/journal/types';
import type { LocalMemo } from '../../modules/memoro/types';
import type {
LocalInvoice,
LocalInvoiceClient,
@ -141,13 +140,6 @@ export const ENCRYPTION_REGISTRY: Record<string, EncryptionConfig> = {
// is encrypted.
dreamSymbols: entry<LocalDreamSymbol>(['meaning']),
// ─── Memoro ──────────────────────────────────────────────
// Voice transcripts are typically the largest plaintext blobs in the
// whole app — encrypting them yields the biggest disk-footprint win
// of any single field.
memos: entry<LocalMemo>(['title', 'intro', 'transcript']),
memories: { enabled: true, fields: ['title', 'content'] },
// ─── Contacts ────────────────────────────────────────────
contacts: {
enabled: true,

View file

@ -1559,6 +1559,22 @@ db.version(63).stores({
whoMessages: null,
});
// v64 — Memoro module retirement (2026-05-18).
// Memoro lebt als eigenständiger Stack auf memoro-app.mana.how /
// memoro-api.mana.how / memoro-audio.mana.how aus Code/memoro/ →
// ~/projects/memoro-deploy/. Die unified-App-Surface (Modul +
// memoro-server-Container im managarten-Compose) wird hier gedroppt.
// dropped: memos, memories, memoTags, memoroSpaces, spaceMembers,
// memoSpaces.
db.version(64).stores({
memos: null,
memories: null,
memoTags: null,
memoroSpaces: null,
spaceMembers: null,
memoSpaces: null,
});
// ─── Sync Routing ──────────────────────────────────────────
// SYNC_APP_MAP, TABLE_TO_SYNC_NAME, TABLE_TO_APP, SYNC_NAME_TO_TABLE,
// toSyncName() and fromSyncName() are now derived from per-module

View file

@ -222,7 +222,6 @@ describe('module-registry — snapshot', () => {
questions: ['qCollections', 'questions', 'answers', 'questionTags'],
uload: ['links', 'uloadTags', 'uloadFolders', 'linkTags'],
calc: ['calculations', 'savedFormulas'],
memoro: ['memos', 'memories', 'memoTags', 'memoroSpaces', 'spaceMembers', 'memoSpaces'],
guides: ['guides', 'sections', 'steps', 'guideCollections', 'runs', 'guideTags'],
habits: ['habits', 'habitLogs'],
notes: ['notes', 'noteTags'],
@ -310,7 +309,6 @@ describe('module-registry — snapshot', () => {
qCollections: 'collections',
uloadTags: 'tags',
uloadFolders: 'folders',
memoroSpaces: 'spaces',
guideCollections: 'collections',
socialEvents: 'events',
financeCategories: 'categories',

View file

@ -67,7 +67,6 @@ import { timesModuleConfig } from '$lib/modules/times/module.config';
import { questionsModuleConfig } from '$lib/modules/questions/module.config';
import { uloadModuleConfig } from '$lib/modules/uload/module.config';
import { calcModuleConfig } from '$lib/modules/calc/module.config';
import { memoroModuleConfig } from '$lib/modules/memoro/module.config';
import { guidesModuleConfig } from '$lib/modules/guides/module.config';
import { habitsModuleConfig } from '$lib/modules/habits/module.config';
import { notesModuleConfig } from '$lib/modules/notes/module.config';
@ -124,7 +123,6 @@ export const MODULE_CONFIGS: readonly ModuleConfig[] = [
questionsModuleConfig,
uloadModuleConfig,
calcModuleConfig,
memoroModuleConfig,
guidesModuleConfig,
habitsModuleConfig,
notesModuleConfig,

View file

@ -187,18 +187,6 @@ const TABLES: TableConfig[] = [
return eventsStore.setVisibility(id, next);
},
},
{
module: 'memoro',
collection: 'memos',
moduleLabel: 'Memoro',
encrypted: true,
title: (r) => asString(r.title ?? r.intro),
href: (id) => `/memoro/${id}`,
setVisibility: async (id, next) => {
const { memosStore } = await import('$lib/modules/memoro/stores/memos.svelte');
return memosStore.setVisibility(id, next);
},
},
{
module: 'presi',
collection: 'presiDecks',

View file

@ -20,7 +20,6 @@ import { socialEventsTools } from '$lib/modules/events/tools';
import { musicTools } from '$lib/modules/music/tools';
import { storageTools } from '$lib/modules/storage/tools';
import { chatTools } from '$lib/modules/chat/tools';
import { memoroTools } from '$lib/modules/memoro/tools';
import { skilltreeTools } from '$lib/modules/skilltree/tools';
import { periodTools } from '$lib/modules/period/tools';
import { firstsTools } from '$lib/modules/firsts/tools';
@ -69,7 +68,6 @@ export function initTools(): void {
registerTools(musicTools);
registerTools(storageTools);
registerTools(chatTools);
registerTools(memoroTools);
registerTools(skilltreeTools);
registerTools(periodTools);
registerTools(firstsTools);

View file

@ -17,7 +17,6 @@
"storage": "Speicher",
"presi": "Presi",
"inventory": "Inventar",
"memoro": "Memoro",
"questions": "Recherche",
"skilltree": "Skills",
"uload": "uLoad",

View file

@ -17,7 +17,6 @@
"storage": "Storage",
"presi": "Presi",
"inventory": "Inventory",
"memoro": "Memoro",
"questions": "Research",
"skilltree": "Skills",
"uload": "uLoad",

View file

@ -17,7 +17,6 @@
"storage": "Almacén",
"presi": "Presi",
"inventory": "Inventario",
"memoro": "Memoro",
"questions": "Investigación",
"skilltree": "Skills",
"uload": "uLoad",

View file

@ -17,7 +17,6 @@
"storage": "Stockage",
"presi": "Presi",
"inventory": "Inventaire",
"memoro": "Memoro",
"questions": "Recherche",
"skilltree": "Skills",
"uload": "uLoad",

View file

@ -17,7 +17,6 @@
"storage": "Archivio",
"presi": "Presi",
"inventory": "Inventario",
"memoro": "Memoro",
"questions": "Ricerca",
"skilltree": "Skills",
"uload": "uLoad",

View file

@ -1,336 +0,0 @@
{
"common": {
"save": "Speichern",
"cancel": "Abbrechen",
"delete": "Löschen",
"edit": "Bearbeiten",
"share": "Teilen",
"back": "Zurück",
"next": "Weiter",
"done": "Fertig",
"loading": "Wird geladen...",
"search": "Suchen",
"settings": "Einstellungen",
"yes": "Ja",
"no": "Nein",
"ok": "OK",
"error": "Fehler",
"success": "Erfolg",
"create": "Erstellen",
"confirm": "Bestätigen",
"close": "Schließen",
"or": "ODER"
},
"nav": {
"dashboard": "Dashboard",
"tags": "Tags",
"spaces": "Spaces",
"mana": "Mana",
"blueprints": "Vorlagen",
"statistics": "Statistiken",
"settings": "Einstellungen",
"logout": "Abmelden",
"expand": "Erweitern",
"minimize": "Minimieren",
"shortcuts": "Tastenkombinationen"
},
"auth": {
"welcome": "Willkommen bei Memoro",
"get_started": "Los geht's",
"create_account": "Neues Konto erstellen",
"sign_in": "Anmelden",
"sign_in_with_email": "Mit E-Mail anmelden",
"sign_up_with_email": "Mit E-Mail registrieren",
"email": "E-Mail",
"password": "Passwort",
"confirm_password": "Passwort bestätigen",
"forgot_password": "Passwort vergessen?",
"reset_password": "Passwort zurücksetzen",
"logging_in": "Anmelden...",
"creating_account": "Konto wird erstellt...",
"sending": "Senden...",
"error_email_required": "Bitte gib deine E-Mail-Adresse ein",
"error_password_required": "Bitte gib dein Passwort ein",
"error_confirm_password": "Bitte bestätige dein Passwort",
"error_passwords_not_match": "Passwörter stimmen nicht überein",
"error_password_too_short": "Das Passwort muss mindestens 8 Zeichen lang sein",
"error_password_requirements": "Das Passwort muss mindestens einen Kleinbuchstaben, einen Großbuchstaben, eine Ziffer und ein Sonderzeichen enthalten",
"error_registration_failed": "Registrierung fehlgeschlagen",
"password_requirement": "Passwort muss mindestens 8 Zeichen lang sein und mindestens einen Kleinbuchstaben, einen Großbuchstaben, eine Ziffer und ein Sonderzeichen enthalten.",
"registration_success": "Registrierung erfolgreich! Überprüfe deine E-Mail, um dein Konto zu bestätigen.",
"check_email_confirmation": "Bitte überprüfe deine E-Mail, um dein Konto zu bestätigen.",
"reset_email_sent": "Gib deine E-Mail-Adresse ein und wir senden dir einen Link zum Zurücksetzen deines Passworts.",
"email_only_title": "Warum nur E-Mail-Authentifizierung?",
"email_only_info": "Wir unterstützen nur E-Mail-Registrierung, um deine Unabhängigkeit und Privatsphäre zu gewährleisten.",
"email_only_learn_more": "Mehr erfahren",
"email_only_intro": "Wir glauben daran, dir die volle Kontrolle über dein Konto und deine Daten zu geben. Mit E-Mail-basierter Authentifizierung stellen wir sicher:",
"email_only_benefit_1_title": "Kein Vendor Lock-in",
"email_only_benefit_1_desc": "Du bist nicht von Drittanbietern wie Google oder Apple abhängig. Dein Konto funktioniert unabhängig.",
"email_only_benefit_2_title": "Verbesserte Privatsphäre",
"email_only_benefit_2_desc": "Wir teilen deine Daten nicht mit Google, Apple oder anderen Drittanbietern zur Authentifizierung.",
"email_only_benefit_3_title": "Konto-Portabilität",
"email_only_benefit_3_desc": "Deine E-Mail-Adresse ist portabel und funktioniert auf allen Geräten und Plattformen.",
"email_only_benefit_4_title": "Direkte Kommunikation",
"email_only_benefit_4_desc": "Wir können dich direkt über wichtige Updates erreichen, ohne auf Drittanbieter-Benachrichtigungssysteme angewiesen zu sein.",
"email_only_modal_footer": "Wir sind verpflichtet, Tools zu entwickeln, die deine Freiheit und Privatsphäre respektieren. E-Mail-Authentifizierung ist Teil dieser Verpflichtung.",
"got_it": "Verstanden",
"already_have_account": "Hast du bereits ein Konto?",
"dont_have_account": "Noch kein Konto?",
"terms_agreement": "Mit der Nutzung von Memoro stimmst du unseren <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">AGB</a> und der <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Datenschutzerklärung</a> zu.",
"mana_login": "Mana Login",
"mana_login_description": "Ein Login für alle Mana Apps",
"mana_login_benefit_0": "Mana Subscriptions über alle Apps hinweg nutzen - nur einmal zahlen und alles nutzen",
"back": "Zurück",
"reset_password_description": "Gib deine E-Mail-Adresse ein und wir senden dir einen Link zum Zurücksetzen deines Passworts.",
"reset_password_error": "Passwort zurücksetzen fehlgeschlagen",
"reset_password_success": "E-Mail wurde versendet!",
"reset_password_rate_limit": "Zu viele Versuche. Bitte warte einige Minuten.",
"reset_email_sent_description": "Wir haben eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts an {email} gesendet. Bitte überprüfe deinen Posteingang und Spam-Ordner.",
"back_to_login": "Zurück zum Login",
"resend_email": "E-Mail erneut senden",
"reset_email_sent_title": "E-Mail wurde versendet!",
"terms_agreement_conjunction": "und der",
"terms_agreement_suffix": "zu.",
"oauth_error_access_denied": "Zugriff verweigert. Die Anmeldung wurde abgebrochen.",
"oauth_error_server_error": "Server-Fehler bei der Authentifizierung. Bitte versuche es erneut.",
"oauth_error_temporarily_unavailable": "Der Authentifizierungsdienst ist vorübergehend nicht verfügbar. Bitte versuche es später erneut.",
"oauth_error_invalid_request": "Ungültige Anfrage. Bitte versuche es erneut.",
"oauth_error_unauthorized_client": "Nicht autorisierter Client. Bitte kontaktiere den Support.",
"oauth_error_unsupported_response_type": "Nicht unterstützter Antworttyp. Bitte kontaktiere den Support.",
"oauth_error_invalid_scope": "Ungültiger Berechtigungsbereich. Bitte kontaktiere den Support.",
"oauth_error_unknown": "Ein unbekannter Fehler ist aufgetreten. Bitte versuche es erneut.",
"password_requirements_title": "Passwort-Anforderungen:",
"password_requirement_length": "Mindestens 8 Zeichen",
"password_requirement_lowercase": "Einen Kleinbuchstaben",
"password_requirement_uppercase": "Einen Großbuchstaben",
"password_requirement_digit": "Eine Ziffer",
"password_requirement_special": "Ein Sonderzeichen"
},
"dashboard": {
"title": "Dashboard",
"recent_memos": "Neueste Memos",
"no_memos": "Keine Memos gefunden",
"create_memo": "Memo erstellen",
"search_placeholder": "Memos durchsuchen..."
},
"memo": {
"title": "Memo",
"unnamed": "Unbenanntes Memo",
"word_count": "{{count}} Wort",
"word_count_plural": "{{count}} Wörter",
"delete_confirmation": "Möchtest du dieses Memo wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
"delete_permanently": "Endgültig löschen",
"deleting": "Wird gelöscht...",
"pin": "Anheften",
"unpin": "Loslösen",
"share": "Teilen",
"edit": "Bearbeiten",
"translate": "Übersetzen",
"create_memory": "Memory erstellen",
"ask_question": "Frage stellen",
"copy_transcript": "Transkript kopieren",
"replace_word": "Wort ersetzen",
"reprocess": "Erneut verarbeiten",
"label_speakers": "Sprecher benennen",
"add_photos": "Fotos hinzufügen",
"manage_spaces": "Spaces verwalten",
"tags": "Tags",
"add_tag": "Tag hinzufügen",
"options": "Optionen",
"search": "Suchen",
"copy": "Kopieren",
"speakers": "Sprecher",
"find_replace": "Suchen & Ersetzen",
"shortcuts": "Tastenkürzel",
"no_memo_selected": "Kein Memo ausgewählt",
"select_memo_hint": "Wähle ein Memo aus der Liste oder erstelle eine neue Aufnahme",
"processing_transcript": "Transkription wird verarbeitet...",
"ask_question_placeholder": "Stelle eine Frage zu diesem Memo...",
"open_in_new_tab": "In neuem Tab öffnen",
"edit_title": "Titel bearbeiten",
"export_text": "Als Text exportieren",
"enter_new_title": "Neuen Titel eingeben:",
"no_title": "Ohne Titel",
"no_transcript": "Kein Transkript verfügbar",
"link_copied": "Link in Zwischenablage kopiert!",
"transcript_copied": "Transkript in Zwischenablage kopiert!",
"no_search_results": "Keine Suchergebnisse",
"no_memos_with_search": "Es wurden keine Memos gefunden, die \"{query}\" enthalten.",
"clear_search": "Suche löschen",
"no_memos_with_tag": "Keine Memos mit diesem Tag",
"no_memos_with_tag_hint": "Es gibt noch keine Memos mit diesem Tag.",
"show_all_memos": "Alle Memos anzeigen",
"no_memos_yet": "Noch keine Memos",
"no_memos_hint": "Gehe zur Aufnahme-Seite, um dein erstes Memo aufzunehmen",
"load_more": "Mehr Memos laden",
"search_placeholder": "Memos durchsuchen...",
"delete_memo_title": "Memo löschen",
"delete_memo_confirm": "Möchtest du \"{title}\" wirklich löschen?",
"delete_memo_warning": "Diese Aktion kann nicht rückgängig gemacht werden. Das Memo und alle zugehörigen Daten werden dauerhaft gelöscht.",
"error_user_not_authenticated": "Benutzer nicht authentifiziert",
"error_loading_memos": "Memos konnten nicht geladen werden",
"error_deleting_memo": "Fehler beim Löschen des Memos. Bitte versuche es erneut.",
"error_updating_title": "Fehler beim Aktualisieren des Titels. Bitte versuche es erneut.",
"error_copying_link": "Fehler beim Kopieren des Links. Bitte versuche es erneut.",
"error_pin_status": "Fehler beim Ändern des Pin-Status. Bitte versuche es erneut.",
"error_saving": "Fehler beim Speichern. Bitte versuche es erneut.",
"error_copying_transcript": "Fehler beim Kopieren des Transkripts. Bitte versuche es erneut.",
"error_updating_tags": "Fehler beim Aktualisieren der Tags. Bitte versuche es erneut.",
"error_creating_tag": "Fehler beim Erstellen des Tags. Bitte versuche es erneut.",
"error_asking_question": "Fehler bei der Verarbeitung der Frage. Bitte versuche es erneut.",
"retry": "Erneut versuchen",
"export_title": "Titel",
"export_date": "Datum",
"export_duration": "Dauer",
"export_transcript": "Transkript",
"export_no_transcript": "Kein Transkript verfügbar",
"export_ai_analysis": "KI-Analyse"
},
"tags": {
"title": "Tags",
"create_tag": "Tag erstellen",
"search_placeholder": "Tags durchsuchen...",
"no_tags": "Keine Tags gefunden",
"delete_confirmation": "Möchtest du den Tag \"{{name}}\" wirklich löschen?",
"tag_name": "Tag-Name",
"tag_color": "Tag-Farbe"
},
"spaces": {
"title": "Spaces",
"create_space": "Space erstellen",
"no_spaces": "Keine Spaces gefunden",
"members": "Mitglieder",
"invite": "Einladen"
},
"blueprints": {
"title": "Vorlagen",
"loading": "Lade Vorlagen...",
"no_blueprints": "Keine Vorlagen gefunden",
"search_placeholder": "Vorlagen durchsuchen...",
"all_categories": "Alle",
"standard": "Standard",
"manage": "Blueprints verwalten",
"activate": "Blueprint aktivieren",
"load_error": "Blueprints konnten nicht geladen werden.",
"previous_tip": "Vorheriger Tipp",
"next_tip": "Nächster Tipp",
"go_to_tip": "Gehe zu Tipp {index}"
},
"record": {
"title": "Aufnehmen - Memoro",
"instruction": "Halte gedrückt zum Aufnehmen",
"uploading": "Wird hochgeladen...",
"user_not_authenticated": "Benutzer nicht authentifiziert",
"upload_failed": "Upload fehlgeschlagen",
"network_error": "Netzwerkfehler: Bitte überprüfe deine Verbindung und versuche es erneut.",
"upload_error": "Fehler beim Hochladen der Aufnahme: {error}",
"unexpected_error": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut.",
"cancel_title": "Aufnahme löschen",
"cancel_message": "Möchtest du die aktuelle Aufnahme wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
"cancel_confirm": "Löschen",
"cancel_abort": "Nicht löschen",
"pause": "Pause",
"resume": "Fortsetzen",
"cancel": "Aufnahme abbrechen"
},
"statistics": {
"title": "Statistiken",
"today": "Heute",
"last_30_days": "Letzte 30 Tage",
"total": "Gesamt",
"memos": "Memos",
"words": "Wörter",
"recording_duration": "Aufnahmedauer",
"loading": "Lade Statistiken..."
},
"subscription": {
"title": "Mana kaufen",
"current_plan": "Aktueller Tarif",
"your_mana": "Dein Mana",
"buy": "Kaufen",
"popular": "Beliebt",
"legacy_plan": "Alter Tarif",
"monthly": "Monatlich",
"yearly": "Jährlich"
},
"settings": {
"title": "Einstellungen",
"appearance": "Darstellung",
"theme": "Theme",
"system": "System",
"light": "Hell",
"dark": "Dunkel",
"language": "Sprache",
"user_interface": "Benutzeroberfläche",
"show_language_button": "Sprachen-Button anzeigen",
"show_recording_instruction": "Aufnahme-Anleitung anzeigen",
"show_blueprints": "Blueprints anzeigen",
"show_mana_badge": "Mana-Anzeige im Header",
"data_privacy": "Daten & Privatsphäre",
"save_location": "Standort speichern",
"enable_analytics": "Analytics aktivieren",
"support": "Support",
"contact_support": "Support kontaktieren",
"rate_app": "App bewerten",
"account": "Konto",
"email_label": "E-Mail-Adresse",
"sign_out": "Abmelden",
"delete_account": "Konto löschen",
"app_info": "App-Informationen",
"version": "Version",
"platform": "Plattform",
"build": "Build",
"browser": "Browser",
"copyright": "© 2025 Memoro GmbH",
"made_with_love": "Made with ❤️ in Germany"
},
"theme": {
"toggle": "Theme wechseln",
"light_mode": "Heller Modus",
"dark_mode": "Dunkler Modus",
"switch_to_light": "Zum hellen Modus wechseln",
"switch_to_dark": "Zum dunklen Modus wechseln"
},
"errors": {
"unexpected": "Ein unerwarteter Fehler ist aufgetreten.",
"network": "Netzwerkfehler. Bitte überprüfe deine Internetverbindung.",
"not_found": "Nicht gefunden.",
"unauthorized": "Nicht autorisiert.",
"forbidden": "Zugriff verweigert.",
"server_error": "Serverfehler. Bitte versuche es später erneut."
},
"detail_view": {
"title_sources": {
"none": "Lokal (regelbasiert)",
"browser": "Auf deinem Gerät (Gemma 4 E2B)",
"mana_server": "Mana-Server (Gemma 4 E4B)",
"byok": "Dein API-Key",
"cloud": "Google Gemini"
},
"statuses": {
"pending": "Ausstehend",
"processing": "Wird verarbeitet",
"completed": "Fertig",
"failed": "Fehlgeschlagen"
},
"not_found": "Memo nicht gefunden",
"confirm_delete": "Memo wirklich löschen?",
"toast_deleted": "Memo gelöscht",
"placeholder_title_idle": "Titel…",
"placeholder_title_generating": "Titel wird generiert…",
"label_status": "Status",
"label_duration": "Dauer",
"label_language": "Sprache",
"placeholder_language": "z.B. de",
"label_visibility": "Sichtbarkeit",
"section_summary": "Zusammenfassung",
"placeholder_summary": "Zusammenfassung hinzufügen...",
"section_transcript": "Transkript",
"transcribing": "Wird transkribiert…",
"transcript_failed": "Transkription fehlgeschlagen. Versuche es erneut oder gib das Transkript manuell ein.",
"transcript_empty": "Kein Transkript vorhanden.",
"transcript_source": "Voxtral via mana-stt",
"meta_created": "Erstellt: {date}",
"meta_updated": "Bearbeitet: {date}"
}
}

View file

@ -1,336 +0,0 @@
{
"common": {
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"share": "Share",
"back": "Back",
"next": "Next",
"done": "Done",
"loading": "Loading...",
"search": "Search",
"settings": "Settings",
"yes": "Yes",
"no": "No",
"ok": "OK",
"error": "Error",
"success": "Success",
"create": "Create",
"confirm": "Confirm",
"close": "Close",
"or": "OR"
},
"nav": {
"dashboard": "Dashboard",
"tags": "Tags",
"spaces": "Spaces",
"mana": "Mana",
"blueprints": "Blueprints",
"statistics": "Statistics",
"settings": "Settings",
"logout": "Logout",
"expand": "Expand",
"minimize": "Minimize",
"shortcuts": "Shortcuts"
},
"auth": {
"welcome": "Welcome to Memoro",
"get_started": "Get Started",
"create_account": "Create Account",
"sign_in": "Sign In",
"sign_in_with_email": "Sign In with Email",
"sign_up_with_email": "Sign Up with Email",
"email": "Email",
"password": "Password",
"confirm_password": "Confirm Password",
"forgot_password": "Forgot Password?",
"reset_password": "Reset Password",
"logging_in": "Signing in...",
"creating_account": "Creating account...",
"sending": "Sending...",
"error_email_required": "Please enter your email address",
"error_password_required": "Please enter your password",
"error_confirm_password": "Please confirm your password",
"error_passwords_not_match": "Passwords do not match",
"error_password_too_short": "Password must be at least 8 characters long",
"error_password_requirements": "Password must contain at least one lowercase letter, one uppercase letter, one digit, and one special character",
"error_registration_failed": "Registration failed",
"password_requirement": "Password must be at least 8 characters long and contain at least one lowercase letter, one uppercase letter, one digit, and one special character.",
"registration_success": "Registration successful! Check your email to verify your account.",
"check_email_confirmation": "Please check your email to confirm your account.",
"reset_email_sent": "Enter your email address and we'll send you a link to reset your password.",
"email_only_title": "Why Email-Only Authentication?",
"email_only_info": "We only support email registration to ensure your independence and privacy.",
"email_only_learn_more": "Learn more",
"email_only_intro": "We believe in giving you full control over your account and data. By using email-based authentication, we ensure:",
"email_only_benefit_1_title": "No Vendor Lock-In",
"email_only_benefit_1_desc": "You're not dependent on third-party services like Google or Apple. Your account works independently.",
"email_only_benefit_2_title": "Enhanced Privacy",
"email_only_benefit_2_desc": "We don't share your data with Google, Apple, or any other third party for authentication.",
"email_only_benefit_3_title": "Account Portability",
"email_only_benefit_3_desc": "Your email address is portable and works across all devices and platforms.",
"email_only_benefit_4_title": "Direct Communication",
"email_only_benefit_4_desc": "We can reach you directly for important updates without relying on third-party notification systems.",
"email_only_modal_footer": "We're committed to building tools that respect your freedom and privacy. Email authentication is part of that commitment.",
"got_it": "Got it",
"already_have_account": "Already have an account?",
"dont_have_account": "Don't have an account?",
"terms_agreement": "By using Memoro you agree to our <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Terms</a> and <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Privacy Policy</a>.",
"mana_login": "Mana Login",
"mana_login_description": "One login for all Mana apps",
"mana_login_benefit_0": "Use Mana subscriptions across all apps - pay once and use everything",
"back": "Back",
"reset_password_description": "Enter your email address and we'll send you a link to reset your password.",
"reset_password_error": "Password reset failed",
"reset_password_success": "Email sent!",
"reset_password_rate_limit": "Too many attempts. Please wait a few minutes.",
"reset_email_sent_description": "We've sent an email with instructions to reset your password to {email}. Please check your inbox and spam folder.",
"back_to_login": "Back to login",
"resend_email": "Resend email",
"reset_email_sent_title": "Email sent!",
"terms_agreement_conjunction": "and the",
"terms_agreement_suffix": ".",
"oauth_error_access_denied": "Access denied. The login was cancelled.",
"oauth_error_server_error": "Server error during authentication. Please try again.",
"oauth_error_temporarily_unavailable": "The authentication service is temporarily unavailable. Please try again later.",
"oauth_error_invalid_request": "Invalid request. Please try again.",
"oauth_error_unauthorized_client": "Unauthorized client. Please contact support.",
"oauth_error_unsupported_response_type": "Unsupported response type. Please contact support.",
"oauth_error_invalid_scope": "Invalid scope. Please contact support.",
"oauth_error_unknown": "An unknown error occurred. Please try again.",
"password_requirements_title": "Password requirements:",
"password_requirement_length": "At least 8 characters",
"password_requirement_lowercase": "One lowercase letter",
"password_requirement_uppercase": "One uppercase letter",
"password_requirement_digit": "One digit",
"password_requirement_special": "One special character"
},
"dashboard": {
"title": "Dashboard",
"recent_memos": "Recent Memos",
"no_memos": "No memos found",
"create_memo": "Create Memo",
"search_placeholder": "Search memos..."
},
"memo": {
"title": "Memo",
"unnamed": "Unnamed Memo",
"word_count": "{{count}} word",
"word_count_plural": "{{count}} words",
"delete_confirmation": "Are you sure you want to delete this memo? This action cannot be undone.",
"delete_permanently": "Delete Permanently",
"deleting": "Deleting...",
"pin": "Pin",
"unpin": "Unpin",
"share": "Share",
"edit": "Edit",
"translate": "Translate",
"create_memory": "Create Memory",
"ask_question": "Ask Question",
"copy_transcript": "Copy Transcript",
"replace_word": "Replace Word",
"reprocess": "Reprocess",
"label_speakers": "Label Speakers",
"add_photos": "Add Photos",
"manage_spaces": "Manage Spaces",
"tags": "Tags",
"add_tag": "Add Tag",
"options": "Options",
"search": "Search",
"copy": "Copy",
"speakers": "Speakers",
"find_replace": "Find & Replace",
"shortcuts": "Shortcuts",
"no_memo_selected": "No memo selected",
"select_memo_hint": "Select a memo from the list or create a new recording",
"processing_transcript": "Transcription in progress...",
"ask_question_placeholder": "Ask a question about this memo...",
"open_in_new_tab": "Open in new tab",
"edit_title": "Edit title",
"export_text": "Export as text",
"enter_new_title": "Enter new title:",
"no_title": "Untitled",
"no_transcript": "No transcript available",
"link_copied": "Link copied to clipboard!",
"transcript_copied": "Transcript copied to clipboard!",
"no_search_results": "No search results",
"no_memos_with_search": "No memos found containing \"{query}\".",
"clear_search": "Clear search",
"no_memos_with_tag": "No memos with this tag",
"no_memos_with_tag_hint": "There are no memos with this tag yet.",
"show_all_memos": "Show all memos",
"no_memos_yet": "No memos yet",
"no_memos_hint": "Go to the recording page to create your first memo",
"load_more": "Load more memos",
"search_placeholder": "Search memos...",
"delete_memo_title": "Delete memo",
"delete_memo_confirm": "Are you sure you want to delete \"{title}\"?",
"delete_memo_warning": "This action cannot be undone. The memo and all associated data will be permanently deleted.",
"error_user_not_authenticated": "User not authenticated",
"error_loading_memos": "Could not load memos",
"error_deleting_memo": "Error deleting memo. Please try again.",
"error_updating_title": "Error updating title. Please try again.",
"error_copying_link": "Error copying link. Please try again.",
"error_pin_status": "Error updating pin status. Please try again.",
"error_saving": "Error saving changes. Please try again.",
"error_copying_transcript": "Error copying transcript. Please try again.",
"error_updating_tags": "Error updating tags. Please try again.",
"error_creating_tag": "Error creating tag. Please try again.",
"error_asking_question": "Error processing question. Please try again.",
"retry": "Try again",
"export_title": "Title",
"export_date": "Date",
"export_duration": "Duration",
"export_transcript": "Transcript",
"export_no_transcript": "No transcript available",
"export_ai_analysis": "AI Analysis"
},
"tags": {
"title": "Tags",
"create_tag": "Create Tag",
"search_placeholder": "Search tags...",
"no_tags": "No tags found",
"delete_confirmation": "Are you sure you want to delete the tag \"{{name}}\"?",
"tag_name": "Tag Name",
"tag_color": "Tag Color"
},
"spaces": {
"title": "Spaces",
"create_space": "Create Space",
"no_spaces": "No spaces found",
"members": "Members",
"invite": "Invite"
},
"blueprints": {
"title": "Blueprints",
"loading": "Loading blueprints...",
"no_blueprints": "No blueprints found",
"search_placeholder": "Search blueprints...",
"all_categories": "All",
"standard": "Standard",
"manage": "Manage blueprints",
"activate": "Activate blueprint",
"load_error": "Could not load blueprints.",
"previous_tip": "Previous tip",
"next_tip": "Next tip",
"go_to_tip": "Go to tip {index}"
},
"record": {
"title": "Record - Memoro",
"instruction": "Hold to record",
"uploading": "Uploading...",
"user_not_authenticated": "User not authenticated",
"upload_failed": "Upload failed",
"network_error": "Network error: Please check your connection and try again.",
"upload_error": "Error uploading recording: {error}",
"unexpected_error": "An unexpected error occurred. Please try again.",
"cancel_title": "Delete Recording",
"cancel_message": "Do you really want to delete the current recording? This action cannot be undone.",
"cancel_confirm": "Delete",
"cancel_abort": "Don't delete",
"pause": "Pause",
"resume": "Resume",
"cancel": "Cancel recording"
},
"statistics": {
"title": "Statistics",
"today": "Today",
"last_30_days": "Last 30 days",
"total": "Total",
"memos": "Memos",
"words": "Words",
"recording_duration": "Recording Duration",
"loading": "Loading statistics..."
},
"subscription": {
"title": "Buy Mana",
"current_plan": "Current Plan",
"your_mana": "Your Mana",
"buy": "Buy",
"popular": "Popular",
"legacy_plan": "Legacy Plan",
"monthly": "Monthly",
"yearly": "Yearly"
},
"settings": {
"title": "Settings",
"appearance": "Appearance",
"theme": "Theme",
"system": "System",
"light": "Light",
"dark": "Dark",
"language": "Language",
"user_interface": "User Interface",
"show_language_button": "Show Language Button",
"show_recording_instruction": "Show Recording Instruction",
"show_blueprints": "Show Blueprints",
"show_mana_badge": "Show Mana Badge in Header",
"data_privacy": "Data & Privacy",
"save_location": "Save Location",
"enable_analytics": "Enable Analytics",
"support": "Support",
"contact_support": "Contact Support",
"rate_app": "Rate App",
"account": "Account",
"email_label": "Email Address",
"sign_out": "Sign Out",
"delete_account": "Delete Account",
"app_info": "App Information",
"version": "Version",
"platform": "Platform",
"build": "Build",
"browser": "Browser",
"copyright": "© 2025 Memoro GmbH",
"made_with_love": "Made with ❤️ in Germany"
},
"theme": {
"toggle": "Toggle Theme",
"light_mode": "Light Mode",
"dark_mode": "Dark Mode",
"switch_to_light": "Switch to light mode",
"switch_to_dark": "Switch to dark mode"
},
"errors": {
"unexpected": "An unexpected error occurred.",
"network": "Network error. Please check your internet connection.",
"not_found": "Not found.",
"unauthorized": "Unauthorized.",
"forbidden": "Access denied.",
"server_error": "Server error. Please try again later."
},
"detail_view": {
"title_sources": {
"none": "Local (rule-based)",
"browser": "On your device (Gemma 4 E2B)",
"mana_server": "Mana server (Gemma 4 E4B)",
"byok": "Your API key",
"cloud": "Google Gemini"
},
"statuses": {
"pending": "Pending",
"processing": "Processing",
"completed": "Done",
"failed": "Failed"
},
"not_found": "Memo not found",
"confirm_delete": "Really delete memo?",
"toast_deleted": "Memo deleted",
"placeholder_title_idle": "Title…",
"placeholder_title_generating": "Generating title…",
"label_status": "Status",
"label_duration": "Duration",
"label_language": "Language",
"placeholder_language": "e.g. en",
"label_visibility": "Visibility",
"section_summary": "Summary",
"placeholder_summary": "Add summary…",
"section_transcript": "Transcript",
"transcribing": "Transcribing…",
"transcript_failed": "Transcription failed. Try again or enter the transcript manually.",
"transcript_empty": "No transcript available.",
"transcript_source": "Voxtral via mana-stt",
"meta_created": "Created: {date}",
"meta_updated": "Edited: {date}"
}
}

View file

@ -1,336 +0,0 @@
{
"common": {
"save": "Guardar",
"cancel": "Cancelar",
"delete": "Eliminar",
"edit": "Editar",
"share": "Compartir",
"back": "Volver",
"next": "Siguiente",
"done": "Hecho",
"loading": "Cargando...",
"search": "Buscar",
"settings": "Ajustes",
"yes": "Sí",
"no": "No",
"ok": "OK",
"error": "Error",
"success": "Éxito",
"create": "Crear",
"confirm": "Confirmar",
"close": "Cerrar",
"or": "O"
},
"nav": {
"dashboard": "Tablero",
"tags": "Etiquetas",
"spaces": "Espacios",
"mana": "Mana",
"blueprints": "Plantillas",
"statistics": "Estadísticas",
"settings": "Ajustes",
"logout": "Cerrar sesión",
"expand": "Expandir",
"minimize": "Minimizar",
"shortcuts": "Atajos"
},
"auth": {
"welcome": "Bienvenido a Memoro",
"get_started": "Comenzar",
"create_account": "Crear cuenta",
"sign_in": "Iniciar sesión",
"sign_in_with_email": "Iniciar sesión con correo",
"sign_up_with_email": "Registrarse con correo",
"email": "Correo electrónico",
"password": "Contraseña",
"confirm_password": "Confirmar contraseña",
"forgot_password": "¿Olvidó su contraseña?",
"reset_password": "Restablecer contraseña",
"logging_in": "Iniciando sesión...",
"creating_account": "Creando cuenta...",
"sending": "Enviando...",
"error_email_required": "Por favor, introduzca su dirección de correo electrónico",
"error_password_required": "Por favor, introduzca su contraseña",
"error_confirm_password": "Por favor, confirme su contraseña",
"error_passwords_not_match": "Las contraseñas no coinciden",
"error_password_too_short": "La contraseña debe tener al menos 8 caracteres",
"error_password_requirements": "La contraseña debe contener al menos una letra minúscula, una letra mayúscula, un dígito y un carácter especial",
"error_registration_failed": "Error en el registro",
"password_requirement": "La contraseña debe tener al menos 8 caracteres y contener al menos una letra minúscula, una letra mayúscula, un dígito y un carácter especial.",
"registration_success": "¡Registro exitoso! Revise su correo electrónico para confirmar su cuenta.",
"check_email_confirmation": "Por favor, revise su correo electrónico para confirmar su cuenta.",
"reset_email_sent": "Introduzca su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.",
"email_only_title": "¿Por qué solo autenticación por correo?",
"email_only_info": "Solo admitimos el registro por correo electrónico para garantizar su independencia y privacidad.",
"email_only_learn_more": "Más información",
"email_only_intro": "Creemos en darle control total sobre su cuenta y datos. Al usar autenticación por correo electrónico, garantizamos:",
"email_only_benefit_1_title": "Sin dependencia de proveedores",
"email_only_benefit_1_desc": "No depende de servicios de terceros como Google o Apple. Su cuenta funciona de forma independiente.",
"email_only_benefit_2_title": "Privacidad mejorada",
"email_only_benefit_2_desc": "No compartimos sus datos con Google, Apple ni ningún otro tercero para la autenticación.",
"email_only_benefit_3_title": "Portabilidad de la cuenta",
"email_only_benefit_3_desc": "Su dirección de correo electrónico es portátil y funciona en todos los dispositivos y plataformas.",
"email_only_benefit_4_title": "Comunicación directa",
"email_only_benefit_4_desc": "Podemos contactarle directamente para actualizaciones importantes sin depender de sistemas de notificación de terceros.",
"email_only_modal_footer": "Nos comprometemos a crear herramientas que respeten su libertad y privacidad. La autenticación por correo electrónico es parte de ese compromiso.",
"got_it": "Entendido",
"already_have_account": "¿Ya tiene una cuenta?",
"dont_have_account": "¿No tiene una cuenta?",
"terms_agreement": "Al usar Memoro, acepta nuestros <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Términos</a> y nuestra <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Política de privacidad</a>.",
"mana_login": "Mana Login",
"mana_login_description": "Un login para todas las apps de Mana",
"mana_login_benefit_0": "Usa suscripciones de Mana en todas las apps - paga una vez y úsalo todo",
"back": "Volver",
"reset_password_description": "Introduzca su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.",
"reset_password_error": "Error al restablecer la contraseña",
"reset_password_success": "¡Correo enviado!",
"reset_password_rate_limit": "Demasiados intentos. Por favor, espere unos minutos.",
"reset_email_sent_description": "Hemos enviado un correo con instrucciones para restablecer su contraseña a {email}. Por favor, revise su bandeja de entrada y carpeta de spam.",
"back_to_login": "Volver al login",
"resend_email": "Reenviar correo",
"reset_email_sent_title": "¡Correo enviado!",
"terms_agreement_conjunction": "y la",
"terms_agreement_suffix": ".",
"oauth_error_access_denied": "Acceso denegado. El inicio de sesión fue cancelado.",
"oauth_error_server_error": "Error del servidor durante la autenticación. Por favor, inténtelo de nuevo.",
"oauth_error_temporarily_unavailable": "El servicio de autenticación no está disponible temporalmente. Por favor, inténtelo más tarde.",
"oauth_error_invalid_request": "Solicitud inválida. Por favor, inténtelo de nuevo.",
"oauth_error_unauthorized_client": "Cliente no autorizado. Por favor, contacte con soporte.",
"oauth_error_unsupported_response_type": "Tipo de respuesta no soportado. Por favor, contacte con soporte.",
"oauth_error_invalid_scope": "Alcance inválido. Por favor, contacte con soporte.",
"oauth_error_unknown": "Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo.",
"password_requirements_title": "Requisitos de contraseña:",
"password_requirement_length": "Al menos 8 caracteres",
"password_requirement_lowercase": "Una letra minúscula",
"password_requirement_uppercase": "Una letra mayúscula",
"password_requirement_digit": "Un dígito",
"password_requirement_special": "Un carácter especial"
},
"dashboard": {
"title": "Tablero",
"recent_memos": "Memos recientes",
"no_memos": "No se encontraron memos",
"create_memo": "Crear memo",
"search_placeholder": "Buscar memos..."
},
"memo": {
"title": "Memo",
"unnamed": "Memo sin nombre",
"word_count": "{{count}} palabra",
"word_count_plural": "{{count}} palabras",
"delete_confirmation": "¿Realmente desea eliminar este memo? Esta acción no se puede deshacer.",
"delete_permanently": "Eliminar permanentemente",
"deleting": "Eliminando...",
"pin": "Fijar",
"unpin": "Desfijar",
"share": "Compartir",
"edit": "Editar",
"translate": "Traducir",
"create_memory": "Crear Memory",
"ask_question": "Hacer pregunta",
"copy_transcript": "Copiar transcripción",
"replace_word": "Reemplazar palabra",
"reprocess": "Reprocesar",
"label_speakers": "Etiquetar oradores",
"add_photos": "Añadir fotos",
"manage_spaces": "Gestionar espacios",
"tags": "Etiquetas",
"add_tag": "Añadir etiqueta",
"options": "Opciones",
"search": "Buscar",
"copy": "Copiar",
"speakers": "Oradores",
"find_replace": "Buscar y reemplazar",
"shortcuts": "Atajos",
"no_memo_selected": "Ningún memo seleccionado",
"select_memo_hint": "Seleccione un memo de la lista o cree una nueva grabación",
"processing_transcript": "Transcripción en proceso...",
"ask_question_placeholder": "Haga una pregunta sobre este memo...",
"open_in_new_tab": "Abrir en nueva pestaña",
"edit_title": "Editar título",
"export_text": "Exportar como texto",
"enter_new_title": "Introduzca nuevo título:",
"no_title": "Sin título",
"no_transcript": "No hay transcripción disponible",
"link_copied": "¡Enlace copiado al portapapeles!",
"transcript_copied": "¡Transcripción copiada al portapapeles!",
"no_search_results": "Sin resultados de búsqueda",
"no_memos_with_search": "No se encontraron memos que contengan \"{query}\".",
"clear_search": "Limpiar búsqueda",
"no_memos_with_tag": "No hay memos con esta etiqueta",
"no_memos_with_tag_hint": "Aún no hay memos con esta etiqueta.",
"show_all_memos": "Mostrar todos los memos",
"no_memos_yet": "Aún no hay memos",
"no_memos_hint": "Ve a la página de grabación para crear tu primer memo",
"load_more": "Cargar más memos",
"search_placeholder": "Buscar memos...",
"delete_memo_title": "Eliminar memo",
"delete_memo_confirm": "¿Realmente desea eliminar \"{title}\"?",
"delete_memo_warning": "Esta acción no se puede deshacer. El memo y todos los datos asociados se eliminarán permanentemente.",
"error_user_not_authenticated": "Usuario no autenticado",
"error_loading_memos": "No se pudieron cargar los memos",
"error_deleting_memo": "Error al eliminar el memo. Por favor, inténtelo de nuevo.",
"error_updating_title": "Error al actualizar el título. Por favor, inténtelo de nuevo.",
"error_copying_link": "Error al copiar el enlace. Por favor, inténtelo de nuevo.",
"error_pin_status": "Error al cambiar el estado de fijado. Por favor, inténtelo de nuevo.",
"error_saving": "Error al guardar. Por favor, inténtelo de nuevo.",
"error_copying_transcript": "Error al copiar la transcripción. Por favor, inténtelo de nuevo.",
"error_updating_tags": "Error al actualizar las etiquetas. Por favor, inténtelo de nuevo.",
"error_creating_tag": "Error al crear la etiqueta. Por favor, inténtelo de nuevo.",
"error_asking_question": "Error al procesar la pregunta. Por favor, inténtelo de nuevo.",
"retry": "Reintentar",
"export_title": "Título",
"export_date": "Fecha",
"export_duration": "Duración",
"export_transcript": "Transcripción",
"export_no_transcript": "No hay transcripción disponible",
"export_ai_analysis": "Análisis de IA"
},
"tags": {
"title": "Etiquetas",
"create_tag": "Crear etiqueta",
"search_placeholder": "Buscar etiquetas...",
"no_tags": "No se encontraron etiquetas",
"delete_confirmation": "¿Realmente desea eliminar la etiqueta \"{{name}}\"?",
"tag_name": "Nombre de etiqueta",
"tag_color": "Color de etiqueta"
},
"spaces": {
"title": "Espacios",
"create_space": "Crear espacio",
"no_spaces": "No se encontraron espacios",
"members": "Miembros",
"invite": "Invitar"
},
"blueprints": {
"title": "Plantillas",
"loading": "Cargando plantillas...",
"no_blueprints": "No se encontraron plantillas",
"search_placeholder": "Buscar plantillas...",
"all_categories": "Todas",
"standard": "Estándar",
"manage": "Gestionar plantillas",
"activate": "Activar plantilla",
"load_error": "No se pudieron cargar las plantillas.",
"previous_tip": "Consejo anterior",
"next_tip": "Siguiente consejo",
"go_to_tip": "Ir al consejo {index}"
},
"record": {
"title": "Grabar - Memoro",
"instruction": "Mantén presionado para grabar",
"uploading": "Subiendo...",
"user_not_authenticated": "Usuario no autenticado",
"upload_failed": "Error en la subida",
"network_error": "Error de red: Por favor, verifica tu conexión e inténtalo de nuevo.",
"upload_error": "Error al subir la grabación: {error}",
"unexpected_error": "Ha ocurrido un error inesperado. Por favor, inténtalo de nuevo.",
"cancel_title": "Eliminar grabación",
"cancel_message": "¿Realmente deseas eliminar la grabación actual? Esta acción no se puede deshacer.",
"cancel_confirm": "Eliminar",
"cancel_abort": "No eliminar",
"pause": "Pausar",
"resume": "Continuar",
"cancel": "Cancelar grabación"
},
"statistics": {
"title": "Estadísticas",
"today": "Hoy",
"last_30_days": "Últimos 30 días",
"total": "Total",
"memos": "Memos",
"words": "Palabras",
"recording_duration": "Duración de grabación",
"loading": "Cargando estadísticas..."
},
"subscription": {
"title": "Comprar Mana",
"current_plan": "Plan actual",
"your_mana": "Tu Mana",
"buy": "Comprar",
"popular": "Popular",
"legacy_plan": "Plan antiguo",
"monthly": "Mensual",
"yearly": "Anual"
},
"settings": {
"title": "Ajustes",
"appearance": "Apariencia",
"theme": "Tema",
"system": "Sistema",
"light": "Claro",
"dark": "Oscuro",
"language": "Idioma",
"user_interface": "Interfaz de usuario",
"show_language_button": "Mostrar botón de idioma",
"show_recording_instruction": "Mostrar instrucciones de grabación",
"show_blueprints": "Mostrar plantillas",
"show_mana_badge": "Mostrar insignia de Mana en el encabezado",
"data_privacy": "Datos y privacidad",
"save_location": "Guardar ubicación",
"enable_analytics": "Habilitar análisis",
"support": "Soporte",
"contact_support": "Contactar soporte",
"rate_app": "Calificar app",
"account": "Cuenta",
"email_label": "Dirección de correo",
"sign_out": "Cerrar sesión",
"delete_account": "Eliminar cuenta",
"app_info": "Información de la app",
"version": "Versión",
"platform": "Plataforma",
"build": "Build",
"browser": "Navegador",
"copyright": "© 2025 Memoro GmbH",
"made_with_love": "Made with ❤️ in Germany"
},
"theme": {
"toggle": "Cambiar tema",
"light_mode": "Modo claro",
"dark_mode": "Modo oscuro",
"switch_to_light": "Cambiar a modo claro",
"switch_to_dark": "Cambiar a modo oscuro"
},
"errors": {
"unexpected": "Ha ocurrido un error inesperado.",
"network": "Error de red. Verifique su conexión a Internet.",
"not_found": "No encontrado.",
"unauthorized": "No autorizado.",
"forbidden": "Acceso denegado.",
"server_error": "Error del servidor. Inténtelo de nuevo más tarde."
},
"detail_view": {
"title_sources": {
"none": "Local (reglas)",
"browser": "En tu dispositivo (Gemma 4 E2B)",
"mana_server": "Mana server (Gemma 4 E4B)",
"byok": "Tu API key",
"cloud": "Google Gemini"
},
"statuses": {
"pending": "Pendiente",
"processing": "Procesando",
"completed": "Listo",
"failed": "Fallido"
},
"not_found": "Memo no encontrado",
"confirm_delete": "¿Eliminar realmente el memo?",
"toast_deleted": "Memo eliminado",
"placeholder_title_idle": "Título…",
"placeholder_title_generating": "Generando título…",
"label_status": "Estado",
"label_duration": "Duración",
"label_language": "Idioma",
"placeholder_language": "p. ej. es",
"label_visibility": "Visibilidad",
"section_summary": "Resumen",
"placeholder_summary": "Añade un resumen…",
"section_transcript": "Transcripción",
"transcribing": "Transcribiendo…",
"transcript_failed": "Transcripción fallida. Inténtalo de nuevo o introdúcela manualmente.",
"transcript_empty": "Sin transcripción.",
"transcript_source": "Voxtral via mana-stt",
"meta_created": "Creado: {date}",
"meta_updated": "Editado: {date}"
}
}

View file

@ -1,336 +0,0 @@
{
"common": {
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer",
"edit": "Modifier",
"share": "Partager",
"back": "Retour",
"next": "Suivant",
"done": "Terminé",
"loading": "Chargement...",
"search": "Rechercher",
"settings": "Paramètres",
"yes": "Oui",
"no": "Non",
"ok": "OK",
"error": "Erreur",
"success": "Succès",
"create": "Créer",
"confirm": "Confirmer",
"close": "Fermer",
"or": "OU"
},
"nav": {
"dashboard": "Tableau de bord",
"tags": "Tags",
"spaces": "Espaces",
"mana": "Mana",
"blueprints": "Modèles",
"statistics": "Statistiques",
"settings": "Paramètres",
"logout": "Déconnexion",
"expand": "Agrandir",
"minimize": "Réduire",
"shortcuts": "Raccourcis"
},
"auth": {
"welcome": "Bienvenue sur Memoro",
"get_started": "Commencer",
"create_account": "Créer un compte",
"sign_in": "Connexion",
"sign_in_with_email": "Se connecter avec l'email",
"sign_up_with_email": "S'inscrire avec l'email",
"email": "Email",
"password": "Mot de passe",
"confirm_password": "Confirmer le mot de passe",
"forgot_password": "Mot de passe oublié ?",
"reset_password": "Réinitialiser le mot de passe",
"logging_in": "Connexion en cours...",
"creating_account": "Création du compte...",
"sending": "Envoi en cours...",
"error_email_required": "Veuillez entrer votre adresse email",
"error_password_required": "Veuillez entrer votre mot de passe",
"error_confirm_password": "Veuillez confirmer votre mot de passe",
"error_passwords_not_match": "Les mots de passe ne correspondent pas",
"error_password_too_short": "Le mot de passe doit contenir au moins 8 caractères",
"error_password_requirements": "Le mot de passe doit contenir au moins une lettre minuscule, une lettre majuscule, un chiffre et un caractère spécial",
"error_registration_failed": "Échec de l'inscription",
"password_requirement": "Le mot de passe doit contenir au moins 8 caractères et inclure au moins une lettre minuscule, une lettre majuscule, un chiffre et un caractère spécial.",
"registration_success": "Inscription réussie ! Vérifiez votre email pour confirmer votre compte.",
"check_email_confirmation": "Veuillez vérifier votre email pour confirmer votre compte.",
"reset_email_sent": "Entrez votre adresse email et nous vous enverrons un lien pour réinitialiser votre mot de passe.",
"email_only_title": "Pourquoi uniquement l'authentification par email ?",
"email_only_info": "Nous ne prenons en charge que l'inscription par email pour garantir votre indépendance et votre confidentialité.",
"email_only_learn_more": "En savoir plus",
"email_only_intro": "Nous croyons en vous donnant le contrôle total sur votre compte et vos données. En utilisant l'authentification par email, nous garantissons :",
"email_only_benefit_1_title": "Pas de dépendance aux fournisseurs",
"email_only_benefit_1_desc": "Vous n'êtes pas dépendant de services tiers comme Google ou Apple. Votre compte fonctionne de manière indépendante.",
"email_only_benefit_2_title": "Confidentialité renforcée",
"email_only_benefit_2_desc": "Nous ne partageons pas vos données avec Google, Apple ou d'autres tiers pour l'authentification.",
"email_only_benefit_3_title": "Portabilité du compte",
"email_only_benefit_3_desc": "Votre adresse email est portable et fonctionne sur tous les appareils et plateformes.",
"email_only_benefit_4_title": "Communication directe",
"email_only_benefit_4_desc": "Nous pouvons vous contacter directement pour des mises à jour importantes sans dépendre de systèmes de notification tiers.",
"email_only_modal_footer": "Nous nous engageons à créer des outils qui respectent votre liberté et votre confidentialité. L'authentification par email fait partie de cet engagement.",
"got_it": "Compris",
"already_have_account": "Vous avez déjà un compte ?",
"dont_have_account": "Pas encore de compte ?",
"terms_agreement": "En utilisant Memoro, vous acceptez nos <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Conditions</a> et notre <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Politique de confidentialité</a>.",
"mana_login": "Mana Login",
"mana_login_description": "Une connexion pour toutes les applications Mana",
"mana_login_benefit_0": "Utilisez les abonnements Mana dans toutes les applications - payez une fois et utilisez tout",
"back": "Retour",
"reset_password_description": "Entrez votre adresse email et nous vous enverrons un lien pour réinitialiser votre mot de passe.",
"reset_password_error": "Échec de la réinitialisation du mot de passe",
"reset_password_success": "Email envoyé !",
"reset_password_rate_limit": "Trop de tentatives. Veuillez patienter quelques minutes.",
"reset_email_sent_description": "Nous avons envoyé un email avec des instructions pour réinitialiser votre mot de passe à {email}. Veuillez vérifier votre boîte de réception et votre dossier spam.",
"back_to_login": "Retour à la connexion",
"resend_email": "Renvoyer l'email",
"reset_email_sent_title": "Email envoyé !",
"terms_agreement_conjunction": "et la",
"terms_agreement_suffix": ".",
"oauth_error_access_denied": "Accès refusé. La connexion a été annulée.",
"oauth_error_server_error": "Erreur serveur lors de l'authentification. Veuillez réessayer.",
"oauth_error_temporarily_unavailable": "Le service d'authentification est temporairement indisponible. Veuillez réessayer plus tard.",
"oauth_error_invalid_request": "Requête invalide. Veuillez réessayer.",
"oauth_error_unauthorized_client": "Client non autorisé. Veuillez contacter le support.",
"oauth_error_unsupported_response_type": "Type de réponse non supporté. Veuillez contacter le support.",
"oauth_error_invalid_scope": "Portée invalide. Veuillez contacter le support.",
"oauth_error_unknown": "Une erreur inconnue s'est produite. Veuillez réessayer.",
"password_requirements_title": "Exigences du mot de passe :",
"password_requirement_length": "Au moins 8 caractères",
"password_requirement_lowercase": "Une lettre minuscule",
"password_requirement_uppercase": "Une lettre majuscule",
"password_requirement_digit": "Un chiffre",
"password_requirement_special": "Un caractère spécial"
},
"dashboard": {
"title": "Tableau de bord",
"recent_memos": "Mémos récents",
"no_memos": "Aucun mémo trouvé",
"create_memo": "Créer un mémo",
"search_placeholder": "Rechercher des mémos..."
},
"memo": {
"title": "Mémo",
"unnamed": "Mémo sans nom",
"word_count": "{{count}} mot",
"word_count_plural": "{{count}} mots",
"delete_confirmation": "Voulez-vous vraiment supprimer ce mémo ? Cette action ne peut pas être annulée.",
"delete_permanently": "Supprimer définitivement",
"deleting": "Suppression...",
"pin": "Épingler",
"unpin": "Désépingler",
"share": "Partager",
"edit": "Modifier",
"translate": "Traduire",
"create_memory": "Créer une Memory",
"ask_question": "Poser une question",
"copy_transcript": "Copier la transcription",
"replace_word": "Remplacer un mot",
"reprocess": "Retraiter",
"label_speakers": "Nommer les intervenants",
"add_photos": "Ajouter des photos",
"manage_spaces": "Gérer les espaces",
"tags": "Tags",
"add_tag": "Ajouter un tag",
"options": "Options",
"search": "Rechercher",
"copy": "Copier",
"speakers": "Intervenants",
"find_replace": "Rechercher et remplacer",
"shortcuts": "Raccourcis",
"no_memo_selected": "Aucun mémo sélectionné",
"select_memo_hint": "Sélectionnez un mémo dans la liste ou créez un nouvel enregistrement",
"processing_transcript": "Transcription en cours...",
"ask_question_placeholder": "Posez une question sur ce mémo...",
"open_in_new_tab": "Ouvrir dans un nouvel onglet",
"edit_title": "Modifier le titre",
"export_text": "Exporter en texte",
"enter_new_title": "Entrez le nouveau titre :",
"no_title": "Sans titre",
"no_transcript": "Aucune transcription disponible",
"link_copied": "Lien copié dans le presse-papiers !",
"transcript_copied": "Transcription copiée dans le presse-papiers !",
"no_search_results": "Aucun résultat de recherche",
"no_memos_with_search": "Aucun mémo contenant \"{query}\" n'a été trouvé.",
"clear_search": "Effacer la recherche",
"no_memos_with_tag": "Aucun mémo avec ce tag",
"no_memos_with_tag_hint": "Il n'y a pas encore de mémos avec ce tag.",
"show_all_memos": "Afficher tous les mémos",
"no_memos_yet": "Pas encore de mémos",
"no_memos_hint": "Allez à la page d'enregistrement pour créer votre premier mémo",
"load_more": "Charger plus de mémos",
"search_placeholder": "Rechercher des mémos...",
"delete_memo_title": "Supprimer le mémo",
"delete_memo_confirm": "Voulez-vous vraiment supprimer \"{title}\" ?",
"delete_memo_warning": "Cette action ne peut pas être annulée. Le mémo et toutes les données associées seront supprimés définitivement.",
"error_user_not_authenticated": "Utilisateur non authentifié",
"error_loading_memos": "Impossible de charger les mémos",
"error_deleting_memo": "Erreur lors de la suppression du mémo. Veuillez réessayer.",
"error_updating_title": "Erreur lors de la mise à jour du titre. Veuillez réessayer.",
"error_copying_link": "Erreur lors de la copie du lien. Veuillez réessayer.",
"error_pin_status": "Erreur lors du changement du statut d'épinglage. Veuillez réessayer.",
"error_saving": "Erreur lors de l'enregistrement. Veuillez réessayer.",
"error_copying_transcript": "Erreur lors de la copie de la transcription. Veuillez réessayer.",
"error_updating_tags": "Erreur lors de la mise à jour des tags. Veuillez réessayer.",
"error_creating_tag": "Erreur lors de la création du tag. Veuillez réessayer.",
"error_asking_question": "Erreur lors du traitement de la question. Veuillez réessayer.",
"retry": "Réessayer",
"export_title": "Titre",
"export_date": "Date",
"export_duration": "Durée",
"export_transcript": "Transcription",
"export_no_transcript": "Aucune transcription disponible",
"export_ai_analysis": "Analyse IA"
},
"tags": {
"title": "Tags",
"create_tag": "Créer un tag",
"search_placeholder": "Rechercher des tags...",
"no_tags": "Aucun tag trouvé",
"delete_confirmation": "Voulez-vous vraiment supprimer le tag \"{{name}}\" ?",
"tag_name": "Nom du tag",
"tag_color": "Couleur du tag"
},
"spaces": {
"title": "Espaces",
"create_space": "Créer un espace",
"no_spaces": "Aucun espace trouvé",
"members": "Membres",
"invite": "Inviter"
},
"blueprints": {
"title": "Modèles",
"loading": "Chargement des modèles...",
"no_blueprints": "Aucun modèle trouvé",
"search_placeholder": "Rechercher des modèles...",
"all_categories": "Tous",
"standard": "Standard",
"manage": "Gérer les modèles",
"activate": "Activer le modèle",
"load_error": "Impossible de charger les modèles.",
"previous_tip": "Conseil précédent",
"next_tip": "Conseil suivant",
"go_to_tip": "Aller au conseil {index}"
},
"record": {
"title": "Enregistrer - Memoro",
"instruction": "Maintenez pour enregistrer",
"uploading": "Téléchargement...",
"user_not_authenticated": "Utilisateur non authentifié",
"upload_failed": "Échec du téléchargement",
"network_error": "Erreur réseau : Veuillez vérifier votre connexion et réessayer.",
"upload_error": "Erreur lors du téléchargement de l'enregistrement : {error}",
"unexpected_error": "Une erreur inattendue s'est produite. Veuillez réessayer.",
"cancel_title": "Supprimer l'enregistrement",
"cancel_message": "Voulez-vous vraiment supprimer l'enregistrement actuel ? Cette action ne peut pas être annulée.",
"cancel_confirm": "Supprimer",
"cancel_abort": "Ne pas supprimer",
"pause": "Pause",
"resume": "Reprendre",
"cancel": "Annuler l'enregistrement"
},
"statistics": {
"title": "Statistiques",
"today": "Aujourd'hui",
"last_30_days": "30 derniers jours",
"total": "Total",
"memos": "Mémos",
"words": "Mots",
"recording_duration": "Durée d'enregistrement",
"loading": "Chargement des statistiques..."
},
"subscription": {
"title": "Acheter du Mana",
"current_plan": "Plan actuel",
"your_mana": "Votre Mana",
"buy": "Acheter",
"popular": "Populaire",
"legacy_plan": "Plan ancien",
"monthly": "Mensuel",
"yearly": "Annuel"
},
"settings": {
"title": "Paramètres",
"appearance": "Apparence",
"theme": "Thème",
"system": "Système",
"light": "Clair",
"dark": "Sombre",
"language": "Langue",
"user_interface": "Interface utilisateur",
"show_language_button": "Afficher le bouton de langue",
"show_recording_instruction": "Afficher les instructions d'enregistrement",
"show_blueprints": "Afficher les modèles",
"show_mana_badge": "Afficher le badge Mana dans l'en-tête",
"data_privacy": "Données et confidentialité",
"save_location": "Enregistrer la position",
"enable_analytics": "Activer les analyses",
"support": "Support",
"contact_support": "Contacter le support",
"rate_app": "Évaluer l'application",
"account": "Compte",
"email_label": "Adresse email",
"sign_out": "Déconnexion",
"delete_account": "Supprimer le compte",
"app_info": "Informations de l'application",
"version": "Version",
"platform": "Plateforme",
"build": "Build",
"browser": "Navigateur",
"copyright": "© 2025 Memoro GmbH",
"made_with_love": "Made with ❤️ in Germany"
},
"theme": {
"toggle": "Changer de thème",
"light_mode": "Mode clair",
"dark_mode": "Mode sombre",
"switch_to_light": "Passer au mode clair",
"switch_to_dark": "Passer au mode sombre"
},
"errors": {
"unexpected": "Une erreur inattendue s'est produite.",
"network": "Erreur réseau. Veuillez vérifier votre connexion Internet.",
"not_found": "Non trouvé.",
"unauthorized": "Non autorisé.",
"forbidden": "Accès refusé.",
"server_error": "Erreur serveur. Veuillez réessayer plus tard."
},
"detail_view": {
"title_sources": {
"none": "Local (règles)",
"browser": "Sur ton appareil (Gemma 4 E2B)",
"mana_server": "Serveur Mana (Gemma 4 E4B)",
"byok": "Ta clé API",
"cloud": "Google Gemini"
},
"statuses": {
"pending": "En attente",
"processing": "En traitement",
"completed": "Terminé",
"failed": "Échec"
},
"not_found": "Mémo introuvable",
"confirm_delete": "Vraiment supprimer le mémo ?",
"toast_deleted": "Mémo supprimé",
"placeholder_title_idle": "Titre…",
"placeholder_title_generating": "Génération du titre…",
"label_status": "Statut",
"label_duration": "Durée",
"label_language": "Langue",
"placeholder_language": "p. ex. fr",
"label_visibility": "Visibilité",
"section_summary": "Résumé",
"placeholder_summary": "Ajouter un résumé…",
"section_transcript": "Transcription",
"transcribing": "Transcription en cours…",
"transcript_failed": "Échec de la transcription. Réessaie ou saisis manuellement.",
"transcript_empty": "Pas de transcription.",
"transcript_source": "Voxtral via mana-stt",
"meta_created": "Créé : {date}",
"meta_updated": "Modifié : {date}"
}
}

View file

@ -1,336 +0,0 @@
{
"common": {
"save": "Salva",
"cancel": "Annulla",
"delete": "Elimina",
"edit": "Modifica",
"share": "Condividi",
"back": "Indietro",
"next": "Avanti",
"done": "Fatto",
"loading": "Caricamento...",
"search": "Cerca",
"settings": "Impostazioni",
"yes": "Sì",
"no": "No",
"ok": "OK",
"error": "Errore",
"success": "Successo",
"create": "Crea",
"confirm": "Conferma",
"close": "Chiudi",
"or": "OPPURE"
},
"nav": {
"dashboard": "Dashboard",
"tags": "Tag",
"spaces": "Spazi",
"mana": "Mana",
"blueprints": "Modelli",
"statistics": "Statistiche",
"settings": "Impostazioni",
"logout": "Esci",
"expand": "Espandi",
"minimize": "Minimizza",
"shortcuts": "Scorciatoie"
},
"auth": {
"welcome": "Benvenuto in Memoro",
"get_started": "Inizia",
"create_account": "Crea account",
"sign_in": "Accedi",
"sign_in_with_email": "Accedi con email",
"sign_up_with_email": "Registrati con email",
"email": "Email",
"password": "Password",
"confirm_password": "Conferma password",
"forgot_password": "Password dimenticata?",
"reset_password": "Reimposta password",
"logging_in": "Accesso in corso...",
"creating_account": "Creazione account...",
"sending": "Invio in corso...",
"error_email_required": "Inserisci il tuo indirizzo email",
"error_password_required": "Inserisci la tua password",
"error_confirm_password": "Conferma la tua password",
"error_passwords_not_match": "Le password non corrispondono",
"error_password_too_short": "La password deve essere lunga almeno 8 caratteri",
"error_password_requirements": "La password deve contenere almeno una lettera minuscola, una lettera maiuscola, un numero e un carattere speciale",
"error_registration_failed": "Registrazione fallita",
"password_requirement": "La password deve essere lunga almeno 8 caratteri e contenere almeno una lettera minuscola, una lettera maiuscola, un numero e un carattere speciale.",
"registration_success": "Registrazione completata con successo! Controlla la tua email per confermare il tuo account.",
"check_email_confirmation": "Controlla la tua email per confermare il tuo account.",
"reset_email_sent": "Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la tua password.",
"email_only_title": "Perché solo autenticazione email?",
"email_only_info": "Supportiamo solo la registrazione via email per garantire la tua indipendenza e privacy.",
"email_only_learn_more": "Scopri di più",
"email_only_intro": "Crediamo nel darti il pieno controllo sul tuo account e sui tuoi dati. Utilizzando l'autenticazione via email, garantiamo:",
"email_only_benefit_1_title": "Nessun vincolo con fornitori",
"email_only_benefit_1_desc": "Non sei dipendente da servizi di terze parti come Google o Apple. Il tuo account funziona in modo indipendente.",
"email_only_benefit_2_title": "Privacy migliorata",
"email_only_benefit_2_desc": "Non condividiamo i tuoi dati con Google, Apple o altre terze parti per l'autenticazione.",
"email_only_benefit_3_title": "Portabilità dell'account",
"email_only_benefit_3_desc": "Il tuo indirizzo email è portabile e funziona su tutti i dispositivi e piattaforme.",
"email_only_benefit_4_title": "Comunicazione diretta",
"email_only_benefit_4_desc": "Possiamo contattarti direttamente per aggiornamenti importanti senza fare affidamento su sistemi di notifica di terze parti.",
"email_only_modal_footer": "Ci impegniamo a costruire strumenti che rispettino la tua libertà e privacy. L'autenticazione via email fa parte di questo impegno.",
"got_it": "Ho capito",
"already_have_account": "Hai già un account?",
"dont_have_account": "Non hai un account?",
"terms_agreement": "Utilizzando Memoro accetti i nostri <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Termini</a> e la nostra <a href=\"https://mana.ai/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"text-decoration: underline;\">Informativa sulla privacy</a>.",
"mana_login": "Mana Login",
"mana_login_description": "Un login per tutte le app Mana",
"mana_login_benefit_0": "Usa gli abbonamenti Mana in tutte le app - paga una volta e usa tutto",
"back": "Indietro",
"reset_password_description": "Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la tua password.",
"reset_password_error": "Reimpostazione password fallita",
"reset_password_success": "Email inviata!",
"reset_password_rate_limit": "Troppi tentativi. Attendi qualche minuto.",
"reset_email_sent_description": "Abbiamo inviato un'email con le istruzioni per reimpostare la password a {email}. Controlla la posta in arrivo e la cartella spam.",
"back_to_login": "Torna al login",
"resend_email": "Reinvia email",
"reset_email_sent_title": "Email inviata!",
"terms_agreement_conjunction": "e la",
"terms_agreement_suffix": ".",
"oauth_error_access_denied": "Accesso negato. L'accesso è stato annullato.",
"oauth_error_server_error": "Errore del server durante l'autenticazione. Riprova.",
"oauth_error_temporarily_unavailable": "Il servizio di autenticazione non è temporaneamente disponibile. Riprova più tardi.",
"oauth_error_invalid_request": "Richiesta non valida. Riprova.",
"oauth_error_unauthorized_client": "Client non autorizzato. Contatta il supporto.",
"oauth_error_unsupported_response_type": "Tipo di risposta non supportato. Contatta il supporto.",
"oauth_error_invalid_scope": "Ambito non valido. Contatta il supporto.",
"oauth_error_unknown": "Si è verificato un errore sconosciuto. Riprova.",
"password_requirements_title": "Requisiti password:",
"password_requirement_length": "Almeno 8 caratteri",
"password_requirement_lowercase": "Una lettera minuscola",
"password_requirement_uppercase": "Una lettera maiuscola",
"password_requirement_digit": "Un numero",
"password_requirement_special": "Un carattere speciale"
},
"dashboard": {
"title": "Dashboard",
"recent_memos": "Memo recenti",
"no_memos": "Nessun memo trovato",
"create_memo": "Crea memo",
"search_placeholder": "Cerca memo..."
},
"memo": {
"title": "Memo",
"unnamed": "Memo senza nome",
"word_count": "{{count}} parola",
"word_count_plural": "{{count}} parole",
"delete_confirmation": "Vuoi davvero eliminare questo memo? Questa azione non può essere annullata.",
"delete_permanently": "Elimina definitivamente",
"deleting": "Eliminazione...",
"pin": "Fissa",
"unpin": "Sblocca",
"share": "Condividi",
"edit": "Modifica",
"translate": "Traduci",
"create_memory": "Crea Memory",
"ask_question": "Fai una domanda",
"copy_transcript": "Copia trascrizione",
"replace_word": "Sostituisci parola",
"reprocess": "Rielabora",
"label_speakers": "Nomina relatori",
"add_photos": "Aggiungi foto",
"manage_spaces": "Gestisci spazi",
"tags": "Tag",
"add_tag": "Aggiungi tag",
"options": "Opzioni",
"search": "Cerca",
"copy": "Copia",
"speakers": "Relatori",
"find_replace": "Trova e sostituisci",
"shortcuts": "Scorciatoie",
"no_memo_selected": "Nessun memo selezionato",
"select_memo_hint": "Seleziona un memo dalla lista o crea una nuova registrazione",
"processing_transcript": "Trascrizione in corso...",
"ask_question_placeholder": "Fai una domanda su questo memo...",
"open_in_new_tab": "Apri in una nuova scheda",
"edit_title": "Modifica titolo",
"export_text": "Esporta come testo",
"enter_new_title": "Inserisci nuovo titolo:",
"no_title": "Senza titolo",
"no_transcript": "Nessuna trascrizione disponibile",
"link_copied": "Link copiato negli appunti!",
"transcript_copied": "Trascrizione copiata negli appunti!",
"no_search_results": "Nessun risultato di ricerca",
"no_memos_with_search": "Nessun memo trovato contenente \"{query}\".",
"clear_search": "Cancella ricerca",
"no_memos_with_tag": "Nessun memo con questo tag",
"no_memos_with_tag_hint": "Non ci sono ancora memo con questo tag.",
"show_all_memos": "Mostra tutti i memo",
"no_memos_yet": "Ancora nessun memo",
"no_memos_hint": "Vai alla pagina di registrazione per creare il tuo primo memo",
"load_more": "Carica altri memo",
"search_placeholder": "Cerca memo...",
"delete_memo_title": "Elimina memo",
"delete_memo_confirm": "Vuoi davvero eliminare \"{title}\"?",
"delete_memo_warning": "Questa azione non può essere annullata. Il memo e tutti i dati associati verranno eliminati definitivamente.",
"error_user_not_authenticated": "Utente non autenticato",
"error_loading_memos": "Impossibile caricare i memo",
"error_deleting_memo": "Errore durante l'eliminazione del memo. Riprova.",
"error_updating_title": "Errore durante l'aggiornamento del titolo. Riprova.",
"error_copying_link": "Errore durante la copia del link. Riprova.",
"error_pin_status": "Errore durante la modifica dello stato di fissaggio. Riprova.",
"error_saving": "Errore durante il salvataggio. Riprova.",
"error_copying_transcript": "Errore durante la copia della trascrizione. Riprova.",
"error_updating_tags": "Errore durante l'aggiornamento dei tag. Riprova.",
"error_creating_tag": "Errore durante la creazione del tag. Riprova.",
"error_asking_question": "Errore durante l'elaborazione della domanda. Riprova.",
"retry": "Riprova",
"export_title": "Titolo",
"export_date": "Data",
"export_duration": "Durata",
"export_transcript": "Trascrizione",
"export_no_transcript": "Nessuna trascrizione disponibile",
"export_ai_analysis": "Analisi IA"
},
"tags": {
"title": "Tag",
"create_tag": "Crea tag",
"search_placeholder": "Cerca tag...",
"no_tags": "Nessun tag trovato",
"delete_confirmation": "Vuoi davvero eliminare il tag \"{{name}}\"?",
"tag_name": "Nome tag",
"tag_color": "Colore tag"
},
"spaces": {
"title": "Spazi",
"create_space": "Crea spazio",
"no_spaces": "Nessuno spazio trovato",
"members": "Membri",
"invite": "Invita"
},
"blueprints": {
"title": "Modelli",
"loading": "Caricamento modelli...",
"no_blueprints": "Nessun modello trovato",
"search_placeholder": "Cerca modelli...",
"all_categories": "Tutti",
"standard": "Standard",
"manage": "Gestisci modelli",
"activate": "Attiva modello",
"load_error": "Impossibile caricare i modelli.",
"previous_tip": "Consiglio precedente",
"next_tip": "Consiglio successivo",
"go_to_tip": "Vai al consiglio {index}"
},
"record": {
"title": "Registra - Memoro",
"instruction": "Tieni premuto per registrare",
"uploading": "Caricamento...",
"user_not_authenticated": "Utente non autenticato",
"upload_failed": "Caricamento fallito",
"network_error": "Errore di rete: Verifica la connessione e riprova.",
"upload_error": "Errore durante il caricamento della registrazione: {error}",
"unexpected_error": "Si è verificato un errore imprevisto. Riprova.",
"cancel_title": "Elimina registrazione",
"cancel_message": "Vuoi davvero eliminare la registrazione corrente? Questa azione non può essere annullata.",
"cancel_confirm": "Elimina",
"cancel_abort": "Non eliminare",
"pause": "Pausa",
"resume": "Riprendi",
"cancel": "Annulla registrazione"
},
"statistics": {
"title": "Statistiche",
"today": "Oggi",
"last_30_days": "Ultimi 30 giorni",
"total": "Totale",
"memos": "Memo",
"words": "Parole",
"recording_duration": "Durata registrazione",
"loading": "Caricamento statistiche..."
},
"subscription": {
"title": "Acquista Mana",
"current_plan": "Piano attuale",
"your_mana": "Il tuo Mana",
"buy": "Acquista",
"popular": "Popolare",
"legacy_plan": "Piano Legacy",
"monthly": "Mensile",
"yearly": "Annuale"
},
"settings": {
"title": "Impostazioni",
"appearance": "Aspetto",
"theme": "Tema",
"system": "Sistema",
"light": "Chiaro",
"dark": "Scuro",
"language": "Lingua",
"user_interface": "Interfaccia utente",
"show_language_button": "Mostra pulsante lingua",
"show_recording_instruction": "Mostra istruzioni di registrazione",
"show_blueprints": "Mostra modelli",
"show_mana_badge": "Mostra badge Mana nell'intestazione",
"data_privacy": "Dati e privacy",
"save_location": "Salva posizione",
"enable_analytics": "Abilita analytics",
"support": "Supporto",
"contact_support": "Contatta supporto",
"rate_app": "Valuta app",
"account": "Account",
"email_label": "Indirizzo email",
"sign_out": "Esci",
"delete_account": "Elimina account",
"app_info": "Informazioni app",
"version": "Versione",
"platform": "Piattaforma",
"build": "Build",
"browser": "Browser",
"copyright": "© 2025 Memoro GmbH",
"made_with_love": "Made with ❤️ in Germany"
},
"theme": {
"toggle": "Cambia tema",
"light_mode": "Modalità chiara",
"dark_mode": "Modalità scura",
"switch_to_light": "Passa alla modalità chiara",
"switch_to_dark": "Passa alla modalità scura"
},
"errors": {
"unexpected": "Si è verificato un errore imprevisto.",
"network": "Errore di rete. Verifica la tua connessione Internet.",
"not_found": "Non trovato.",
"unauthorized": "Non autorizzato.",
"forbidden": "Accesso negato.",
"server_error": "Errore del server. Riprova più tardi."
},
"detail_view": {
"title_sources": {
"none": "Locale (regole)",
"browser": "Sul tuo dispositivo (Gemma 4 E2B)",
"mana_server": "Mana server (Gemma 4 E4B)",
"byok": "La tua API key",
"cloud": "Google Gemini"
},
"statuses": {
"pending": "In attesa",
"processing": "In elaborazione",
"completed": "Completato",
"failed": "Fallito"
},
"not_found": "Memo non trovato",
"confirm_delete": "Eliminare davvero il memo?",
"toast_deleted": "Memo eliminato",
"placeholder_title_idle": "Titolo…",
"placeholder_title_generating": "Generazione del titolo…",
"label_status": "Stato",
"label_duration": "Durata",
"label_language": "Lingua",
"placeholder_language": "es. it",
"label_visibility": "Visibilità",
"section_summary": "Riassunto",
"placeholder_summary": "Aggiungi un riassunto…",
"section_transcript": "Trascrizione",
"transcribing": "Trascrizione in corso…",
"transcript_failed": "Trascrizione non riuscita. Riprova o inseriscila manualmente.",
"transcript_empty": "Nessuna trascrizione.",
"transcript_source": "Voxtral via mana-stt",
"meta_created": "Creato: {date}",
"meta_updated": "Modificato: {date}"
}
}

View file

@ -1,137 +0,0 @@
<!--
Memoro — Workbench ListView
Recent memos with transcription status.
-->
<script lang="ts">
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
import { db } from '$lib/data/database';
import { BaseListView } from '@mana/shared-ui';
import type { ViewProps } from '$lib/app-registry';
import type { LocalMemo } from './types';
import { memosStore } from './stores/memos.svelte';
import FloatingInputBar from '$lib/components/FloatingInputBar.svelte';
let { navigate }: ViewProps = $props();
const memosQuery = useLiveQueryWithDefault(async () => {
const all = await db.table<LocalMemo>('memos').toArray();
return all.filter((m) => !m.deletedAt && !m.isArchived);
}, [] as LocalMemo[]);
const memos = $derived(memosQuery.value);
const sorted = $derived(
[...memos].sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''))
);
const pinned = $derived(memos.filter((m) => m.isPinned));
let memoTitle = $state('');
async function handleTextCreate() {
if (!memoTitle.trim()) return;
const memo = await memosStore.create({ title: memoTitle.trim() });
memoTitle = '';
navigate('detail', {
memoId: memo.id,
_siblingIds: sorted.map((m) => m.id),
_siblingKey: 'memoId',
});
}
async function handleVoiceComplete(blob: Blob, durationMs: number) {
const memo = await memosStore.createFromVoice(blob, durationMs, 'de');
// Open the new memo so the user sees the transcription land
navigate('detail', {
memoId: memo.id,
_siblingIds: sorted.map((m) => m.id),
_siblingKey: 'memoId',
});
}
function formatDuration(ms: number | null): string {
if (!ms) return '--:--';
const sec = Math.round(ms / 1000);
const m = Math.floor(sec / 60);
const s = sec % 60;
return `${m}:${String(s).padStart(2, '0')}`;
}
const statusColors: Record<string, string> = {
pending: 'bg-warning/20 text-warning',
processing: 'bg-primary/20 text-primary',
completed: 'bg-success/20 text-success',
failed: 'bg-error/20 text-error',
};
</script>
<div class="memoro-view">
<BaseListView items={sorted} getKey={(m) => m.id} emptyTitle="Keine Memos">
{#snippet header()}
<span>{memos.length} Memos</span>
<span>{pinned.length} angepinnt</span>
{/snippet}
{#snippet item(memo)}
<button
onclick={() =>
navigate('detail', {
memoId: memo.id,
_siblingIds: sorted.map((m) => m.id),
_siblingKey: 'memoId',
})}
class="mb-2 w-full rounded-md border border-border px-3 py-2.5 text-left transition-colors hover:bg-muted/50 min-h-[44px]"
>
<div class="flex items-start justify-between gap-2">
<div class="min-w-0 flex-1">
<div class="flex items-center gap-1">
{#if memo.isPinned}
<span class="text-xs text-muted-foreground/70">&#128204;</span>
{/if}
<p class="truncate text-sm font-medium text-foreground">
{memo.title || 'Unbenanntes Memo'}
</p>
</div>
{#if memo.intro}
<p class="mt-0.5 truncate text-xs text-muted-foreground">{memo.intro}</p>
{/if}
</div>
<div class="flex items-center gap-1.5 shrink-0">
{#if memo.transcriptModel && memo.processingStatus === 'completed'}
<span
class="rounded px-1 py-0.5 text-[9px] bg-muted text-muted-foreground"
title="STT-Pipeline"
>
{memo.transcriptModel}
</span>
{/if}
<span
class="rounded px-1.5 py-0.5 text-[10px] {statusColors[memo.processingStatus] ?? ''}"
>
{memo.processingStatus === 'completed'
? formatDuration(memo.audioDurationMs)
: memo.processingStatus}
</span>
</div>
</div>
</button>
{/snippet}
</BaseListView>
<FloatingInputBar
bind:value={memoTitle}
placeholder="Memo sprechen..."
onSubmit={handleTextCreate}
voice
voiceFeature="memoro-voice-capture"
voiceReason="Sprach-Memos werden verschlüsselt gespeichert. Dafür brauchst du ein Mana-Konto."
onVoiceComplete={handleVoiceComplete}
/>
</div>
<style>
.memoro-view {
height: 100%;
position: relative;
}
</style>

View file

@ -1,59 +0,0 @@
/**
* Memoro module collection accessors and guest seed data.
*
* Table names: memos, memories, memoroTags, memoTags, memoroSpaces, spaceMembers, memoSpaces
*/
import { db } from '$lib/data/database';
import type {
LocalMemo,
LocalMemory,
LocalMemoTag,
LocalSpace,
LocalSpaceMember,
LocalMemoSpace,
} from './types';
// ─── Collection Accessors ──────────────────────────────────
export const memoTable = db.table<LocalMemo>('memos');
export const memoryTable = db.table<LocalMemory>('memories');
export const memoTagTable = db.table<LocalMemoTag>('memoTags');
export const memoroSpaceTable = db.table<LocalSpace>('memoroSpaces');
export const spaceMemberTable = db.table<LocalSpaceMember>('spaceMembers');
export const memoSpaceTable = db.table<LocalMemoSpace>('memoSpaces');
// ─── Guest Seed ────────────────────────────────────────────
const DEMO_MEMO_ID = 'demo-welcome-memo';
export const MEMORO_GUEST_SEED = {
memos: [
{
id: DEMO_MEMO_ID,
title: 'Willkommen bei Memoro',
intro: 'Dies ist ein Beispiel-Memo zum Kennenlernen.',
transcript:
'Memoro ist dein AI-gestützter Sprachrekorder und Memo-Manager. Nimm Gedanken auf, lass sie transkribieren und erstelle Erinnerungen daraus.',
audioDurationMs: null,
processingStatus: 'completed' as const,
isArchived: false,
isPinned: true,
blueprintId: null,
language: 'de',
},
],
memories: [
{
id: 'demo-memory-1',
memoId: DEMO_MEMO_ID,
title: 'Kernfunktionen',
content:
'Memoro bietet Sprachaufnahme, automatische Transkription, KI-gestützte Zusammenfassungen und Tagging.',
},
],
memoTags: [] as Record<string, unknown>[],
memoroSpaces: [] as Record<string, unknown>[],
spaceMembers: [] as Record<string, unknown>[],
memoSpaces: [] as Record<string, unknown>[],
};

View file

@ -1,53 +0,0 @@
/**
* Memoro module barrel exports.
*/
export { memosStore } from './stores/memos.svelte';
export {
tagMutations,
useAllTags,
getTagById,
getTagsByIds,
getTagColor,
memoTagOps,
} from './stores/tags.svelte';
export { memoriesStore } from './stores/memories.svelte';
export {
useAllMemos,
useArchivedMemos,
useMemoriesByMemo,
useAllMemoTags,
useAllSpaces,
toMemo,
toMemory,
toSpace,
sortMemos,
filterBySearch,
filterByTag,
getTagsForMemo,
formatDuration,
getStatusLabel,
} from './queries';
export {
memoTable,
memoryTable,
memoTagTable,
memoroSpaceTable,
spaceMemberTable,
memoSpaceTable,
MEMORO_GUEST_SEED,
} from './collections';
export type {
LocalMemo,
LocalMemory,
LocalMemoTag,
LocalSpace,
LocalSpaceMember,
LocalMemoSpace,
Memo,
Memory,
Space,
ProcessingStatus,
} from './types';
// Tag type re-exported from @mana/shared-tags (the local memoro Tag was removed)
export type { Tag } from '@mana/shared-tags';

View file

@ -1,188 +0,0 @@
import { formatDate } from '$lib/i18n/format';
/**
* Memoro LLM result watcher.
*
* The persistent task queue stores LlmTask results in its own Dexie
* table but for module-side data (like a memo's title), we want
* those results to land in the module's own collection so existing
* queries / UI keep working without per-component subscriptions.
*
* This file owns the bridge for memoro: it subscribes via Dexie
* liveQuery to completed `common.generateTitle` tasks tagged
* with refType: 'memo', and for each one writes the generated title
* back into the memo row, then deletes the queue entry to mark it
* consumed. Once consumed, the queue stays empty for that memo.
*
* The watcher is started exactly once per page session see
* startMemoroLlmWatcher() below for the idempotent guard. The
* memoro module config calls it from its initialize() hook, but
* even if a future refactor calls it twice, the duplicate call is
* a no-op.
*
* Cleanup: the subscription handle is stored module-scope; the page
* teardown is implicit (page reload kills the dev server too). For
* a long-lived SPA we'd want stop() punt that to a follow-up.
*/
import { liveQuery, type Subscription } from 'dexie';
import type { QueuedTask } from '@mana/shared-llm';
import { llmQueueDb } from '$lib/llm-queue';
import { encryptRecord } from '$lib/data/crypto';
import { memoTable } from './collections';
import type { LocalMemo } from './types';
let subscription: Subscription | null = null;
export function startMemoroLlmWatcher(): void {
if (subscription) return; // already running
if (typeof window === 'undefined') return; // SSR-safe no-op
console.info('[memoro-llm-watcher] starting subscription');
const observable = liveQuery(async () =>
llmQueueDb.tasks
.where('state')
.equals('done')
.and((t: QueuedTask) => t.taskName === 'common.generateTitle' && t.refType === 'memo')
.toArray()
);
subscription = observable.subscribe({
next: async (rows) => {
if (rows.length === 0) return;
console.info(`[memoro-llm-watcher] saw ${rows.length} done title task(s)`);
for (const row of rows) {
try {
await applyRow(row);
} catch (err) {
console.warn('[memoro-llm-watcher] failed to apply row', row.id, err);
// Best-effort: mark the row consumed so we don't keep
// retrying a row that crashes the watcher every cycle.
try {
await llmQueueDb.tasks.delete(row.id);
} catch {
/* ignore */
}
}
}
},
error: (err) => {
console.warn('[memoro-llm-watcher] subscription error:', err);
},
});
// Belt-and-suspenders: Dexie liveQuery sometimes misses the FIRST
// emission if the subscription is set up in the same microtask as
// the table update. Trigger an immediate manual sweep on startup
// so any rows already done from a previous tab session get picked up.
void runOneSweep();
}
async function runOneSweep(): Promise<void> {
try {
const rows = await llmQueueDb.tasks
.where('state')
.equals('done')
.and((t: QueuedTask) => t.taskName === 'common.generateTitle' && t.refType === 'memo')
.toArray();
if (rows.length === 0) {
console.info('[memoro-llm-watcher] startup sweep: no pending done rows');
return;
}
console.info(`[memoro-llm-watcher] startup sweep: applying ${rows.length} row(s)`);
for (const row of rows) {
try {
await applyRow(row);
} catch (err) {
console.warn('[memoro-llm-watcher] startup sweep failed for row', row.id, err);
}
}
} catch (err) {
console.warn('[memoro-llm-watcher] startup sweep error:', err);
}
}
async function applyRow(row: QueuedTask): Promise<void> {
if (!row.refId || typeof row.result !== 'string') {
console.info(
`[memoro-llm-watcher] dropping row ${row.id} — missing refId or result not a string`
);
await llmQueueDb.tasks.delete(row.id);
return;
}
const memo = await memoTable.get(row.refId);
if (!memo) {
console.info(`[memoro-llm-watcher] dropping row ${row.id} — memo ${row.refId} not found`);
await llmQueueDb.tasks.delete(row.id);
return;
}
// Don't overwrite a manual title that the user typed
// between enqueue time and result time. The memo we just read
// from Dexie is still ENCRYPTED — title is either null/undefined
// (no manual title) or an `enc:1:...` blob (manual title set).
// Either way, presence-check is enough — we don't need to decrypt
// to know if the user filled it in.
if (typeof memo.title === 'string' && memo.title.trim()) {
console.info(
`[memoro-llm-watcher] memo ${row.refId} already has a title — skipping auto-title`
);
await llmQueueDb.tasks.delete(row.id);
return;
}
// Backstop: if the task result somehow came back empty/whitespace
// (LLM emitted only special tokens, runRules got an empty input,
// any other edge case), synthesize a date-based fallback so the
// user always gets *some* title rather than a stuck empty input.
let titleToWrite = row.result.trim();
if (!titleToWrite) {
const created = (memo as { createdAt?: string }).createdAt;
const dateLabel = created
? formatDate(new Date(created), {
day: 'numeric',
month: 'long',
year: 'numeric',
})
: formatDate(new Date());
titleToWrite = `Memo vom ${dateLabel}`;
console.warn(
`[memoro-llm-watcher] row ${row.id} returned empty title — using date fallback "${titleToWrite}"`,
{ source: row.source, attempts: row.attempts, rawResult: JSON.stringify(row.result) }
);
} else {
console.info(`[memoro-llm-watcher] writing title to memo ${row.refId}: "${titleToWrite}"`, {
source: row.source,
attempts: row.attempts,
});
}
// Stamp the title source on the memo's metadata so the DetailView can
// render a "via Mana-Server" / "Auf deinem Gerät" / "Lokal (Regeln)"
// label under the title — the same UX we already have under the
// transcript ("Voxtral via mana-stt"). Stored as plaintext metadata
// because the tier name isn't sensitive and the encryption registry
// for memos only covers title/intro/transcript.
const existingMetadata = (memo.metadata as Record<string, unknown> | null) ?? {};
const diff: Partial<LocalMemo> = {
title: titleToWrite,
metadata: {
...existingMetadata,
titleSource: row.source,
},
};
await encryptRecord('memos', diff);
await memoTable.update(row.refId, diff);
// Mark consumed
await llmQueueDb.tasks.delete(row.id);
console.info(`[memoro-llm-watcher] applied + cleared row ${row.id}`);
}
export function stopMemoroLlmWatcher(): void {
subscription?.unsubscribe();
subscription = null;
}

View file

@ -1,13 +0,0 @@
import type { ModuleConfig } from '$lib/data/module-registry';
export const memoroModuleConfig: ModuleConfig = {
appId: 'memoro',
tables: [
{ name: 'memos' },
{ name: 'memories' },
{ name: 'memoTags' },
{ name: 'memoroSpaces', syncName: 'spaces' },
{ name: 'spaceMembers' },
{ name: 'memoSpaces' },
],
};

View file

@ -1,179 +0,0 @@
/**
* Reactive queries & pure helpers for Memoro uses Dexie liveQuery on the unified DB.
*/
import { useScopedLiveQuery } from '$lib/data/scope/use-scoped-live-query.svelte';
import { deriveUpdatedAt } from '$lib/data/sync';
import { db } from '$lib/data/database';
import { scopedForModule } from '$lib/data/scope';
import { decryptRecords } from '$lib/data/crypto';
// `useAllTags` re-exports the shared-tags hook below; the actual tag
// objects flowing through this module are the shared shape, not the
// memoro/types.Tag declared next to LocalMemoTag. Importing the shared
// type here keeps `getTagsForMemo` and friends in sync with what the
// hook actually returns.
import type { Tag } from '@mana/shared-tags';
import type {
LocalMemo,
LocalMemory,
LocalMemoTag,
LocalSpace,
Memo,
Memory,
Space,
} from './types';
// ─── Type Converters ───────────────────────────────────────
export function toMemo(local: LocalMemo): Memo {
return {
id: local.id,
title: local.title,
intro: local.intro,
transcript: local.transcript,
audioDurationMs: local.audioDurationMs,
transcriptModel: local.transcriptModel ?? null,
processingStatus: local.processingStatus,
isArchived: local.isArchived,
isPinned: local.isPinned,
visibility: local.visibility ?? 'space',
language: local.language,
createdAt: local.createdAt ?? new Date().toISOString(),
updatedAt: deriveUpdatedAt(local),
};
}
export function toMemory(local: LocalMemory): Memory {
return {
id: local.id,
memoId: local.memoId,
title: local.title,
content: local.content,
createdAt: local.createdAt ?? new Date().toISOString(),
updatedAt: deriveUpdatedAt(local),
};
}
export function toSpace(local: LocalSpace): Space {
return {
id: local.id,
name: local.name,
description: local.description,
ownerId: local.ownerId,
createdAt: local.createdAt ?? new Date().toISOString(),
updatedAt: deriveUpdatedAt(local),
};
}
// ─── Live Queries ──────────────────────────────────────────
/** All non-archived memos, sorted by pinned first then createdAt desc. */
export function useAllMemos() {
return useScopedLiveQuery(async () => {
const visible = (await scopedForModule<LocalMemo, string>('memoro', 'memos').toArray()).filter(
(m) => !m.deletedAt && !m.isArchived
);
const decrypted = await decryptRecords('memos', visible);
return sortMemos(decrypted.map(toMemo));
}, [] as Memo[]);
}
/** All archived memos, sorted by updatedAt desc. */
export function useArchivedMemos() {
return useScopedLiveQuery(async () => {
const visible = (await scopedForModule<LocalMemo, string>('memoro', 'memos').toArray()).filter(
(m) => !m.deletedAt && m.isArchived
);
const decrypted = await decryptRecords('memos', visible);
return decrypted
.map(toMemo)
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
}, [] as Memo[]);
}
/** Memories for a specific memo. */
export function useMemoriesByMemo(memoId: string) {
return useScopedLiveQuery(async () => {
const visible = (
await db.table<LocalMemory>('memories').where('memoId').equals(memoId).toArray()
).filter((m) => !m.deletedAt);
const decrypted = await decryptRecords('memories', visible);
return decrypted.map(toMemory);
}, [] as Memory[]);
}
// Tags: use shared global tags from @mana/shared-stores
export { useAllTags } from '@mana/shared-stores';
/** All memo-tag associations. */
export function useAllMemoTags() {
return useScopedLiveQuery(async () => {
const locals = await scopedForModule<LocalMemoTag, string>('memoro', 'memoTags').toArray();
return locals.filter((mt) => !mt.deletedAt);
}, [] as LocalMemoTag[]);
}
/** All spaces. */
export function useAllSpaces() {
return useScopedLiveQuery(async () => {
const locals = await scopedForModule<LocalSpace, string>('memoro', 'memoroSpaces').toArray();
return locals.filter((s) => !s.deletedAt).map(toSpace);
}, [] as Space[]);
}
// ─── Pure Sort / Filter Functions ──────────────────────────
/** Sort memos: pinned first, then by createdAt descending. */
export function sortMemos(list: Memo[]): Memo[] {
return [...list].sort((a, b) => {
if (a.isPinned && !b.isPinned) return -1;
if (!a.isPinned && b.isPinned) return 1;
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
});
}
/** Filter memos by search query on title and transcript. */
export function filterBySearch(memos: Memo[], query: string): Memo[] {
if (!query.trim()) return memos;
const lower = query.toLowerCase();
return memos.filter(
(m) => m.title?.toLowerCase().includes(lower) || m.transcript?.toLowerCase().includes(lower)
);
}
/** Filter memos by tag. */
export function filterByTag(memos: Memo[], memoTags: LocalMemoTag[], tagId: string): Memo[] {
const memoIds = new Set(memoTags.filter((mt) => mt.tagId === tagId).map((mt) => mt.memoId));
return memos.filter((m) => memoIds.has(m.id));
}
/** Get tags for a specific memo. */
export function getTagsForMemo(tags: Tag[], memoTags: LocalMemoTag[], memoId: string): Tag[] {
const tagIds = new Set(memoTags.filter((mt) => mt.memoId === memoId).map((mt) => mt.tagId));
return tags.filter((t) => tagIds.has(t.id));
}
/** Format audio duration in ms to readable string. */
export function formatDuration(ms: number | null): string {
if (!ms) return '';
const totalSeconds = Math.floor(ms / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}:${String(seconds).padStart(2, '0')}`;
}
/** Get processing status label. */
export function getStatusLabel(status: string): string {
switch (status) {
case 'pending':
return 'Ausstehend';
case 'processing':
return 'Verarbeitung...';
case 'completed':
return 'Fertig';
case 'failed':
return 'Fehlgeschlagen';
default:
return status;
}
}

View file

@ -1,45 +0,0 @@
/**
* Memories Store Mutations Only
*
* Reads come from liveQuery hooks in queries.ts.
* Handles memory (AI insight) CRUD.
*/
import { memoryTable } from '../collections';
import { toMemory } from '../queries';
import { MemoroEvents } from '@mana/shared-utils/analytics';
import { encryptRecord } from '$lib/data/crypto';
import type { LocalMemory } from '../types';
export const memoriesStore = {
/** Create a new memory for a memo. */
async create(data: { memoId: string; title: string; content?: string }) {
const newLocal: LocalMemory = {
id: crypto.randomUUID(),
memoId: data.memoId,
title: data.title,
content: data.content ?? null,
};
const plaintextSnapshot = toMemory(newLocal);
await encryptRecord('memories', newLocal);
await memoryTable.add(newLocal);
MemoroEvents.memoCreated();
return plaintextSnapshot;
},
/** Update a memory. */
async update(id: string, data: Partial<Pick<LocalMemory, 'title' | 'content'>>) {
const diff: Partial<LocalMemory> = {
...data,
};
await encryptRecord('memories', diff);
await memoryTable.update(id, diff);
},
/** Soft-delete a memory. */
async delete(id: string) {
const now = new Date().toISOString();
await memoryTable.update(id, { deletedAt: now });
MemoroEvents.memoDeleted();
},
};

View file

@ -1,215 +0,0 @@
/**
* Memos Store Mutations Only
*
* Reads come from liveQuery hooks in queries.ts.
* Handles memo CRUD, archive, pin, delete.
*/
import { memoTable } from '../collections';
import { toMemo } from '../queries';
import { createArchiveOps } from '@mana/shared-stores';
import { MemoroEvents } from '@mana/shared-utils/analytics';
import { encryptRecord } from '$lib/data/crypto';
import { emitDomainEvent } from '$lib/data/events';
import { getActiveSpace } from '$lib/data/scope';
import { getEffectiveUserId } from '$lib/data/current-user';
import { defaultVisibilityFor, type VisibilityLevel } from '@mana/shared-privacy';
import { transcribeAudio } from '$lib/voice/transcribe';
import { llmTaskQueue } from '$lib/llm-queue';
import { generateTitleTask } from '$lib/llm-tasks/generate-title';
import type { LocalMemo } from '../types';
/** Archive/soft-delete ops for memos. */
export const memoArchive = createArchiveOps({
table: () => memoTable,
});
export const memosStore = {
/** Create a new memo (e.g., after recording). */
async create(data: {
title?: string;
transcript?: string;
language?: string;
blueprintId?: string;
audioDurationMs?: number;
processingStatus?: LocalMemo['processingStatus'];
}) {
const now = new Date().toISOString();
const newLocal: LocalMemo = {
id: crypto.randomUUID(),
title: data.title ?? null,
intro: null,
transcript: data.transcript ?? null,
audioDurationMs: data.audioDurationMs ?? null,
transcriptModel: null,
processingStatus: data.processingStatus ?? (data.transcript ? 'completed' : 'pending'),
isArchived: false,
isPinned: false,
visibility: defaultVisibilityFor(getActiveSpace()?.type),
blueprintId: data.blueprintId ?? null,
language: data.language ?? null,
// createdAt + updatedAt are required by LocalMemo's type but the
// previous create() never set them — DetailView showed
// "Erstellt: Invalid Date" for every memo. The Dexie creating
// hook only auto-stamps userId + __fieldMeta; module
// stores have to set their own createdAt/updatedAt explicitly
// (consistent with the rest of the Mana modules).
createdAt: now,
} as LocalMemo;
const plaintextSnapshot = toMemo(newLocal);
await encryptRecord('memos', newLocal);
await memoTable.add(newLocal);
emitDomainEvent('MemoCreated', 'memoro', 'memos', newLocal.id, {
memoId: newLocal.id,
fromVoice: false,
});
MemoroEvents.memoCreated();
return plaintextSnapshot;
},
/**
* Create a placeholder memo from a fresh voice recording and start the
* background transcription. Returns the new memo immediately so the UI
* can navigate / show a "processing" state without waiting.
*/
async createFromVoice(blob: Blob, durationMs: number, language?: string) {
const memo = await this.create({
audioDurationMs: durationMs,
language,
processingStatus: 'processing',
});
// Fire and forget — transcription updates the memo when it returns.
void this.transcribeBlob(memo.id, blob, language);
return memo;
},
/**
* Upload an audio blob to /api/v1/voice/transcribe and write the result
* back into the memo. Marks completed on success, failed on error.
*/
async transcribeBlob(memoId: string, blob: Blob, language?: string): Promise<void> {
try {
const result = await transcribeAudio(blob, language);
const transcript = result.text;
const existing = await memoTable.get(memoId);
if (!existing) return;
const diff: Partial<LocalMemo> = {
transcript,
transcriptModel: result.model,
language: existing.language ?? result.language ?? null,
processingStatus: 'completed',
};
await encryptRecord('memos', diff);
await memoTable.update(memoId, diff);
// Auto-title: if the user didn't already give the memo a title,
// queue a background task to generate one from the transcript.
// The task is fire-and-forget — the memoro LLM watcher
// (./llm-watcher.svelte.ts) picks up the result reactively and
// writes it back to memo.title. Works on every tier including
// none (regex-based first-sentence fallback).
if (!existing.title?.trim() && transcript.length > 0) {
try {
const taskId = await llmTaskQueue.enqueue(
generateTitleTask,
{ text: transcript, language: existing.language ?? result.language ?? 'de' },
{ refType: 'memo', refId: memoId, priority: 1 }
);
console.info('[memoro] enqueued title task', { taskId, memoId });
} catch (err) {
// Don't let queue failures break the transcription path.
// Worst case the memo stays untitled — the user can still
// rename it manually.
console.warn('[memoro] failed to enqueue title task:', err);
}
}
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
await memoTable.update(memoId, {
processingStatus: 'failed',
metadata: { ...(((await memoTable.get(memoId))?.metadata as object) ?? {}), error: msg },
});
}
},
/** Update a memo's fields. */
async update(
id: string,
data: Partial<Pick<LocalMemo, 'title' | 'intro' | 'transcript' | 'language'>>
) {
const diff: Partial<LocalMemo> = {
...data,
};
// If the user is overwriting the title manually, clear the
// auto-generated titleSource marker so the DetailView stops
// showing "via Mana-Server" — the title is now the user's, not
// the LLM's. We only touch metadata when title was actually in
// the diff so we don't accidentally wipe other metadata fields
// (e.g. STT failure markers) on a non-title update.
if ('title' in data) {
const existing = await memoTable.get(id);
const existingMetadata = (existing?.metadata as Record<string, unknown> | null) ?? {};
if ('titleSource' in existingMetadata) {
const { titleSource: _omit, ...rest } = existingMetadata;
void _omit;
diff.metadata = rest;
}
}
await encryptRecord('memos', diff);
await memoTable.update(id, diff);
},
// Archive ops (delegated to shared factory)
archive: (id: string) => memoArchive.archive(id),
unarchive: (id: string) => memoArchive.unarchive(id),
/**
* Flip a memo's visibility. Public memos surface in the user's
* website embed via the memoro embed-resolver.
*/
async setVisibility(id: string, next: VisibilityLevel) {
const existing = await memoTable.get(id);
if (!existing) throw new Error(`Memo ${id} not found`);
const before: VisibilityLevel = existing.visibility ?? 'space';
if (before === next) return;
const stamp = new Date().toISOString();
await memoTable.update(id, {
visibility: next,
visibilityChangedAt: stamp,
visibilityChangedBy: getEffectiveUserId(),
updatedAt: stamp,
});
emitDomainEvent('VisibilityChanged', 'memoro', 'memos', id, {
recordId: id,
collection: 'memos',
before,
after: next,
});
},
/** Pin a memo. */
async pin(id: string) {
await memoTable.update(id, {
isPinned: true,
});
},
/** Unpin a memo. */
async unpin(id: string) {
await memoTable.update(id, {
isPinned: false,
});
},
/** Soft-delete a memo. */
async delete(id: string) {
await memoArchive.softDelete(id);
MemoroEvents.memoDeleted();
},
};

View file

@ -1,19 +0,0 @@
/**
* Memoro Tags Uses shared global tags + module-specific junction table.
*/
import { db } from '$lib/data/database';
import { createTagLinkOps } from '@mana/shared-stores';
export {
tagMutations,
useAllTags,
getTagById,
getTagsByIds,
getTagColor,
} from '@mana/shared-stores';
export const memoTagOps = createTagLinkOps({
table: () => db.table('memoTags'),
entityIdField: 'memoId',
});

View file

@ -1,21 +0,0 @@
import type { ModuleTool } from '$lib/data/tools/types';
import { memosStore } from './stores/memos.svelte';
export const memoroTools: ModuleTool[] = [
{
name: 'create_memo',
module: 'memoro',
description: 'Erstellt ein neues Sprachmemo / Memo',
parameters: [
{ name: 'title', type: 'string', description: 'Titel', required: false },
{ name: 'transcript', type: 'string', description: 'Text / Transkript', required: true },
],
async execute(params) {
const memo = await memosStore.create({
title: params.title as string | undefined,
transcript: params.transcript as string,
});
return { success: true, data: memo, message: `Memo "${memo.title || 'Neu'}" erstellt` };
},
},
];

View file

@ -1,118 +0,0 @@
/**
* Memoro module types for the unified app.
*/
import type { BaseRecord } from '@mana/local-store';
import type { VisibilityLevel } from '@mana/shared-privacy';
export type ProcessingStatus = 'pending' | 'processing' | 'completed' | 'failed';
export interface LocalMemo extends BaseRecord {
title: string | null;
intro: string | null;
transcript: string | null;
audioDurationMs: number | null;
transcriptModel: string | null;
processingStatus: ProcessingStatus;
isArchived: boolean;
isPinned: boolean;
visibility?: VisibilityLevel;
visibilityChangedAt?: string;
visibilityChangedBy?: string;
blueprintId: string | null;
language: string | null;
location?: Record<string, unknown>;
source?: {
audioPath?: string;
audioDeleted?: boolean;
audioDuration?: number;
transcript?: string;
utterances?: Array<{
text: string;
offset?: number;
duration?: number;
speakerId?: string;
}>;
speakers?: Record<string, unknown>;
speakerMap?: Record<string, string>;
primaryLanguage?: string;
languages?: string[];
processing?: {
transcription?: { status: ProcessingStatus };
headlineAndIntro?: { status: ProcessingStatus };
};
recordingStartedAt?: string;
};
metadata?: Record<string, unknown>;
}
export interface LocalMemory extends BaseRecord {
memoId: string;
title: string;
content: string | null;
metadata?: Record<string, unknown>;
}
export interface LocalMemoTag extends BaseRecord {
memoId: string;
tagId: string;
}
export interface LocalSpace extends BaseRecord {
name: string;
description: string | null;
ownerId: string;
}
export interface LocalSpaceMember extends BaseRecord {
contextSpaceId: string;
userId: string;
role: 'owner' | 'member';
}
export interface LocalMemoSpace extends BaseRecord {
memoId: string;
contextSpaceId: string;
}
// ─── View Types ────────────────────────────────────────────
export interface Memo {
id: string;
title: string | null;
intro: string | null;
transcript: string | null;
audioDurationMs: number | null;
transcriptModel: string | null;
processingStatus: ProcessingStatus;
isArchived: boolean;
isPinned: boolean;
visibility: VisibilityLevel;
language: string | null;
createdAt: string;
updatedAt: string;
}
export interface Memory {
id: string;
memoId: string;
title: string;
content: string | null;
createdAt: string;
updatedAt: string;
}
// NOTE: the Tag type used throughout the memoro module comes from
// `@mana/shared-tags`, not from a local definition. A legacy `Tag`
// interface used to live here but was removed because it diverged
// from the shared shape (missing userId, had isPinned/sortOrder that
// nothing reads). Import Tag from '@mana/shared-tags' everywhere.
export interface Space {
id: string;
name: string;
description: string | null;
ownerId: string;
createdAt: string;
updatedAt: string;
}

View file

@ -1,338 +0,0 @@
<!--
Memoro — DetailView (inline editable overlay)
Memo details with transcript, pin toggle, auto-save on blur.
-->
<script lang="ts">
import { formatDate } from '$lib/i18n/format';
import { useDetailEntity } from '$lib/data/detail-entity.svelte';
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
import DetailViewShell from '$lib/components/DetailViewShell.svelte';
import { memosStore } from '../stores/memos.svelte';
import { llmQueueDb } from '$lib/llm-queue';
import type { QueuedTask } from '@mana/shared-llm';
import type { LlmTier } from '@mana/shared-llm';
import { PushPin } from '@mana/shared-icons';
import { VisibilityPicker, type VisibilityLevel } from '@mana/shared-privacy';
import type { ViewProps } from '$lib/app-registry';
import type { LocalMemo, ProcessingStatus } from '../types';
import { _ } from 'svelte-i18n';
// Map LlmTier → i18n key (mana-server keyed as 'mana_server' since dots are
// reserved path separators). Strings live in memoro.detail_view.title_sources.
const TITLE_SOURCE_KEYS: Record<LlmTier, string> = {
none: 'memoro.detail_view.title_sources.none',
browser: 'memoro.detail_view.title_sources.browser',
'mana-server': 'memoro.detail_view.title_sources.mana_server',
byok: 'memoro.detail_view.title_sources.byok',
cloud: 'memoro.detail_view.title_sources.cloud',
};
function isLlmTier(value: unknown): value is LlmTier {
return value === 'none' || value === 'browser' || value === 'mana-server' || value === 'cloud';
}
let { params, goBack }: ViewProps = $props();
let memoId = $derived(params.memoId as string);
let editTitle = $state('');
let editIntro = $state('');
let editLanguage = $state('');
const detail = useDetailEntity<LocalMemo>({
id: () => memoId,
table: 'memos',
// title, intro, transcript live in the encryption registry — without
// `decrypt: true` the inputs would bind to raw `enc:1:...` ciphertext
// strings instead of plaintext. Pre-existing bug surfaced when the
// LLM auto-title started populating the title field for the first
// time on 2026-04-09; previously the field was always null and the
// transcript was the only encrypted field shown, but no one noticed.
decrypt: true,
onLoad: (val) => {
editTitle = val.title ?? '';
editIntro = val.intro ?? '';
editLanguage = val.language ?? '';
},
});
async function saveField() {
detail.blur();
await memosStore.update(memoId, {
title: editTitle.trim() || null,
intro: editIntro.trim() || null,
language: editLanguage.trim() || null,
});
}
async function togglePin() {
const memo = detail.entity;
if (!memo) return;
if (memo.isPinned) {
await memosStore.unpin(memoId);
} else {
await memosStore.pin(memoId);
}
}
function formatDuration(ms: number | null): string {
if (!ms) return '--:--';
const sec = Math.round(ms / 1000);
const m = Math.floor(sec / 60);
const s = sec % 60;
return `${m}:${String(s).padStart(2, '0')}`;
}
const STATUS_KEYS: Record<ProcessingStatus, string> = {
pending: 'memoro.detail_view.statuses.pending',
processing: 'memoro.detail_view.statuses.processing',
completed: 'memoro.detail_view.statuses.completed',
failed: 'memoro.detail_view.statuses.failed',
};
const statusColors: Record<ProcessingStatus, string> = {
pending: '#eab308',
processing: '#3b82f6',
completed: '#22c55e',
failed: '#ef4444',
};
// Reactive lookup of any LLM queue task tagged with this memo, so the
// UI can show "Titel wird generiert..." while a generateTitleTask is
// pending or running. Returns the most recent task row (any state).
const titleQueueRow = useLiveQueryWithDefault<QueuedTask | null>(async () => {
if (!memoId) return null;
const rows = await llmQueueDb.tasks
.where('[refType+refId]')
.equals(['memo', memoId])
.and((t) => t.taskName === 'common.generateTitle')
.reverse()
.sortBy('enqueuedAt');
return rows[0] ?? null;
}, null);
const titleIsGenerating = $derived(
titleQueueRow.value?.state === 'pending' || titleQueueRow.value?.state === 'running'
);
// Source label for the title — read from memo.metadata.titleSource
// (set by the memoro LLM watcher when it applies an auto-generated
// title, cleared by memosStore.update() when the user types over it).
// Returns a label string or null if the title was manually entered.
const titleSourceLabel = $derived.by(() => {
const memo = detail.entity;
if (!memo) return null;
// Don't show a source label while we're still mid-generation.
if (titleIsGenerating) return null;
// Don't show a source label if the user has typed into the field
// and edits haven't been saved yet — they're about to overwrite.
if (detail.focused) return null;
const metadata = (memo.metadata as Record<string, unknown> | null) ?? {};
const source = metadata.titleSource;
return isLlmTier(source) ? $_(TITLE_SOURCE_KEYS[source]) : null;
});
</script>
<DetailViewShell
entity={detail.entity}
loading={detail.loading}
notFoundLabel={$_('memoro.detail_view.not_found')}
confirmDelete={detail.confirmDelete}
onAskDelete={detail.askDelete}
onCancelDelete={detail.cancelDelete}
confirmDeleteLabel={$_('memoro.detail_view.confirm_delete')}
onConfirmDelete={() =>
detail.deleteWithUndo({
label: $_('memoro.detail_view.toast_deleted'),
delete: () => memosStore.delete(memoId),
goBack,
})}
>
{#snippet body(memo)}
<div class="title-row">
<input
class="title-input"
bind:value={editTitle}
onfocus={detail.focus}
onblur={saveField}
placeholder={titleIsGenerating && !editTitle
? $_('memoro.detail_view.placeholder_title_generating')
: $_('memoro.detail_view.placeholder_title_idle')}
/>
<button class="pin-btn" class:pinned={memo.isPinned} onclick={togglePin}>
<PushPin size={16} />
</button>
</div>
{#if titleSourceLabel}
<div class="source-label title-source-label">{titleSourceLabel}</div>
{/if}
<div class="properties">
<div class="prop-row">
<span class="prop-label">{$_('memoro.detail_view.label_status')}</span>
<span class="prop-value" style="color: {statusColors[memo.processingStatus]}">
{$_(STATUS_KEYS[memo.processingStatus])}
</span>
</div>
<div class="prop-row">
<span class="prop-label">{$_('memoro.detail_view.label_duration')}</span>
<span class="prop-value">{formatDuration(memo.audioDurationMs)}</span>
</div>
<div class="prop-row">
<span class="prop-label">{$_('memoro.detail_view.label_language')}</span>
<input
class="prop-input"
bind:value={editLanguage}
onfocus={detail.focus}
onblur={saveField}
placeholder={$_('memoro.detail_view.placeholder_language')}
/>
</div>
<div class="prop-row">
<span class="prop-label">{$_('memoro.detail_view.label_visibility')}</span>
<VisibilityPicker
level={memo.visibility ?? 'space'}
onChange={(next: VisibilityLevel) => memosStore.setVisibility(memoId, next)}
disabledLevels={['unlisted']}
/>
</div>
</div>
<div class="section">
<span class="section-label">{$_('memoro.detail_view.section_summary')}</span>
<textarea
class="description-input"
bind:value={editIntro}
onfocus={detail.focus}
onblur={saveField}
placeholder={$_('memoro.detail_view.placeholder_summary')}
rows={2}
></textarea>
</div>
<div class="section">
<span class="section-label">{$_('memoro.detail_view.section_transcript')}</span>
{#if memo.processingStatus === 'processing'}
<div class="transcript transcript-loading">
<span class="loading-dot"></span>
<span class="loading-dot"></span>
<span class="loading-dot"></span>
<span>{$_('memoro.detail_view.transcribing')}</span>
</div>
{:else if memo.processingStatus === 'failed'}
<div class="transcript transcript-failed">
{$_('memoro.detail_view.transcript_failed')}
</div>
{:else if memo.transcript}
<div class="transcript">{memo.transcript}</div>
<div class="source-label">{$_('memoro.detail_view.transcript_source')}</div>
{:else}
<div class="transcript transcript-empty">{$_('memoro.detail_view.transcript_empty')}</div>
{/if}
</div>
<div class="meta">
<span
>{$_('memoro.detail_view.meta_created', {
values: { date: formatDate(new Date(memo.createdAt ?? '')) },
})}</span
>
{#if memo.updatedAt}
<span
>{$_('memoro.detail_view.meta_updated', {
values: { date: formatDate(new Date(memo.updatedAt)) },
})}</span
>
{/if}
</div>
{/snippet}
</DetailViewShell>
<style>
.pin-btn {
border: none;
background: transparent;
cursor: pointer;
padding: 0.125rem;
color: hsl(var(--color-muted-foreground));
flex-shrink: 0;
transition:
color 0.15s,
transform 0.15s;
}
.pin-btn:hover {
color: hsl(var(--color-muted-foreground));
}
.pin-btn.pinned {
color: hsl(var(--color-warning));
}
.transcript {
font-size: 0.8125rem;
color: hsl(var(--color-muted-foreground));
line-height: 1.5;
padding: 0.5rem;
border-radius: 0.375rem;
background: hsl(var(--color-surface-hover));
white-space: pre-wrap;
max-height: 12rem;
overflow-y: auto;
}
.transcript-loading {
display: flex;
align-items: center;
gap: 0.375rem;
font-style: italic;
}
.transcript-empty {
font-style: italic;
opacity: 0.7;
}
.transcript-failed {
color: hsl(var(--color-destructive, 0 84% 60%));
}
.loading-dot {
display: inline-block;
width: 0.375rem;
height: 0.375rem;
border-radius: 50%;
background: currentColor;
opacity: 0.4;
animation: loadingPulse 1.2s ease-in-out infinite;
}
.loading-dot:nth-child(2) {
animation-delay: 0.15s;
}
.loading-dot:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes loadingPulse {
0%,
80%,
100% {
opacity: 0.4;
transform: scale(0.85);
}
40% {
opacity: 1;
transform: scale(1);
}
}
.source-label {
margin-top: 0.375rem;
font-size: 0.6875rem;
color: hsl(var(--color-muted-foreground));
opacity: 0.7;
font-style: italic;
}
.title-source-label {
/* Sit visually right under the title input rather than the
transcript box — needs a tighter top gap and a small left
indent so it lines up with the text inside the input. */
margin-top: 0.125rem;
margin-bottom: 0.5rem;
padding-left: 0.125rem;
}
</style>

View file

@ -33,7 +33,6 @@ import type { LocalComicStory } from '$lib/modules/comic/types';
import type { LocalHabit, LocalHabitLog } from '$lib/modules/habits/types';
import type { LocalQuiz } from '$lib/modules/quiz/types';
import type { LocalSocialEvent } from '$lib/modules/events/types';
import type { LocalMemo } from '$lib/modules/memoro/types';
import type { LocalDeck as LocalPresiDeck } from '$lib/modules/presi/types';
import type { LocalAugurEntry } from '$lib/modules/augur/types';
import type { LocalTimeBlock } from '$lib/data/time-blocks/types';
@ -83,9 +82,6 @@ export async function resolveEmbed(props: ModuleEmbedProps): Promise<ResolvedEmb
case 'events.socialEvents':
items = await resolveSocialEvents(props);
break;
case 'memoro.memos':
items = await resolveMemos(props);
break;
// 'cards.decks' source: dekommissioniert 2026-05-08 (Cards
// eigenständig auf cardecky.mana.how, kein Local-Dexie-Embed
// mehr). Falls Public-Cardecky-Decks später website-embeddable
@ -741,53 +737,6 @@ async function resolveSocialEvents(props: ModuleEmbedProps): Promise<EmbedItem[]
});
}
/**
* Memoro: voice-memo teaser. Returns memos flipped to 'public' with
* the first sentence of the intro as subtitle.
*
* Whitelist: title + intro (first 140 chars) + audio duration. The
* full transcript, source-audio paths, and per-utterance speaker
* data all stay private those are the user's words verbatim and
* have a much stronger privacy weight than a curated headline.
*/
async function resolveMemos(_props: ModuleEmbedProps): Promise<EmbedItem[]> {
let memos = await db.table<LocalMemo>('memos').toArray();
memos = memos.filter(
(m) => !m.deletedAt && !m.isArchived && canEmbedOnWebsite(m.visibility ?? 'private')
);
if (memos.length === 0) return [];
const decrypted = (await decryptRecords('memos', memos)) as LocalMemo[];
// Pinned first, then newest.
decrypted.sort((a, b) => {
const pinA = a.isPinned ? 0 : 1;
const pinB = b.isPinned ? 0 : 1;
if (pinA !== pinB) return pinA - pinB;
return (b.updatedAt ?? '').localeCompare(a.updatedAt ?? '');
});
function durationLabel(ms: number | null): string | null {
if (!ms || ms <= 0) return null;
const seconds = Math.round(ms / 1000);
if (seconds < 60) return `${seconds}s`;
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return s === 0 ? `${m} Min` : `${m}:${String(s).padStart(2, '0')} Min`;
}
return decrypted.map((m) => {
const intro = (m.intro ?? '').trim().slice(0, 140);
const dur = durationLabel(m.audioDurationMs);
const subtitleParts = [intro || null, dur].filter((x): x is string => Boolean(x));
return {
title: (m.title ?? '').trim() || 'Memo',
subtitle: subtitleParts.length > 0 ? subtitleParts.join(' · ') : undefined,
};
});
}
// resolveCardDecks: dekommissioniert 2026-05-08, Cards lebt eigenständig
// auf cardecky.mana.how. Public-Deck-Embeds für Cardecky kommen später
// über die Cardecky-API.

View file

@ -24,7 +24,6 @@ const SPLIT_APP_ID_LIST = [
'questions',
'uload',
'calc',
'memoro',
'places',
'automations',
'playground',

View file

@ -71,10 +71,6 @@
import { getEffectiveTier } from '$lib/data/scope';
import { useLocalStt } from '$lib/components/voice/use-local-stt.svelte';
import { Microphone, Stop } from '@mana/shared-icons';
import {
startMemoroLlmWatcher,
stopMemoroLlmWatcher,
} from '$lib/modules/memoro/llm-watcher.svelte';
import { createUnifiedSync, restoreClientIdFromDexie } from '$lib/data/sync';
import { cleanupOrphanMigrationFlags } from '$lib/data/migrations-cleanup';
import { bootstrapSingletons } from '$lib/data/bootstrap-singletons';
@ -587,7 +583,6 @@
startFeedbackToaster();
initByok();
startLlmQueue();
startMemoroLlmWatcher();
// dashboardStore only drives /dashboard — safe to defer; other
// routes don't read from it on first paint.
void dashboardStore.initialize();
@ -761,7 +756,6 @@
// will finish in the background and the next page session will
// pick up where we left off.
void stopLlmQueue();
stopMemoroLlmWatcher();
});
// ── Search / Spotlight ───────────────────────────────────

View file

@ -1,26 +0,0 @@
<script lang="ts">
import { setContext } from 'svelte';
import type { Snippet } from 'svelte';
import {
useAllMemos,
useArchivedMemos,
useAllTags,
useAllMemoTags,
} from '$lib/modules/memoro/queries';
let { children }: { children: Snippet } = $props();
// Live queries — auto-update when IndexedDB changes
const allMemos = useAllMemos();
const archivedMemos = useArchivedMemos();
const allTags = useAllTags();
const allMemoTags = useAllMemoTags();
// Provide data to child components via Svelte context
setContext('memos', allMemos);
setContext('archivedMemos', archivedMemos);
setContext('tags', allTags);
setContext('memoTags', allMemoTags);
</script>
{@render children()}

View file

@ -1,286 +0,0 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { getContext } from 'svelte';
import { memosStore } from '$lib/modules/memoro/stores/memos.svelte';
import VoiceCaptureBar from '$lib/components/voice/VoiceCaptureBar.svelte';
import {
filterBySearch,
filterByTag,
getTagsForMemo,
formatDuration,
getStatusLabel,
} from '$lib/modules/memoro/queries';
import type { Memo, LocalMemoTag } from '$lib/modules/memoro/types';
import type { Tag } from '@mana/shared-tags';
import {
Plus,
MagnifyingGlass,
PushPin,
Archive,
Microphone,
Tag as TagIcon,
} from '@mana/shared-icons';
import { RoutePage } from '$lib/components/shell';
const memosCtx: { readonly value: Memo[] } = getContext('memos');
const tagsCtx: { readonly value: Tag[] } = getContext('tags');
const memoTagsCtx: { readonly value: LocalMemoTag[] } = getContext('memoTags');
let searchQuery = $state('');
let selectedTagId = $state<string | null>(null);
let filtered = $derived(() => {
let result = filterBySearch(memosCtx.value, searchQuery);
if (selectedTagId) {
result = filterByTag(result, memoTagsCtx.value, selectedTagId);
}
return result;
});
function handleMemoClick(id: string) {
goto(`/memoro/${id}`);
}
async function handleNewMemo() {
const memo = await memosStore.create({});
goto(`/memoro/${memo.id}`);
}
// ── Voice capture ─────────────────────────────────────────
async function handleVoiceComplete(blob: Blob, durationMs: number) {
const memo = await memosStore.createFromVoice(blob, durationMs, 'de');
goto(`/memoro/${memo.id}`);
}
async function handlePin(e: Event, id: string, isPinned: boolean) {
e.stopPropagation();
if (isPinned) {
await memosStore.unpin(id);
} else {
await memosStore.pin(id);
}
}
async function handleArchive(e: Event, id: string) {
e.stopPropagation();
await memosStore.archive(id);
}
function getMemoTags(memoId: string): Tag[] {
return getTagsForMemo(tagsCtx.value, memoTagsCtx.value, memoId);
}
function formatDate(dateStr: string): string {
const date = new Date(dateStr);
const now = new Date();
const diff = now.getTime() - date.getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (days === 0) return 'Heute';
if (days === 1) return 'Gestern';
if (days < 7) return `vor ${days} Tagen`;
return date.toLocaleDateString('de-DE', { day: '2-digit', month: 'short' });
}
</script>
<svelte:head>
<title>Memoro - Mana</title>
</svelte:head>
<RoutePage appId="memoro">
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Memoro</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{memosCtx.value.length} Memos
</p>
</div>
<div class="flex items-center gap-2">
<a
href="/memoro/tags"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))]"
>
<TagIcon size={16} />
Tags
</a>
<div class="w-64">
<VoiceCaptureBar
idleLabel="Memo aufnehmen"
feature="memoro-voice-capture"
reason="Sprach-Memos werden verschlüsselt gespeichert. Dafür brauchst du ein Mana-Konto."
onComplete={handleVoiceComplete}
/>
</div>
<button
onclick={handleNewMemo}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
<Plus size={20} />
Neues Memo
</button>
</div>
</div>
<!-- Search -->
<div class="relative">
<MagnifyingGlass
size={18}
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<input
type="text"
placeholder="Memos durchsuchen..."
bind:value={searchQuery}
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] py-2.5 pl-10 pr-4 text-sm text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<!-- Tag Filter -->
{#if tagsCtx.value.length > 0}
<div class="flex flex-wrap gap-2">
<button
onclick={() => (selectedTagId = null)}
class="rounded-full px-3 py-1 text-xs font-medium transition-colors {selectedTagId ===
null
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'bg-[hsl(var(--color-muted))] text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted)/0.8)]'}"
>
Alle
</button>
{#each tagsCtx.value as tag (tag.id)}
<button
onclick={() => (selectedTagId = selectedTagId === tag.id ? null : tag.id)}
class="rounded-full px-3 py-1 text-xs font-medium transition-colors {selectedTagId ===
tag.id
? 'text-white'
: 'bg-[hsl(var(--color-muted))] text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted)/0.8)]'}"
style={selectedTagId === tag.id && tag.color ? `background-color: ${tag.color}` : ''}
>
{tag.name}
</button>
{/each}
</div>
{/if}
<!-- Memos List -->
{#if memosCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<div
class="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-[hsl(var(--color-primary)/0.1)]"
>
<Microphone size={32} weight="duotone" class="text-[hsl(var(--color-primary))]" />
</div>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Erstelle dein erstes Memo
</h2>
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Nimm Gedanken auf oder schreibe sie direkt auf.
</p>
<button
onclick={handleNewMemo}
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neues Memo
</button>
</div>
{:else}
<div class="space-y-2">
{#each filtered() as memo (memo.id)}
<div
role="button"
tabindex="0"
onclick={() => handleMemoClick(memo.id)}
onkeydown={(e) => e.key === 'Enter' && handleMemoClick(memo.id)}
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-colors hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
{#if memo.isPinned}
<PushPin
size={14}
weight="fill"
class="shrink-0 text-[hsl(var(--color-primary))]"
/>
{/if}
<h3 class="truncate font-semibold text-[hsl(var(--color-foreground))]">
{memo.title || 'Unbenanntes Memo'}
</h3>
</div>
{#if memo.intro}
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{memo.intro}
</p>
{:else if memo.transcript}
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{memo.transcript}
</p>
{/if}
</div>
<div
class="ml-4 flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100"
>
<button
onclick={(e) => handlePin(e, memo.id, memo.isPinned)}
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title={memo.isPinned ? 'Loslosen' : 'Anpinnen'}
>
<PushPin size={16} weight={memo.isPinned ? 'fill' : 'regular'} />
</button>
<button
onclick={(e) => handleArchive(e, memo.id)}
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title="Archivieren"
>
<Archive size={16} />
</button>
</div>
</div>
<!-- Footer -->
<div class="mt-3 flex items-center gap-3">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(memo.createdAt)}
</span>
{#if memo.audioDurationMs}
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDuration(memo.audioDurationMs)}
</span>
{/if}
{#if memo.processingStatus !== 'completed'}
<span
class="rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--color-muted-foreground))]"
>
{getStatusLabel(memo.processingStatus)}
</span>
{/if}
<!-- Tags -->
{#each getMemoTags(memo.id) as tag (tag.id)}
<span
class="rounded-full px-2 py-0.5 text-[10px] font-medium text-white"
style="background-color: {tag.color || 'hsl(var(--color-muted))'}"
>
{tag.name}
</span>
{/each}
</div>
</div>
{/each}
</div>
{/if}
<!-- Archive link -->
<div class="pt-2">
<a
href="/memoro/archive"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<Archive size={16} />
Archiv anzeigen
</a>
</div>
</div>
</RoutePage>

View file

@ -1,302 +0,0 @@
<script lang="ts">
import { formatDate } from '$lib/i18n/format';
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { getContext } from 'svelte';
import { memosStore } from '$lib/modules/memoro/stores/memos.svelte';
import { memoriesStore } from '$lib/modules/memoro/stores/memories.svelte';
import { memoTagOps } from '$lib/modules/memoro/stores/tags.svelte';
import {
useMemoriesByMemo,
getTagsForMemo,
formatDuration,
getStatusLabel,
} from '$lib/modules/memoro/queries';
import type { Memo, Memory, LocalMemoTag } from '$lib/modules/memoro/types';
import type { Tag } from '@mana/shared-tags';
import { RoutePage } from '$lib/components/shell';
import {
ArrowLeft,
Trash,
PushPin,
Archive,
PencilSimple,
Check,
X,
Plus,
Tag as TagIcon,
} from '@mana/shared-icons';
const memosCtx: { readonly value: Memo[] } = getContext('memos');
const tagsCtx: { readonly value: Tag[] } = getContext('tags');
const memoTagsCtx: { readonly value: LocalMemoTag[] } = getContext('memoTags');
const memoId = $derived($page.params.id ?? '');
const memo = $derived(memosCtx.value.find((m) => m.id === memoId));
// Live query for memories of this memo
const memoriesQuery = $derived(useMemoriesByMemo(memoId));
let memories = $derived(memoriesQuery.value);
let memoTags = $derived(getTagsForMemo(tagsCtx.value, memoTagsCtx.value, memoId));
let isEditingTitle = $state(false);
let editTitle = $state('');
let showTagPicker = $state(false);
function startEditTitle() {
editTitle = memo?.title ?? '';
isEditingTitle = true;
}
async function saveTitle() {
if (editTitle.trim()) {
await memosStore.update(memoId, { title: editTitle.trim() });
}
isEditingTitle = false;
}
async function togglePin() {
if (!memo) return;
if (memo.isPinned) {
await memosStore.unpin(memoId);
} else {
await memosStore.pin(memoId);
}
}
async function handleArchive() {
await memosStore.archive(memoId);
goto('/memoro');
}
async function handleDelete() {
if (confirm('Memo wirklich loschen?')) {
await memosStore.delete(memoId);
goto('/memoro');
}
}
async function handleAddTag(tagId: string) {
await memoTagOps.addTag(memoId, tagId);
showTagPicker = false;
}
async function handleRemoveTag(tagId: string) {
await memoTagOps.removeTag(memoId, tagId);
}
// Available tags (not already assigned)
let availableTags = $derived(tagsCtx.value.filter((t) => !memoTags.some((mt) => mt.id === t.id)));
</script>
<svelte:head>
<title>{memo?.title || 'Memo'} - Memoro - Mana</title>
</svelte:head>
<RoutePage appId="memoro" backHref="/memoro" title="Memo">
{#if !memo}
<div class="flex flex-col items-center justify-center py-16">
<p class="mb-4 text-[hsl(var(--color-muted-foreground))]">Memo nicht gefunden</p>
<a href="/memoro" class="text-sm text-[hsl(var(--color-primary))] hover:underline">
Zuruck zu Memoro
</a>
</div>
{:else}
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<a
href="/memoro"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div class="min-w-0 flex-1">
{#if isEditingTitle}
<div class="flex items-center gap-2">
<input
type="text"
bind:value={editTitle}
onkeydown={(e) => e.key === 'Enter' && saveTitle()}
class="flex-1 rounded border border-[hsl(var(--color-border))] bg-transparent px-2 py-1 text-xl font-bold focus:outline-none focus:ring-1 focus:ring-[hsl(var(--color-primary))]"
/>
<button onclick={saveTitle} class="text-[hsl(var(--color-primary))]">
<Check size={18} />
</button>
<button
onclick={() => (isEditingTitle = false)}
class="text-[hsl(var(--color-muted-foreground))]"
>
<X size={18} />
</button>
</div>
{:else}
<button onclick={startEditTitle} class="group flex items-center gap-2 text-left">
<h1 class="text-xl font-bold text-[hsl(var(--color-foreground))]">
{memo.title || 'Unbenanntes Memo'}
</h1>
<PencilSimple
size={16}
class="shrink-0 text-[hsl(var(--color-muted-foreground))] opacity-0 group-hover:opacity-100"
/>
</button>
{/if}
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{formatDate(new Date(memo.createdAt), {
day: '2-digit',
month: 'long',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
})}
{#if memo.audioDurationMs}
&middot; {formatDuration(memo.audioDurationMs)}
{/if}
</p>
</div>
</div>
<div class="flex items-center gap-1">
<button
onclick={togglePin}
class="rounded-lg p-1.5 transition-colors {memo.isPinned
? 'text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'}"
title={memo.isPinned ? 'Loslosen' : 'Anpinnen'}
>
<PushPin size={18} weight={memo.isPinned ? 'fill' : 'regular'} />
</button>
<button
onclick={handleArchive}
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
title="Archivieren"
>
<Archive size={18} />
</button>
<button
onclick={handleDelete}
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Loschen"
>
<Trash size={18} />
</button>
</div>
</div>
<!-- Status -->
{#if memo.processingStatus !== 'completed'}
<div
class="rounded-lg bg-[hsl(var(--color-muted))] px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>
Status: {getStatusLabel(memo.processingStatus)}
</div>
{/if}
<!-- Tags -->
<div class="flex flex-wrap items-center gap-2">
{#each memoTags as tag (tag.id)}
<span
class="inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs font-medium text-white"
style="background-color: {tag.color || 'hsl(var(--color-muted))'}"
>
{tag.name}
<button
onclick={() => handleRemoveTag(tag.id)}
class="ml-0.5 rounded-full hover:bg-muted/20"
>
<X size={12} />
</button>
</span>
{/each}
<div class="relative">
<button
onclick={() => (showTagPicker = !showTagPicker)}
class="flex items-center gap-1 rounded-full border border-dashed border-[hsl(var(--color-border))] px-2.5 py-1 text-xs text-[hsl(var(--color-muted-foreground))] hover:border-[hsl(var(--color-primary))] hover:text-[hsl(var(--color-primary))]"
>
<TagIcon size={12} />
Tag hinzufugen
</button>
{#if showTagPicker && availableTags.length > 0}
<div
class="absolute left-0 top-full z-10 mt-1 w-48 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-2 shadow-lg"
>
{#each availableTags as tag (tag.id)}
<button
onclick={() => handleAddTag(tag.id)}
class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-[hsl(var(--color-muted))]"
>
<span class="h-3 w-3 rounded-full" style="background-color: {tag.color || '#888'}"
></span>
{tag.name}
</button>
{/each}
</div>
{/if}
</div>
</div>
<!-- Intro -->
{#if memo.intro}
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<h2
class="mb-2 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Zusammenfassung
</h2>
<p class="text-[hsl(var(--color-foreground))]">{memo.intro}</p>
</div>
{/if}
<!-- Transcript -->
{#if memo.transcript}
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<h2
class="mb-2 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Transkript
</h2>
<p
class="whitespace-pre-wrap text-sm leading-relaxed text-[hsl(var(--color-foreground))]"
>
{memo.transcript}
</p>
</div>
{/if}
<!-- Memories -->
<div>
<h2
class="mb-3 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Erinnerungen ({memories.length})
</h2>
{#if memories.length === 0}
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Noch keine Erinnerungen fur dieses Memo.
</p>
{:else}
<div class="space-y-3">
{#each memories as memory (memory.id)}
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="font-medium text-[hsl(var(--color-foreground))]">{memory.title}</h3>
{#if memory.content}
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
{memory.content}
</p>
{/if}
</div>
{/each}
</div>
{/if}
</div>
</div>
{/if}
</RoutePage>

View file

@ -1,111 +0,0 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { getContext } from 'svelte';
import { memosStore } from '$lib/modules/memoro/stores/memos.svelte';
import type { Memo } from '$lib/modules/memoro/types';
import { ArrowLeft, ArrowCounterClockwise, Trash, Microphone } from '@mana/shared-icons';
import { RoutePage } from '$lib/components/shell';
const archivedCtx: { readonly value: Memo[] } = getContext('archivedMemos');
async function handleUnarchive(e: Event, id: string) {
e.stopPropagation();
await memosStore.unarchive(id);
}
async function handleDelete(e: Event, id: string) {
e.stopPropagation();
if (confirm('Memo endgultig loschen?')) {
await memosStore.delete(id);
}
}
function handleClick(id: string) {
goto(`/memoro/${id}`);
}
function formatDate(dateStr: string): string {
return new Date(dateStr).toLocaleDateString('de-DE', {
day: '2-digit',
month: 'short',
year: 'numeric',
});
}
</script>
<svelte:head>
<title>Archiv - Memoro - Mana</title>
</svelte:head>
<RoutePage appId="memoro" backHref="/memoro">
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center gap-3">
<a
href="/memoro"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Archiv</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{archivedCtx.value.length} archivierte Memos
</p>
</div>
</div>
{#if archivedCtx.value.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<Microphone size={48} class="mb-4 text-[hsl(var(--color-muted-foreground))]" />
<p class="text-[hsl(var(--color-muted-foreground))]">Keine archivierten Memos</p>
</div>
{:else}
<div class="space-y-2">
{#each archivedCtx.value as memo (memo.id)}
<div
role="button"
tabindex="0"
onclick={() => handleClick(memo.id)}
onkeydown={(e) => e.key === 'Enter' && handleClick(memo.id)}
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-colors hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<div class="min-w-0 flex-1">
<h3 class="truncate font-medium text-[hsl(var(--color-foreground))]">
{memo.title || 'Unbenanntes Memo'}
</h3>
{#if memo.intro}
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-1">
{memo.intro}
</p>
{/if}
<p class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(memo.updatedAt)}
</p>
</div>
<div
class="ml-4 flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100"
>
<button
onclick={(e) => handleUnarchive(e, memo.id)}
class="rounded p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-primary))]"
title="Wiederherstellen"
>
<ArrowCounterClockwise size={16} />
</button>
<button
onclick={(e) => handleDelete(e, memo.id)}
class="rounded p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Endgultig loschen"
>
<Trash size={16} />
</button>
</div>
</div>
</div>
{/each}
</div>
{/if}
</div>
</RoutePage>

View file

@ -1,217 +0,0 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { getContext } from 'svelte';
import { tagMutations } from '$lib/modules/memoro/stores/tags.svelte';
import type { Tag } from '@mana/shared-tags';
import { RoutePage } from '$lib/components/shell';
import {
ArrowLeft,
Plus,
Trash,
PencilSimple,
PushPin,
Check,
X,
Tag as TagIcon,
} from '@mana/shared-icons';
const tagsCtx: { readonly value: Tag[] } = getContext('tags');
let showCreateForm = $state(false);
let editingId = $state<string | null>(null);
let formName = $state('');
let formColor = $state('#3b82f6');
const COLORS = [
'#3b82f6',
'#8b5cf6',
'#ec4899',
'#f97316',
'#10b981',
'#06b6d4',
'#ef4444',
'#eab308',
];
function openCreateForm() {
editingId = null;
formName = '';
formColor = '#3b82f6';
showCreateForm = true;
}
function openEditForm(tag: Tag) {
editingId = tag.id;
formName = tag.name;
formColor = tag.color || '#3b82f6';
showCreateForm = true;
}
async function handleSubmit() {
if (!formName.trim()) return;
if (editingId) {
await tagMutations.updateTag(editingId, { name: formName.trim(), color: formColor });
} else {
await tagMutations.createTag({ name: formName.trim(), color: formColor });
}
showCreateForm = false;
}
async function handleDelete(id: string) {
if (confirm('Tag wirklich loschen?')) {
await tagMutations.deleteTag(id);
}
}
</script>
<svelte:head>
<title>Tags - Memoro - Mana</title>
</svelte:head>
<RoutePage appId="memoro" backHref="/memoro">
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<a
href="/memoro"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Tags</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{tagsCtx.value.length} Tags
</p>
</div>
</div>
<button
onclick={openCreateForm}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Plus size={16} />
Neuer Tag
</button>
</div>
{#if tagsCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<TagIcon size={48} class="mb-4 text-[hsl(var(--color-muted-foreground))]" />
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">Keine Tags</h2>
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Erstelle Tags, um deine Memos zu organisieren.
</p>
<button
onclick={openCreateForm}
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neuer Tag
</button>
</div>
{:else}
<div class="space-y-2">
{#each tagsCtx.value as tag (tag.id)}
<div
class="group flex items-center gap-3 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<span
class="h-4 w-4 shrink-0 rounded-full"
style="background-color: {tag.color || '#888'}"
></span>
<span class="flex-1 font-medium text-[hsl(var(--color-foreground))]">{tag.name}</span>
<div
class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100"
>
<button
onclick={() => openEditForm(tag)}
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<PencilSimple size={16} />
</button>
<button
onclick={() => handleDelete(tag.id)}
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
>
<Trash size={16} />
</button>
</div>
</div>
{/each}
</div>
{/if}
</div>
<!-- Create/Edit Form Modal -->
{#if showCreateForm}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
<div
class="w-full max-w-sm rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-semibold text-[hsl(var(--color-foreground))]">
{editingId ? 'Tag bearbeiten' : 'Neuer Tag'}
</h2>
<button
onclick={() => (showCreateForm = false)}
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<X size={20} />
</button>
</div>
<form
onsubmit={(e) => {
e.preventDefault();
handleSubmit();
}}
class="space-y-4"
>
<div>
<label for="tag-name" class="mb-1 block text-sm font-medium">Name</label>
<input
id="tag-name"
type="text"
bind:value={formName}
required
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<span class="mb-1 block text-sm font-medium">Farbe</span>
<div class="flex gap-2">
{#each COLORS as color}
<button
type="button"
aria-label="Farbe wählen"
onclick={() => (formColor = color)}
class="h-7 w-7 rounded-full border-2 transition-transform {formColor === color
? 'scale-110 border-[hsl(var(--color-foreground))]'
: 'border-transparent hover:scale-105'}"
style="background-color: {color}"
></button>
{/each}
</div>
</div>
<div class="flex justify-end gap-3 pt-2">
<button
type="button"
onclick={() => (showCreateForm = false)}
class="px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
type="submit"
disabled={!formName.trim()}
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{editingId ? $_('common.save') : $_('common.create')}
</button>
</div>
</form>
</div>
</div>
{/if}
</RoutePage>

View file

@ -1 +0,0 @@
*.env.deploy

View file

@ -1,63 +0,0 @@
# Memoro
AI-powered voice recording + memo management. Memoro is a **hybrid**: its
frontend was consolidated into the unified Mana web app, but its backend
was kept as standalone services (Hono + Supabase) because of the
audio-processing pipeline and the legacy Supabase Storage bucket layout.
## Where things live
| Surface | Path | Notes |
|---------|------|-------|
| **Web frontend** (local-first, in unified Mana app) | [`apps/mana/apps/web/src/lib/modules/memoro/`](../mana/apps/web/src/lib/modules/memoro/) | Same module pattern as every other module — Dexie collections, Svelte 5 stores, runes UI. Reachable via `/memoro` route in mana.how. |
| **Native mobile app** | [`apps/memoro/apps/mobile/`](apps/mobile/) | React Native + Expo SDK 55. Talks directly to `memoro-server` (NOT to mana.how). Build via EAS, see `apps/mobile/eas.json`. |
| **Backend compute** | [`apps/memoro/apps/server/`](apps/server/) (`@memoro/server`) | Hono + Bun. Handles memo CRUD, transcription callbacks, spaces, invites, credits, settings, cleanup, meetings. **Still uses Supabase** for some legacy state. Deployed as `memoro-server` container. |
| **Audio processing** | [`apps/memoro/apps/audio-server/`](apps/audio-server/) | Separate Hono+Bun service for audio uploads + transcoding. Deployed as `memoro-audio-server` container. |
| **Landing page** | [`apps/memoro/apps/landing/`](apps/landing/) | Astro static landing → Cloudflare Pages |
## Why memoro is not (yet) in `apps/api`
Most consolidated products migrated their compute routes into
`apps/api/src/modules/{name}/`. Memoro stayed standalone because:
1. **Audio pipeline.** The audio-server runs background transcoding/
upload jobs that don't fit the request-response shape of `apps/api`.
2. **Legacy Supabase coupling.** Memo and storage records still live
in Supabase tables (`storage.objects`, RLS policies on `memos`).
Migrating to mana_platform was descoped in the consolidation sprint.
3. **Three deploy targets.** `memoro-server`, `memoro-audio-server`,
and the mobile app all need to coordinate. Easier to evolve as one
unit while migration is in flight.
A future cleanup item is to either fold the routes into `apps/api`
(once Supabase is gone) or document this exception explicitly in the
root architecture overview.
## Production deployment
Both backends are part of `docker-compose.macmini.yml`:
```
memoro-server (apps/memoro/apps/server) — main backend
memoro-audio-server (apps/memoro/apps/audio-server) — audio worker
```
The mobile app builds via EAS — not part of the monorepo CI.
## Known issues / cleanup items
- **`@mana/notify-client` is imported by `apps/memoro/apps/server/src/lib/notify.ts:6` but NOT declared as a dependency** in `apps/memoro/apps/server/package.json`. Currently works via hoisted node_modules but should either be added as a workspace dep or replaced with a direct call to `mana-notify`. Tracked in `docs/REFACTORING_AUDIT_2026_04.md` items #29.
- **`apps/memoro/apps/server` still pulls `@supabase/supabase-js`** — not a bug, but flagged as a dependency to remove once Supabase migration completes.
- **No `apps/memoro/apps/web`** — was removed during the consolidation. The old SvelteKit "companion web app" lives now under `apps/mana/apps/web/src/lib/modules/memoro/`.
## For monorepo-wide patterns
See [root `CLAUDE.md`](../../CLAUDE.md) for the overall architecture and
[`apps/mana/CLAUDE.md`](../mana/CLAUDE.md) for the unified web app's
module pattern (which the memoro frontend follows).
The previous 459-line "Memoro repository overview" describing memoro as
a standalone monorepo with `mana-middleware` and a bespoke auth bridge
was deleted in the audit cleanup of 2026-04-09. It pre-dated the
integration into the Mana monorepo and described an architecture that
no longer exists. Pre-consolidation reference is in git history.

View file

@ -1,373 +0,0 @@
# Memoro
**AI-powered voice recording and memo management platform** that transforms audio recordings into structured, searchable content using artificial intelligence.
![Platform](https://img.shields.io/badge/platform-iOS%20%7C%20Android%20%7C%20Web-blue)
![React Native](https://img.shields.io/badge/React%20Native-0.81.4-61dafb)
![Expo](https://img.shields.io/badge/Expo-54.0.0-000020)
![SvelteKit](https://img.shields.io/badge/SvelteKit-2.x-ff3e00)
![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178c6)
## 📱 What is Memoro?
Memoro is a cross-platform application that combines voice recording, AI processing, and collaborative features to help individuals and teams capture, organize, and analyze spoken content. Record meetings, interviews, lectures, or personal notes, and let AI transform them into structured, actionable insights.
### Key Features
**High-Quality Audio Recording** - Background recording with pause/resume support
🤖 **AI-Powered Analysis** - Transform recordings using customizable Blueprints and Prompts
👥 **Collaborative Spaces** - Share and organize memos within team workspaces
🌍 **32 Languages** - Full internationalization with automatic language detection
🎨 **4 Theme Variants** - Light/dark mode with Nature, Ocean, Stone, and Lume themes
💰 **Credit System** - Transparent Mana-based pricing for AI operations
🔒 **Enterprise Security** - Row-level security with JWT authentication
📊 **Rich Analytics** - Track usage, productivity, and team insights
## 🏗 Monorepo Structure
```
memoro_app/
├── apps/
│ ├── mobile/ # React Native + Expo app (iOS & Android native)
│ └── web/ # SvelteKit web application
├── CLAUDE.md # Development guidance for Claude Code
└── README.md # This file
```
Both applications share the same Supabase backend for seamless data synchronization.
## 🚀 Quick Start
### Prerequisites
- **Node.js** 18 or higher
- **npm** or **pnpm**
- **Expo CLI** (for mobile development)
- **iOS Simulator** (macOS only) or **Android Emulator**
- **Supabase Account** (for backend services)
### Installation
```bash
# Clone the repository
git clone <repository-url>
cd memoro_app
# Install mobile app dependencies
cd apps/mobile
npm install
# Install web app dependencies
cd ../web
npm install
```
### Environment Setup
Both apps require environment variables. Copy the example files and fill in your credentials:
```bash
# Mobile app
cd apps/mobile
cp .env.dev.example .env.dev
cp .env.prod.example .env.prod
# Edit .env.dev and .env.prod with your Supabase and API credentials
# Web app
cd apps/web
cp .env.example .env
# Edit .env with your Supabase credentials
```
**Required Environment Variables:**
- `EXPO_PUBLIC_SUPABASE_URL` - Your Supabase project URL
- `EXPO_PUBLIC_SUPABASE_ANON_KEY` - Your Supabase anonymous key
- `EXPO_PUBLIC_MIDDLEWARE_API_URL` - Middleware authentication service URL
- `EXPO_PUBLIC_APPID` - Application ID for middleware
### Running the Apps
**Mobile App (iOS & Android):**
```bash
cd apps/mobile
# Start development server
npm start
# Run on iOS
npm run ios
# Run on Android
npm run android
# Run with specific environment
npm run start:dev # Development environment
npm run start:prod # Production environment
```
**Web App:**
```bash
cd apps/web
# Start development server
npm run dev
# Build for production
npm run build
npm run preview
```
## 📖 Documentation
### Comprehensive Guides
- **[CLAUDE.md](./CLAUDE.md)** - Complete architectural overview and development guidelines
- **[Mobile App README](./apps/mobile/README.md)** - Detailed mobile app documentation
- **[Web App README](./apps/web/README.md)** - SvelteKit web app guide
### Feature Documentation
- **[Authentication System](./apps/mobile/features/auth/README.md)** - Middleware-based auth with JWT
- **[Audio Recording](./apps/mobile/features/audioRecordingV2/README.md)** - AudioRecordingV2 implementation
- **[Blueprints & Prompts](./apps/mobile/docs/blueprints_and_prompts.md)** - AI processing system
- **[Spaces](./apps/mobile/docs/SPACES.md)** - Collaborative workspaces
- **[SvelteKit Migration](./SVELTEKIT_MIGRATION_ANALYSIS.md)** - Web app migration analysis
## 🛠 Technology Stack
### Mobile App (`apps/mobile/`)
| Category | Technologies |
|----------|-------------|
| **Framework** | React Native 0.81.4, Expo SDK 54 |
| **Language** | TypeScript 5.x |
| **Navigation** | Expo Router (file-based) |
| **Styling** | NativeWind (Tailwind CSS) |
| **State** | Zustand, React Context |
| **Backend** | Supabase (PostgreSQL, Storage, Realtime) |
| **Audio** | expo-audio, Azure Speech Services |
| **Payments** | RevenueCat (iOS, Android) |
| **Analytics** | PostHog, Sentry |
| **i18n** | react-i18next (32 languages) |
### Web App (`apps/web/`)
| Category | Technologies |
|----------|-------------|
| **Framework** | SvelteKit 2.x |
| **Language** | TypeScript 5.x |
| **Styling** | TailwindCSS 3.x |
| **State** | Svelte Stores |
| **Backend** | Supabase (shared with mobile) |
| **i18n** | svelte-i18n |
## 🏛 Architecture Highlights
### Feature-Based Structure
The mobile app uses a feature-based architecture with **33 self-contained modules** (auth, audioRecordingV2, memos, spaces, credits, subscription, i18n, theme, etc.), each with its own services, hooks, components, and stores.
### Atomic Design System
Components are organized using atomic design principles:
- **Atoms**: Button, Input, Text, Icon (16 components)
- **Molecules**: MemoPreview, RecordingBar, TagSelector (21 components)
- **Organisms**: AudioRecorder, Memory, TranscriptDisplay (9 components)
- **Statistics**: Analytics components (14 components)
### Middleware Authentication
Uses a custom middleware service as a bridge between the app and Supabase:
```
Mobile/Web App → Middleware Auth → Supabase (with JWT + RLS)
```
- Three token types: `manaToken`, `appToken`, `refreshToken`
- Platform-specific secure storage
- Automatic token refresh
- Supports email/password, Google, and Apple Sign-In
### AI Processing Pipeline
- **Blueprints**: Reusable analysis patterns (Text Analysis, Creative Writing, Meeting Notes)
- **Prompts**: Specific AI tasks (Summary, To-Do, Translation, Q&A)
- **Categories**: 8 organizational categories (Office, Healthcare, University, etc.)
- Multi-language support with localized advice
## 🎯 Key Features Deep Dive
### Audio Recording System (V2)
- High-quality M4A/AAC recording
- Background recording with foreground service (Android)
- Pause/resume support
- Real-time audio metering
- Platform-specific optimizations (iOS/Android)
- Crash recovery with automatic segmentation
- Zero-byte recording prevention
### Collaborative Spaces
- Create unlimited team workspaces
- Role-based permissions (owner, member)
- Email-based invitation system
- Shared credit pools
- Real-time collaboration via Supabase Realtime
### Theme System
4 complete theme variants with light/dark modes:
- **Lume**: Modern gold & dark
- **Nature**: Soothing green
- **Stone**: Elegant slate
- **Ocean**: Tranquil blue
Each theme includes 13 semantic color tokens for consistent UI.
### Internationalization
**32 supported languages** with:
- Automatic device language detection
- Persistent user preferences
- RTL support (Arabic, Hebrew)
- Complete UI translations
Languages: Arabic, Bengali, Bulgarian, Chinese, Czech, Danish, Dutch, English, Estonian, Finnish, French, Gaelic, German, Greek, Hindi, Croatian, Hungarian, Indonesian, Italian, Japanese, Korean, Lithuanian, Latvian, Maltese, Norwegian, Persian, Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovenian, Spanish, Swedish, Turkish, Ukrainian, Urdu, Vietnamese.
## 💻 Development
### Code Quality Tools
```bash
# Mobile app linting
cd apps/mobile
npm run lint # Check code quality
npm run lint:fix # Auto-fix issues
npm run lint:unused # Find unused imports/vars
npm run format # Format with Prettier + ESLint
# Web app checking
cd apps/web
npm run check # Type check
npm run check:watch # Watch mode
```
### Building for Production
**Mobile App (EAS Build):**
```bash
cd apps/mobile
# Development build (with dev client)
eas build --profile development
# Preview build (internal testing)
eas build --profile preview
# Production build (store submission)
eas build --profile production
```
**Web App:**
```bash
cd apps/web
# Build static site
npm run build
# Preview production build
npm run preview
```
## 📊 Project Statistics
- **~10,890** TypeScript/JavaScript files in mobile app
- **33** feature modules
- **60+** reusable components
- **32** language translations
- **4** theme variants (8 including dark modes)
- **2** platforms (mobile + web)
- **1** shared Supabase backend
## 🔒 Security
- **Row Level Security (RLS)** on all Supabase tables
- **JWT-based authentication** with middleware
- **Secure token storage** (platform-specific)
- **Automatic token rotation**
- **Environment variable protection**
- **Sensitive file exclusion** (.gitignore)
## 🤝 Contributing
1. Read the [CLAUDE.md](./CLAUDE.md) for architectural guidance
2. Follow the atomic design system for components
3. Use feature-based organization for new features
4. Test on both iOS and Android before committing
5. Run linting and formatting before pushing
6. Update documentation for significant changes
## 📝 Common Development Tasks
### Adding a New Feature
```bash
# 1. Create feature directory in mobile app
mkdir -p apps/mobile/features/my-feature/{components,hooks,services,store,types}
# 2. Create index.ts for public API
touch apps/mobile/features/my-feature/index.ts
# 3. Add feature-specific README if complex
touch apps/mobile/features/my-feature/README.md
# 4. Update CLAUDE.md if architecturally significant
```
### Adding a New Route (Mobile)
```bash
# File-based routing with Expo Router
# Protected route:
touch apps/mobile/app/\(protected\)/my-route.tsx
# Public route:
touch apps/mobile/app/\(public\)/my-route.tsx
```
### Platform-Specific Code (Mobile App Only)
```bash
# Create platform variants for iOS/Android differences
touch apps/mobile/features/my-feature/myService.ts # Default/shared
touch apps/mobile/features/my-feature/myService.ios.ts # iOS-specific
touch apps/mobile/features/my-feature/myService.android.ts # Android-specific
# Metro bundler automatically resolves the correct file based on platform
# Note: .web.ts variants are no longer used - use apps/web/ for web features
```
### Adding a New Route (Web App)
```bash
# SvelteKit file-based routing
# Protected route:
mkdir -p apps/web/src/routes/\(protected\)/my-route
touch apps/web/src/routes/\(protected\)/my-route/+page.svelte
# Public route:
mkdir -p apps/web/src/routes/my-route
touch apps/web/src/routes/my-route/+page.svelte
```
## 🐛 Known Issues
1. **Android 16+**: Must be in foreground to start recording (platform restriction)
2. **Zero-byte recordings**: Occasional issue on some Android devices (retry mechanism implemented)
3. **Token refresh**: Email may not be in refreshed JWT (stored separately as workaround)
## 📄 License
Proprietary - All rights reserved
---
## 🔗 Quick Links
- **Documentation**: [CLAUDE.md](./CLAUDE.md)
- **Mobile App**: [apps/mobile/README.md](./apps/mobile/README.md)
- **Web App**: [apps/web/README.md](./apps/web/README.md)
- **Architecture**: See CLAUDE.md for detailed architecture
- **Issue Tracking**: (Add your issue tracker link)
- **Support**: (Add your support contact)
---
**Built with ❤️ using React Native, Expo, SvelteKit, and Supabase**

View file

@ -1,19 +0,0 @@
FROM oven/bun:1 AS production
# Install ffmpeg
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile 2>/dev/null || bun install
COPY src ./src
COPY tsconfig.json ./
EXPOSE 3016
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD bun -e "fetch('http://localhost:3016/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["bun", "run", "src/index.ts"]

View file

@ -1,21 +0,0 @@
{
"name": "@memoro/audio-server",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "bun run --hot src/index.ts",
"start": "bun run src/index.ts",
"build": "bun build src/index.ts --outdir dist --target bun"
},
"dependencies": {
"@azure/storage-blob": "^12.17.0",
"@supabase/supabase-js": "^2.49.5",
"fluent-ffmpeg": "^2.1.2",
"hono": "^4.7.0"
},
"devDependencies": {
"@types/fluent-ffmpeg": "^2.1.21",
"@types/node": "^20.0.0",
"typescript": "^5.5.0"
}
}

View file

@ -1,72 +0,0 @@
import { Hono } from 'hono';
import type { MiddlewareHandler } from 'hono';
import { createTranscribeRoutes } from './routes/transcribe.ts';
const app = new Hono();
// ─── Service key middleware ───────────────────────────────────────────────────
function serviceKeyMiddleware(): MiddlewareHandler {
return async (c, next) => {
const expectedKey = process.env.SERVICE_KEY;
if (!expectedKey) {
console.error('[Auth] SERVICE_KEY env var is not configured');
return c.json({ error: 'Server misconfiguration' }, 500);
}
const providedKey = c.req.header('X-Service-Key');
if (!providedKey || providedKey !== expectedKey) {
console.warn(
`[Auth] Unauthorized request to ${c.req.path} — invalid or missing X-Service-Key`
);
return c.json({ error: 'Unauthorized' }, 401);
}
await next();
};
}
// ─── Health check (no auth) ───────────────────────────────────────────────────
app.get('/health', (c) => {
return c.json({
status: 'ok',
service: 'memoro-audio-server',
version: '1.0.0',
timestamp: new Date().toISOString(),
});
});
// ─── Protected routes ─────────────────────────────────────────────────────────
app.use('/api/*', serviceKeyMiddleware());
const transcribeRoutes = createTranscribeRoutes();
app.route('/api/v1/transcribe', transcribeRoutes);
// ─── 404 handler ─────────────────────────────────────────────────────────────
app.notFound((c) => {
return c.json({ error: `Not found: ${c.req.method} ${c.req.path}` }, 404);
});
// ─── Error handler ────────────────────────────────────────────────────────────
app.onError((err, c) => {
console.error(`[Error] Unhandled error on ${c.req.method} ${c.req.path}:`, err);
return c.json({ error: 'Internal server error' }, 500);
});
// ─── Start ────────────────────────────────────────────────────────────────────
const port = parseInt(process.env.PORT ?? '3016', 10);
console.log(`[Server] Memoro Audio Server starting on port ${port}`);
export { app };
export default {
port,
fetch: app.fetch,
};

View file

@ -1,109 +0,0 @@
/**
* Tests for Azure Speech config utilities.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { getAvailableSpeechServices, pickRandomService, BATCH_ENDPOINT_BASE } from './azure';
import type { SpeechServiceConfig } from './azure';
describe('BATCH_ENDPOINT_BASE', () => {
it('points to swedencentral', () => {
expect(BATCH_ENDPOINT_BASE).toBe(
'https://swedencentral.api.cognitive.microsoft.com/speechtotext'
);
});
});
describe('getAvailableSpeechServices', () => {
const originalEnv = process.env;
beforeEach(() => {
process.env = { ...originalEnv };
// Clear all Azure keys
delete process.env.AZURE_SPEECH_KEY;
delete process.env.AZURE_SPEECH_KEY_1;
delete process.env.AZURE_SPEECH_KEY_2;
delete process.env.AZURE_SPEECH_KEY_3;
delete process.env.AZURE_SPEECH_KEY_4;
delete process.env.AZURE_SPEECH_REGION;
});
afterEach(() => {
process.env = originalEnv;
});
it('throws if no keys configured', () => {
expect(() => getAvailableSpeechServices()).toThrow('No Azure Speech credentials configured');
});
it('uses single AZURE_SPEECH_KEY as fallback', () => {
process.env.AZURE_SPEECH_KEY = 'single-key';
const services = getAvailableSpeechServices();
expect(services).toHaveLength(1);
expect(services[0].key).toBe('single-key');
expect(services[0].name).toBe('azure-speech-default');
});
it('uses numbered keys when available', () => {
process.env.AZURE_SPEECH_KEY_1 = 'key-1';
process.env.AZURE_SPEECH_KEY_2 = 'key-2';
const services = getAvailableSpeechServices();
expect(services).toHaveLength(2);
expect(services[0].name).toBe('azure-speech-1');
expect(services[1].name).toBe('azure-speech-2');
});
it('prefers numbered keys over single key', () => {
process.env.AZURE_SPEECH_KEY = 'fallback-key';
process.env.AZURE_SPEECH_KEY_1 = 'key-1';
const services = getAvailableSpeechServices();
expect(services).toHaveLength(1);
expect(services[0].key).toBe('key-1');
});
it('uses custom region', () => {
process.env.AZURE_SPEECH_KEY = 'key';
process.env.AZURE_SPEECH_REGION = 'westeurope';
const services = getAvailableSpeechServices();
expect(services[0].region).toBe('westeurope');
expect(services[0].endpoint).toContain('westeurope');
});
it('defaults to swedencentral region', () => {
process.env.AZURE_SPEECH_KEY = 'key';
const services = getAvailableSpeechServices();
expect(services[0].region).toBe('swedencentral');
});
});
describe('pickRandomService', () => {
it('throws for empty array', () => {
expect(() => pickRandomService([])).toThrow('No speech services available');
});
it('returns the only service for single-element array', () => {
const service: SpeechServiceConfig = {
key: 'key-1',
endpoint: 'https://example.com',
region: 'swedencentral',
name: 'azure-speech-1',
};
expect(pickRandomService([service])).toBe(service);
});
it('returns a service from the array', () => {
const services: SpeechServiceConfig[] = [
{ key: 'key-1', endpoint: 'https://example.com', region: 'swedencentral', name: 'svc-1' },
{ key: 'key-2', endpoint: 'https://example.com', region: 'swedencentral', name: 'svc-2' },
];
const result = pickRandomService(services);
expect(services).toContainEqual(result);
});
});

View file

@ -1,60 +0,0 @@
export interface SpeechServiceConfig {
key: string;
endpoint: string;
region: string;
name: string;
}
export const BATCH_ENDPOINT_BASE = 'https://swedencentral.api.cognitive.microsoft.com/speechtotext';
export function getAvailableSpeechServices(): SpeechServiceConfig[] {
const region = process.env.AZURE_SPEECH_REGION || 'swedencentral';
const endpoint = `https://${region}.api.cognitive.microsoft.com/speechtotext/transcriptions:transcribe`;
const batchBase = `https://${region}.api.cognitive.microsoft.com/speechtotext`;
const services: SpeechServiceConfig[] = [];
// Try numbered keys first (AZURE_SPEECH_KEY_1 through AZURE_SPEECH_KEY_4)
for (let i = 1; i <= 4; i++) {
const key = process.env[`AZURE_SPEECH_KEY_${i}`];
if (key) {
services.push({
key,
endpoint,
region,
name: `azure-speech-${i}`,
});
}
}
// Fall back to single key if no numbered keys found
if (services.length === 0) {
const key = process.env.AZURE_SPEECH_KEY;
if (key) {
services.push({
key,
endpoint,
region,
name: 'azure-speech-default',
});
}
}
if (services.length === 0) {
throw new Error('No Azure Speech credentials configured. Set AZURE_SPEECH_KEY_1..4 or AZURE_SPEECH_KEY.');
}
console.log(`[Azure] Available speech services: ${services.map((s) => s.name).join(', ')}`);
return services;
}
export function pickRandomService(services: SpeechServiceConfig[]): SpeechServiceConfig {
if (services.length === 0) {
throw new Error('No speech services available');
}
const index = Math.floor(Math.random() * services.length);
const service = services[index];
console.log(`[Azure] Selected service: ${service.name} (${index + 1}/${services.length})`);
return service;
}

View file

@ -1,41 +0,0 @@
import { createClient } from '@supabase/supabase-js';
function getSupabaseClient() {
const supabaseUrl = process.env.MEMORO_SUPABASE_URL;
const supabaseServiceKey = process.env.MEMORO_SUPABASE_SERVICE_KEY;
if (!supabaseUrl || !supabaseServiceKey) {
throw new Error('Missing required env vars: MEMORO_SUPABASE_URL, MEMORO_SUPABASE_SERVICE_KEY');
}
return createClient(supabaseUrl, supabaseServiceKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
});
}
export async function downloadAudioFromStorage(audioPath: string): Promise<Buffer> {
const supabase = getSupabaseClient();
console.log(`[Supabase] Downloading audio from storage: ${audioPath}`);
const { data, error } = await supabase.storage.from('user-uploads').download(audioPath);
if (error) {
console.error(`[Supabase] Failed to download audio: ${error.message}`);
throw new Error(`Failed to download audio from storage: ${error.message}`);
}
if (!data) {
throw new Error(`No data returned for audio path: ${audioPath}`);
}
const arrayBuffer = await data.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
console.log(`[Supabase] Downloaded audio: ${buffer.length} bytes`);
return buffer;
}

View file

@ -1,224 +0,0 @@
/**
* Tests for audio-server transcription routes and health check.
*/
import { describe, it, expect, vi } from 'vitest';
import { app } from '../index';
// ── Mocks ────────────────────────────────────────────────────────────────────
vi.mock('../lib/supabase.ts', () => ({
downloadAudioFromStorage: vi.fn().mockResolvedValue(Buffer.from('fake-audio')),
}));
vi.mock('../services/transcription.ts', () => {
class MockTranscriptionService {
transcribeWithFallback = vi.fn().mockResolvedValue({ transcript: 'Hello world' });
}
return { TranscriptionService: MockTranscriptionService };
});
const SERVICE_KEY = 'test-service-key';
function post(path: string, body: unknown, headers?: Record<string, string>) {
return app.request(path, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...headers,
},
body: JSON.stringify(body),
});
}
// ── Health ───────────────────────────────────────────────────────────────────
describe('GET /health', () => {
it('returns 200 with service info', async () => {
const res = await app.request('/health');
expect(res.status).toBe(200);
const data = await res.json();
expect(data.status).toBe('ok');
expect(data.service).toBe('memoro-audio-server');
expect(data.version).toBe('1.0.0');
expect(data.timestamp).toBeDefined();
});
});
// ── Auth ─────────────────────────────────────────────────────────────────────
describe('Service key authentication', () => {
it('rejects requests without X-Service-Key', async () => {
const res = await post('/api/v1/transcribe', {
audioPath: 'test.m4a',
memoId: 'memo-1',
userId: 'user-1',
});
expect(res.status).toBe(401);
const data = await res.json();
expect(data.error).toBe('Unauthorized');
});
it('rejects requests with wrong service key', async () => {
const res = await post(
'/api/v1/transcribe',
{ audioPath: 'test.m4a', memoId: 'memo-1', userId: 'user-1' },
{ 'X-Service-Key': 'wrong-key' }
);
expect(res.status).toBe(401);
});
it('accepts requests with valid service key', async () => {
const res = await post(
'/api/v1/transcribe',
{ audioPath: 'test.m4a', memoId: 'memo-1', userId: 'user-1' },
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(200);
});
});
// ── POST /api/v1/transcribe ──────────────────────────────────────────────────
describe('POST /api/v1/transcribe', () => {
it('starts transcription with valid input', async () => {
const res = await post(
'/api/v1/transcribe',
{ audioPath: 'user/recording.m4a', memoId: 'memo-1', userId: 'user-1' },
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(200);
const data = await res.json();
expect(data.success).toBe(true);
expect(data.memoId).toBe('memo-1');
expect(data.message).toBe('Transcription started');
});
it('accepts optional fields', async () => {
const res = await post(
'/api/v1/transcribe',
{
audioPath: 'user/recording.m4a',
memoId: 'memo-1',
userId: 'user-1',
spaceId: 'space-1',
recordingLanguages: ['de-DE', 'en-US'],
enableDiarization: true,
recordingIndex: 0,
},
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(200);
});
it('rejects missing audioPath', async () => {
const res = await post(
'/api/v1/transcribe',
{ memoId: 'memo-1', userId: 'user-1' },
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(400);
const data = await res.json();
expect(data.error).toContain('Missing required fields');
});
it('rejects missing memoId', async () => {
const res = await post(
'/api/v1/transcribe',
{ audioPath: 'test.m4a', userId: 'user-1' },
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(400);
});
it('rejects missing userId', async () => {
const res = await post(
'/api/v1/transcribe',
{ audioPath: 'test.m4a', memoId: 'memo-1' },
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(400);
});
it('rejects invalid JSON', async () => {
const res = await app.request('/api/v1/transcribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Service-Key': SERVICE_KEY,
},
body: 'not json',
});
expect(res.status).toBe(400);
const data = await res.json();
expect(data.error).toBe('Invalid JSON body');
});
});
// ── POST /api/v1/transcribe/append ───────────────────────────────────────────
describe('POST /api/v1/transcribe/append', () => {
it('starts append transcription with valid input', async () => {
const res = await post(
'/api/v1/transcribe/append',
{
audioPath: 'user/append.m4a',
memoId: 'memo-1',
userId: 'user-1',
recordingIndex: 1,
},
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(200);
const data = await res.json();
expect(data.success).toBe(true);
expect(data.memoId).toBe('memo-1');
expect(data.message).toBe('Append transcription started');
});
it('rejects missing required fields', async () => {
const res = await post(
'/api/v1/transcribe/append',
{ audioPath: 'test.m4a' },
{ 'X-Service-Key': SERVICE_KEY }
);
expect(res.status).toBe(400);
});
it('rejects invalid JSON', async () => {
const res = await app.request('/api/v1/transcribe/append', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Service-Key': SERVICE_KEY,
},
body: '{invalid',
});
expect(res.status).toBe(400);
});
});
// ── 404 ──────────────────────────────────────────────────────────────────────
describe('404 handler', () => {
it('returns 404 for unknown routes', async () => {
const res = await app.request('/nonexistent');
expect(res.status).toBe(404);
const data = await res.json();
expect(data.error).toContain('Not found');
});
it('returns 404 for unknown API routes', async () => {
const res = await app.request('/api/v1/unknown', {
headers: { 'X-Service-Key': SERVICE_KEY },
});
expect(res.status).toBe(404);
});
});

View file

@ -1,124 +0,0 @@
import { Hono } from 'hono';
import { downloadAudioFromStorage } from '../lib/supabase.ts';
import { TranscriptionService } from '../services/transcription.ts';
interface TranscribeBody {
audioPath: string;
memoId: string;
userId: string;
spaceId?: string;
recordingLanguages?: string[];
enableDiarization?: boolean;
isAppend?: boolean;
recordingIndex?: number;
}
const transcriptionService = new TranscriptionService();
export function createTranscribeRoutes() {
const app = new Hono();
app.post('/', async (c) => {
let body: TranscribeBody;
try {
body = await c.req.json<TranscribeBody>();
} catch {
return c.json({ error: 'Invalid JSON body' }, 400);
}
const { audioPath, memoId, userId, spaceId, recordingLanguages, enableDiarization, recordingIndex } = body;
if (!audioPath || !memoId || !userId) {
return c.json({ error: 'Missing required fields: audioPath, memoId, userId' }, 400);
}
const serviceKey = process.env.SERVICE_KEY ?? '';
const serverUrl = process.env.MEMORO_SERVER_URL ?? 'http://localhost:3015';
console.log(`[Route] POST /transcribe — memoId: ${memoId}, userId: ${userId}, audioPath: ${audioPath}`);
// Fire-and-forget: return immediately, process in background
queueMicrotask(async () => {
try {
const audioBuffer = await downloadAudioFromStorage(audioPath);
await transcriptionService.transcribeWithFallback({
audioBuffer,
audioPath,
memoId,
userId,
spaceId,
recordingLanguages,
enableDiarization,
isAppend: false,
recordingIndex,
serviceKey,
serverUrl,
});
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
console.error(`[Route] Transcription background task failed for memo ${memoId}: ${msg}`);
}
});
return c.json({
success: true,
memoId,
message: 'Transcription started',
});
});
app.post('/append', async (c) => {
let body: TranscribeBody;
try {
body = await c.req.json<TranscribeBody>();
} catch {
return c.json({ error: 'Invalid JSON body' }, 400);
}
const { audioPath, memoId, userId, spaceId, recordingLanguages, enableDiarization, recordingIndex } = body;
if (!audioPath || !memoId || !userId) {
return c.json({ error: 'Missing required fields: audioPath, memoId, userId' }, 400);
}
const serviceKey = process.env.SERVICE_KEY ?? '';
const serverUrl = process.env.MEMORO_SERVER_URL ?? 'http://localhost:3015';
console.log(
`[Route] POST /transcribe/append — memoId: ${memoId}, userId: ${userId}, audioPath: ${audioPath}, recordingIndex: ${recordingIndex}`,
);
// Fire-and-forget: return immediately, process in background
queueMicrotask(async () => {
try {
const audioBuffer = await downloadAudioFromStorage(audioPath);
await transcriptionService.transcribeWithFallback({
audioBuffer,
audioPath,
memoId,
userId,
spaceId,
recordingLanguages,
enableDiarization,
isAppend: true,
recordingIndex,
serviceKey,
serverUrl,
});
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err);
console.error(`[Route] Append transcription background task failed for memo ${memoId}: ${msg}`);
}
});
return c.json({
success: true,
memoId,
message: 'Append transcription started',
});
});
return app;
}

View file

@ -1,210 +0,0 @@
import { BATCH_ENDPOINT_BASE, type SpeechServiceConfig } from '../lib/azure.ts';
import { convertToAzureWav } from './ffmpeg.ts';
const DEFAULT_CANDIDATE_LOCALES = [
'en-US',
'de-DE',
'en-GB',
'fr-FR',
'it-IT',
'es-ES',
'sv-SE',
'ru-RU',
'nl-NL',
'tr-TR',
'pt-PT',
];
interface BatchJobResult {
jobId: string;
status: 'processing';
}
interface BatchJobStatus {
jobId: string;
status: string;
self?: string;
files?: string;
}
async function getAzureBlobClients(accountName: string, accountKey: string) {
const { BlobServiceClient, StorageSharedKeyCredential } = await import('@azure/storage-blob');
const credential = new StorageSharedKeyCredential(accountName, accountKey);
const blobServiceClient = new BlobServiceClient(
`https://${accountName}.blob.core.windows.net`,
credential,
);
return { blobServiceClient, credential };
}
async function uploadWavToBlob(
wavBuffer: Buffer,
userId: string,
accountName: string,
accountKey: string,
): Promise<string> {
const { BlobSASPermissions, generateBlobSASQueryParameters } = await import('@azure/storage-blob');
const { blobServiceClient, credential } = await getAzureBlobClients(accountName, accountKey);
const containerName = 'batch-transcription';
const blobName = `transcriptions/${userId}/${Date.now()}.wav`;
const containerClient = blobServiceClient.getContainerClient(containerName);
await containerClient.createIfNotExists();
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
await blockBlobClient.upload(wavBuffer, wavBuffer.length, {
blobHTTPHeaders: { blobContentType: 'audio/wav' },
});
console.log(`[Batch] Uploaded WAV to Azure Blob: ${containerName}/${blobName}`);
const sasOptions = {
containerName,
blobName,
permissions: BlobSASPermissions.parse('r'),
startsOn: new Date(Date.now() - 5 * 60 * 1000),
expiresOn: new Date(Date.now() + 6 * 60 * 60 * 1000),
};
const sasToken = generateBlobSASQueryParameters(sasOptions, credential).toString();
return `${blockBlobClient.url}?${sasToken}`;
}
async function ensureResultsContainerSasUrl(accountName: string, accountKey: string): Promise<string> {
const { ContainerSASPermissions, generateBlobSASQueryParameters } = await import('@azure/storage-blob');
const { blobServiceClient, credential } = await getAzureBlobClients(accountName, accountKey);
const resultsContainerName = 'results';
const containerClient = blobServiceClient.getContainerClient(resultsContainerName);
await containerClient.createIfNotExists();
const sasToken = generateBlobSASQueryParameters(
{
containerName: resultsContainerName,
permissions: ContainerSASPermissions.parse('rcw'),
startsOn: new Date(Date.now() - 5 * 60 * 1000),
expiresOn: new Date(Date.now() + 24 * 60 * 60 * 1000),
},
credential,
).toString();
return `https://${accountName}.blob.core.windows.net/${resultsContainerName}?${sasToken}`;
}
export class BatchTranscriptionService {
async createBatchJob(
audioBuffer: Buffer,
userId: string,
speechService: SpeechServiceConfig,
languages?: string[],
diarization?: boolean,
): Promise<BatchJobResult> {
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY;
if (!accountName || !accountKey) {
throw new Error('Azure Storage credentials not configured (AZURE_STORAGE_ACCOUNT_NAME, AZURE_STORAGE_ACCOUNT_KEY)');
}
console.log(`[Batch] Creating batch transcription job for user ${userId}`);
// Convert audio to WAV before uploading
const wavBuffer = await convertToAzureWav(audioBuffer, '.wav');
// Upload WAV to Azure Blob Storage
const sasUrl = await uploadWavToBlob(wavBuffer, userId, accountName, accountKey);
console.log(`[Batch] Got SAS URL for blob`);
// Ensure results container and get its SAS URL
const destinationUrl = await ensureResultsContainerSasUrl(accountName, accountKey);
// Build candidate locales
const mainLocale = languages?.[0] || 'de-DE';
let candidateLocales =
languages && languages.length > 0
? Array.from(new Set([mainLocale, ...languages, ...DEFAULT_CANDIDATE_LOCALES]))
: DEFAULT_CANDIDATE_LOCALES;
candidateLocales = candidateLocales.slice(0, 10);
if (candidateLocales.length < 2) {
candidateLocales = Array.from(new Set([...candidateLocales, 'en-US', 'de-DE'])).slice(0, 10);
}
const properties: Record<string, unknown> = {
wordLevelTimestampsEnabled: true,
punctuationMode: 'DictatedAndAutomatic',
profanityFilterMode: 'Masked',
destinationContainerUrl: destinationUrl,
timeToLive: 'PT12H',
languageIdentification: {
candidateLocales,
mode: 'Continuous',
},
};
if (diarization !== false) {
properties['diarizationEnabled'] = true;
properties['speakerCount'] = 10;
}
const transcriptionBody = {
contentUrls: [sasUrl],
locale: mainLocale,
displayName: userId,
properties,
};
const batchEndpoint = `${BATCH_ENDPOINT_BASE}/v3.1/transcriptions`;
console.log(`[Batch] Submitting job to: ${batchEndpoint}`);
const response = await fetch(batchEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': speechService.key,
},
body: JSON.stringify(transcriptionBody),
});
if (!response.ok) {
const errorText = await response.text();
console.error(`[Batch] Job creation failed: ${response.status} - ${errorText}`);
throw new Error(`Azure Batch API error: ${response.status} - ${errorText}`);
}
const jobData = await response.json() as { self?: string };
const jobId = jobData.self?.split('/').pop() ?? String(Date.now());
console.log(`[Batch] Job created successfully: ${jobId}`);
return { jobId, status: 'processing' };
}
async getJobStatus(jobId: string, speechService: SpeechServiceConfig): Promise<BatchJobStatus> {
const batchEndpoint = `${BATCH_ENDPOINT_BASE}/v3.1/transcriptions/${jobId}`;
console.log(`[Batch] Checking job status: ${jobId}`);
const response = await fetch(batchEndpoint, {
method: 'GET',
headers: {
'Ocp-Apim-Subscription-Key': speechService.key,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Azure Batch status check failed: ${response.status} - ${errorText}`);
}
const data = await response.json() as { status?: string; self?: string; links?: { files?: string } };
return {
jobId,
status: data.status ?? 'unknown',
self: data.self,
files: data.links?.files,
};
}
}

View file

@ -1,167 +0,0 @@
import * as ffmpeg from 'fluent-ffmpeg';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
const FORMAT_MAP: Record<string, string> = {
'.m4a': 'mp4',
'.mp4': 'mp4',
'.mp3': 'mp3',
'.wav': 'wav',
'.aac': 'aac',
'.ogg': 'ogg',
'.webm': 'webm',
'.flac': 'flac',
'.caf': 'caf',
'.wma': 'asf',
'.amr': 'amr',
};
const PROBE_FORMAT_MAP: Record<string, string> = {
mp3: 'mp3',
mov: 'mp4',
mp4: 'mp4',
m4a: 'mp4',
wav: 'wav',
aac: 'aac',
ogg: 'ogg',
webm: 'webm',
flac: 'flac',
caf: 'caf',
asf: 'asf',
amr: 'amr',
};
async function probeAudioFile(
filePath: string,
): Promise<{ valid: boolean; format?: string; codec?: string; duration?: number }> {
return new Promise((resolve) => {
(ffmpeg as any).ffprobe(filePath, (err: Error | null, metadata: any) => {
if (err) {
console.warn(`[ffmpeg] Probe failed for ${filePath}: ${err.message}`);
resolve({ valid: false });
return;
}
const format = metadata?.format?.format_name;
const duration = metadata?.format?.duration;
const audioStream = metadata?.streams?.find((s: any) => s.codec_type === 'audio');
const codec = audioStream?.codec_name;
resolve({ valid: true, format, codec, duration });
});
});
}
async function cleanup(...files: string[]): Promise<void> {
await Promise.all(
files.map((f) =>
fs.promises.unlink(f).catch(() => {
// Ignore cleanup errors
}),
),
);
}
export async function convertToAzureWav(inputBuffer: Buffer, fileExtension: string): Promise<Buffer> {
const tempDir = os.tmpdir();
const ext = fileExtension.startsWith('.') ? fileExtension : `.${fileExtension}`;
const inputFile = path.join(tempDir, `memoro_input_${Date.now()}${ext}`);
const outputFile = path.join(tempDir, `memoro_output_${Date.now()}.wav`);
console.log(`[ffmpeg] Converting audio (${ext}) to Azure WAV format`);
try {
await fs.promises.writeFile(inputFile, inputBuffer);
// Probe actual format to detect mismatches between extension and content
const probeResult = await probeAudioFile(inputFile);
let inputFormat = FORMAT_MAP[ext.toLowerCase()];
if (probeResult.valid && probeResult.format) {
const probedFormatName = probeResult.format.split(',')[0].trim();
const detectedFormat = PROBE_FORMAT_MAP[probedFormatName];
if (detectedFormat && detectedFormat !== inputFormat) {
console.warn(
`[ffmpeg] Format mismatch: extension suggests "${inputFormat}", content detected as "${detectedFormat}". Using detected format.`,
);
inputFormat = detectedFormat;
}
console.log(`[ffmpeg] Probed format: ${probeResult.format}, codec: ${probeResult.codec}`);
}
return await new Promise<Buffer>((resolve, reject) => {
const command = (ffmpeg as any)(inputFile)
.audioCodec('pcm_s16le') // PCM 16-bit little-endian
.audioFrequency(16000) // 16kHz — Azure's preferred sample rate
.audioChannels(1) // Mono
.format('wav')
.inputOptions([
'-err_detect',
'ignore_err', // Handle iOS spatial audio metadata (chnl box) gracefully
'-fflags',
'+genpts', // Generate presentation timestamps
])
.outputOptions(['-y']);
if (inputFormat) {
command.inputFormat(inputFormat);
console.log(`[ffmpeg] Using input format: ${inputFormat} for extension: ${ext}`);
} else {
console.warn(`[ffmpeg] Unknown format for extension ${ext}, letting ffmpeg auto-detect`);
}
command
.on('end', async () => {
try {
const converted = await fs.promises.readFile(outputFile);
await cleanup(inputFile, outputFile);
console.log(`[ffmpeg] Conversion complete: ${converted.length} bytes`);
resolve(converted);
} catch (readErr) {
await cleanup(inputFile, outputFile);
reject(readErr);
}
})
.on('error', async (err: Error) => {
await cleanup(inputFile, outputFile);
console.error(`[ffmpeg] Conversion error for ${ext}: ${err.message}`);
reject(err);
})
.save(outputFile);
});
} catch (err) {
await cleanup(inputFile, outputFile);
throw err;
}
}
export async function getAudioDuration(buffer: Buffer): Promise<number> {
const tempDir = os.tmpdir();
const tempFile = path.join(tempDir, `memoro_probe_${Date.now()}.tmp`);
try {
await fs.promises.writeFile(tempFile, buffer);
return await new Promise<number>((resolve, reject) => {
(ffmpeg as any).ffprobe(tempFile, async (err: Error | null, metadata: any) => {
await cleanup(tempFile);
if (err) {
reject(new Error(`Failed to probe audio duration: ${err.message}`));
return;
}
const duration = metadata?.format?.duration;
if (typeof duration === 'number') {
resolve(duration);
} else {
reject(new Error('Could not determine audio duration from metadata'));
}
});
});
} catch (err) {
await cleanup(tempFile);
throw err;
}
}

View file

@ -1,677 +0,0 @@
import {
getAvailableSpeechServices,
pickRandomService,
type SpeechServiceConfig,
} from '../lib/azure.ts';
import { convertToAzureWav } from './ffmpeg.ts';
import { BatchTranscriptionService } from './batch.ts';
import * as path from 'path';
const CANDIDATE_LOCALES = [
'de-DE',
'en-GB',
'fr-FR',
'it-IT',
'es-ES',
'sv-SE',
'ru-RU',
'nl-NL',
'tr-TR',
'pt-PT',
];
const TOTAL_TIMEOUT_MS = 1_200_000; // 20 minutes
const FAST_TIMEOUT_MS = 1_200_000; // 20 minutes
// Self-hosted mana-stt service (WhisperX on GPU server)
const MANA_STT_URL = process.env.MANA_STT_URL || '';
const MANA_STT_API_KEY = process.env.MANA_STT_API_KEY || '';
interface TranscriptionResult {
transcript: string;
utterances: Array<{
speaker: number;
text: string;
offset: number;
duration: number;
}>;
speakers: Record<string, string>;
speakerMap: Record<string, number>;
languages: string[];
primary_language: string;
}
interface TranscribeParams {
audioBuffer: Buffer;
audioPath: string;
memoId: string;
userId: string;
spaceId?: string;
recordingLanguages?: string[];
enableDiarization?: boolean;
isAppend?: boolean;
recordingIndex?: number;
serviceKey: string;
serverUrl: string;
}
export class TranscriptionService {
private readonly batchService = new BatchTranscriptionService();
async transcribeWithFallback(params: TranscribeParams): Promise<void> {
const {
audioBuffer,
audioPath,
memoId,
userId,
recordingLanguages,
enableDiarization,
isAppend,
recordingIndex,
serviceKey,
serverUrl,
} = params;
const startTime = Date.now();
const checkTimeout = (stage: string): void => {
const elapsed = Date.now() - startTime;
if (elapsed > TOTAL_TIMEOUT_MS) {
throw new Error(`Fallback chain timeout exceeded after ${elapsed}ms in stage: ${stage}`);
}
};
const withTimeout = <T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> => {
return Promise.race([
promise,
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error(`${label} timeout after ${timeoutMs}ms`)), timeoutMs)
),
]);
};
try {
console.log(`[Transcription] Starting fallback chain for memo ${memoId} (${audioPath})`);
// Attempt 0: Self-hosted mana-stt (WhisperX on GPU server) — primary
if (MANA_STT_URL) {
try {
checkTimeout('mana-stt');
console.log(`[Transcription] Trying mana-stt (WhisperX) at ${MANA_STT_URL}`);
const result = await withTimeout(
this.performManaSTTTranscription(
audioBuffer,
audioPath,
recordingLanguages,
enableDiarization
),
FAST_TIMEOUT_MS,
'mana-stt transcription'
);
await this.notifyServer(
memoId,
userId,
result,
'fast',
serviceKey,
serverUrl,
isAppend,
recordingIndex
);
console.log(`[Transcription] mana-stt (WhisperX) succeeded for memo ${memoId}`);
return;
} catch (manaSttError: unknown) {
const msg = manaSttError instanceof Error ? manaSttError.message : String(manaSttError);
console.warn(`[Transcription] mana-stt failed, falling back to Azure: ${msg}`);
}
}
// Attempt 1: Fast realtime transcription (Azure)
try {
checkTimeout('initial-fast');
const services = getAvailableSpeechServices();
const service = pickRandomService(services);
const wavBuffer = await convertToAzureWav(audioBuffer, path.extname(audioPath) || '.m4a');
const result = await withTimeout(
this.performRealtimeTranscription(
wavBuffer,
service,
recordingLanguages,
enableDiarization
),
FAST_TIMEOUT_MS,
'Fast transcription'
);
await this.notifyServer(
memoId,
userId,
result,
'fast',
serviceKey,
serverUrl,
isAppend,
recordingIndex
);
console.log(`[Transcription] Fast transcription succeeded for memo ${memoId}`);
return;
} catch (fastError: unknown) {
const fastErrMsg = fastError instanceof Error ? fastError.message : String(fastError);
console.warn(`[Transcription] Fast route failed: ${fastErrMsg}`);
// Attempt 2: Service retry with different Azure key (429 rate limit)
if (this.shouldRetryWithDifferentService(fastErrMsg)) {
try {
checkTimeout('service-retry');
console.log(`[Transcription] Retrying with different Azure service key`);
const services = getAvailableSpeechServices();
if (services.length > 1) {
const service = pickRandomService(services);
const wavBuffer = await convertToAzureWav(
audioBuffer,
path.extname(audioPath) || '.m4a'
);
const result = await withTimeout(
this.performRealtimeTranscription(
wavBuffer,
service,
recordingLanguages,
enableDiarization
),
FAST_TIMEOUT_MS,
'Service retry transcription'
);
await this.notifyServer(
memoId,
userId,
result,
'fast',
serviceKey,
serverUrl,
isAppend,
recordingIndex
);
console.log(`[Transcription] Service retry succeeded for memo ${memoId}`);
return;
} else {
console.warn(
`[Transcription] Only one Azure service configured, skipping service retry`
);
}
} catch (serviceRetryError: unknown) {
const msg =
serviceRetryError instanceof Error
? serviceRetryError.message
: String(serviceRetryError);
console.warn(`[Transcription] Service retry failed: ${msg}`);
}
}
// Attempt 3: FFmpeg conversion + retry (422 / format errors)
if (this.shouldRetryWithConversion(fastErrMsg)) {
try {
checkTimeout('conversion-retry');
console.log(`[Transcription] Retrying with enhanced audio conversion`);
const services = getAvailableSpeechServices();
const service = pickRandomService(services);
// Force conversion even if already attempted — use explicit wav extension
const wavBuffer = await convertToAzureWav(audioBuffer, '.wav');
const result = await withTimeout(
this.performRealtimeTranscription(
wavBuffer,
service,
recordingLanguages,
enableDiarization
),
FAST_TIMEOUT_MS,
'Conversion retry transcription'
);
await this.notifyServer(
memoId,
userId,
result,
'fast',
serviceKey,
serverUrl,
isAppend,
recordingIndex
);
console.log(`[Transcription] Conversion retry succeeded for memo ${memoId}`);
return;
} catch (conversionError: unknown) {
const msg =
conversionError instanceof Error ? conversionError.message : String(conversionError);
console.warn(`[Transcription] Conversion retry failed: ${msg}. Falling back to batch.`);
}
}
// Attempt 4: Azure batch transcription fallback
checkTimeout('batch-fallback');
console.log(`[Transcription] Falling back to Azure Batch transcription for memo ${memoId}`);
try {
const services = getAvailableSpeechServices();
const service = pickRandomService(services);
const batchResult = await this.batchService.createBatchJob(
audioBuffer,
userId,
service,
recordingLanguages,
enableDiarization
);
console.log(`[Transcription] Batch job created: ${batchResult.jobId} for memo ${memoId}`);
// Batch jobs complete asynchronously via webhook — no immediate notify here
return;
} catch (batchError: unknown) {
const msg = batchError instanceof Error ? batchError.message : String(batchError);
throw new Error(`All transcription methods failed. Batch error: ${msg}`);
}
}
} catch (error: unknown) {
const errorMsg = error instanceof Error ? error.message : String(error);
console.error(`[Transcription] All fallback attempts failed for memo ${memoId}: ${errorMsg}`);
await this.notifyServerError(memoId, userId, errorMsg, serviceKey, serverUrl);
}
}
async performRealtimeTranscription(
audioBuffer: Buffer,
speechService: SpeechServiceConfig,
languages?: string[],
diarization?: boolean
): Promise<TranscriptionResult> {
const definition: Record<string, unknown> = {
wordLevelTimestampsEnabled: true,
punctuationMode: 'Automatic',
profanityFilterMode: 'None',
};
if (diarization !== false) {
definition['diarization'] = {
enabled: true,
maxSpeakers: 10,
};
}
const candidateLocales = languages && languages.length > 0 ? languages : CANDIDATE_LOCALES;
definition['languageIdentification'] = {
candidateLocales,
};
console.log(`[Azure] Sending realtime transcription request to ${speechService.name}`);
console.log(`[Azure] Definition: ${JSON.stringify(definition)}`);
const formData = new FormData();
formData.append('definition', JSON.stringify(definition));
const audioBlob = new Blob([audioBuffer], { type: 'audio/wav' });
formData.append('audio', audioBlob, 'audio.wav');
const response = await fetch(`${speechService.endpoint}?api-version=2024-11-15`, {
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': speechService.key,
Accept: 'application/json',
},
body: formData,
});
if (!response.ok) {
const errorText = await response.text();
if (response.status === 429) {
const retryAfter = response.headers.get('retry-after') ?? 'n/a';
const requestId = response.headers.get('x-ms-request-id') ?? 'n/a';
const quotaReason = response.headers.get('x-ms-service-quota-reason') ?? 'n/a';
console.error(
`[AZURE_429_ERROR] Rate limited on ${speechService.name} — retry-after: ${retryAfter}, request-id: ${requestId}, quota-reason: ${quotaReason}`
);
console.error(`[AZURE_429_ERROR] Body: ${errorText}`);
throw new Error(`[AZURE_429_ERROR] Azure Speech API rate limited (429): ${errorText}`);
}
if (response.status === 422) {
const requestId = response.headers.get('x-ms-request-id') ?? 'n/a';
console.error(
`[AZURE_422_ERROR] Format error on ${speechService.name} — request-id: ${requestId}`
);
console.error(`[AZURE_422_ERROR] Body: ${errorText}`);
throw new Error(`[AZURE_422_ERROR] Azure Speech API format error (422): ${errorText}`);
}
throw new Error(`Azure Speech API error: ${response.status} - ${errorText}`);
}
const azureResult = (await response.json()) as Parameters<
typeof this.processTranscriptionResult
>[0];
console.log(`[Azure] Transcription response received from ${speechService.name}`);
console.log(`[Azure] Phrase count: ${azureResult?.phrases?.length ?? 0}`);
return this.processTranscriptionResult(azureResult);
}
processTranscriptionResult(azureResult: {
phrases?: Array<{
text?: string;
speaker?: number;
offsetMilliseconds?: number;
durationMilliseconds?: number;
locale?: string;
words?: unknown[];
}>;
combinedPhrases?: Array<{ text?: string }>;
locale?: string;
}): TranscriptionResult {
let transcript = '';
let primary_language = 'de-DE';
let languages: string[] = ['de-DE'];
// Determine languages from phrase-level locale analysis (more accurate than top-level)
if (azureResult.phrases && azureResult.phrases.length > 0) {
const phraseCounts: Record<string, number> = {};
const charCounts: Record<string, number> = {};
for (const phrase of azureResult.phrases) {
if (phrase.locale) {
phraseCounts[phrase.locale] = (phraseCounts[phrase.locale] ?? 0) + 1;
charCounts[phrase.locale] = (charCounts[phrase.locale] ?? 0) + (phrase.text?.length ?? 0);
}
}
const uniqueLanguages = Object.keys(phraseCounts);
if (uniqueLanguages.length > 0) {
// Pick primary by character count — more accurate than phrase count
primary_language = uniqueLanguages.reduce((best, lang) =>
(charCounts[lang] ?? 0) > (charCounts[best] ?? 0) ? lang : best
);
languages = uniqueLanguages;
console.log(
`[Transcription] Language detection: ${JSON.stringify(charCounts)}, primary: ${primary_language}`
);
}
} else if (azureResult.locale) {
primary_language = azureResult.locale;
languages = [azureResult.locale];
}
// Build transcript text
if (azureResult.combinedPhrases && azureResult.combinedPhrases.length > 0) {
transcript = azureResult.combinedPhrases[0]?.text ?? '';
} else if (azureResult.phrases && azureResult.phrases.length > 0) {
transcript = azureResult.phrases.map((p) => p.text ?? '').join(' ');
}
// Build utterances and speaker maps
const utterances: TranscriptionResult['utterances'] = [];
const speakerIdSet = new Set<number>();
if (azureResult.phrases) {
for (const phrase of azureResult.phrases) {
if (phrase.speaker !== undefined && phrase.text) {
utterances.push({
speaker: phrase.speaker,
text: phrase.text,
offset: phrase.offsetMilliseconds ?? 0,
duration: phrase.durationMilliseconds ?? 0,
});
speakerIdSet.add(phrase.speaker);
}
}
}
// Sort by time
utterances.sort((a, b) => a.offset - b.offset);
// Build speaker label maps
const speakers: Record<string, string> = {};
const speakerMap: Record<string, number> = {};
for (const speakerId of speakerIdSet) {
const label = `Speaker ${speakerId}`;
speakers[String(speakerId)] = label;
speakerMap[label] = speakerId;
}
console.log(
`[Transcription] Processed: ${transcript.length} chars, ${utterances.length} utterances, ${speakerIdSet.size} speakers, lang: ${primary_language}`
);
return { transcript, utterances, speakers, speakerMap, languages, primary_language };
}
/**
* Transcribe via self-hosted mana-stt service (WhisperX on GPU server).
* Uses the /transcribe/whisperx endpoint which returns rich data with
* diarization, word alignment, and utterances.
*/
async performManaSTTTranscription(
audioBuffer: Buffer,
audioPath: string,
languages?: string[],
diarization?: boolean
): Promise<TranscriptionResult> {
const ext = path.extname(audioPath) || '.m4a';
const mimeTypes: Record<string, string> = {
'.m4a': 'audio/mp4',
'.mp3': 'audio/mpeg',
'.wav': 'audio/wav',
'.flac': 'audio/flac',
'.ogg': 'audio/ogg',
'.webm': 'audio/webm',
'.mp4': 'audio/mp4',
};
const mimeType = mimeTypes[ext] || 'audio/wav';
// Determine language hint from recording languages (e.g., 'de-DE' → 'de')
const langHint = languages?.[0]?.split('-')[0] || null;
const formData = new FormData();
const audioBlob = new Blob([audioBuffer], { type: mimeType });
formData.append('file', audioBlob, `audio${ext}`);
if (langHint) formData.append('language', langHint);
formData.append('diarization', String(diarization !== false));
formData.append('alignment', 'true');
const headers: Record<string, string> = {
Accept: 'application/json',
};
if (MANA_STT_API_KEY) {
headers['X-API-Key'] = MANA_STT_API_KEY;
}
console.log(
`[mana-stt] Sending WhisperX request (${audioBuffer.length} bytes, lang=${langHint})`
);
const response = await fetch(`${MANA_STT_URL}/transcribe/whisperx`, {
method: 'POST',
headers,
body: formData,
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`mana-stt error: ${response.status} - ${errorText}`);
}
const sttResult = (await response.json()) as {
text: string;
language?: string;
duration_seconds?: number;
utterances?: Array<{
speaker: number;
text: string;
offset: number;
duration: number;
}>;
speakers?: Record<string, string>;
speaker_map?: Record<string, number>;
languages?: string[];
primary_language?: string;
};
console.log(
`[mana-stt] Response: ${sttResult.text.length} chars, ` +
`${sttResult.utterances?.length ?? 0} utterances, ` +
`lang=${sttResult.primary_language ?? sttResult.language}`
);
// Map mana-stt language codes to locale format (e.g., 'de' → 'de-DE')
const localeMap: Record<string, string> = {
de: 'de-DE',
en: 'en-GB',
fr: 'fr-FR',
it: 'it-IT',
es: 'es-ES',
sv: 'sv-SE',
ru: 'ru-RU',
nl: 'nl-NL',
tr: 'tr-TR',
pt: 'pt-PT',
ja: 'ja-JP',
ko: 'ko-KR',
zh: 'zh-CN',
ar: 'ar-SA',
hi: 'hi-IN',
};
const rawLang = sttResult.primary_language ?? sttResult.language ?? 'de';
const primaryLocale = localeMap[rawLang] ?? `${rawLang}-${rawLang.toUpperCase()}`;
const detectedLanguages = (sttResult.languages ?? [rawLang]).map(
(l) => localeMap[l] ?? `${l}-${l.toUpperCase()}`
);
return {
transcript: sttResult.text,
utterances: sttResult.utterances ?? [],
speakers: sttResult.speakers ?? {},
speakerMap: sttResult.speaker_map ?? {},
languages: detectedLanguages,
primary_language: primaryLocale,
};
}
shouldRetryWithDifferentService(errorMsg: string): boolean {
const has429 = /429|AZURE_429_ERROR|rate.?limit|too many requests/i.test(errorMsg);
console.log(
`[Transcription] shouldRetryWithDifferentService: ${has429} (${errorMsg.substring(0, 100)})`
);
return has429;
}
shouldRetryWithConversion(errorMsg: string): boolean {
const patterns = [
/422/,
/AZURE_422_ERROR/,
/audio.?format/i,
/InvalidAudioFormat/i,
/audio\/x-m4a/i,
/unsupported.*format/i,
/invalid.*audio/i,
/codec.*not.*supported/i,
/content.*type.*unsupported/i,
/bitrate.*not.*supported/i,
/sample.*rate.*invalid/i,
/media.*type.*not.*supported/i,
];
const matches = patterns.some((p) => p.test(errorMsg));
console.log(
`[Transcription] shouldRetryWithConversion: ${matches} (${errorMsg.substring(0, 100)})`
);
return matches;
}
async notifyServer(
memoId: string,
userId: string,
result: TranscriptionResult,
route: 'fast' | 'batch',
serviceKey: string,
serverUrl: string,
isAppend?: boolean,
recordingIndex?: number
): Promise<void> {
const endpoint = isAppend
? `${serverUrl}/api/v1/internal/append-transcription-completed`
: `${serverUrl}/api/v1/internal/transcription-completed`;
const body: Record<string, unknown> = {
memoId,
userId,
transcriptionResult: result,
route,
success: true,
};
if (isAppend) {
body['recordingIndex'] = recordingIndex;
}
console.log(`[Callback] Notifying server at ${endpoint} for memo ${memoId}`);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Service-Key': serviceKey,
},
body: JSON.stringify(body),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Server callback failed: ${response.status} - ${errorText}`);
}
console.log(`[Callback] Server notified successfully for memo ${memoId}`);
}
async notifyServerError(
memoId: string,
userId: string,
errorMsg: string,
serviceKey: string,
serverUrl: string
): Promise<void> {
const endpoint = `${serverUrl}/api/v1/internal/transcription-completed`;
console.error(
`[Callback] Notifying server of transcription error for memo ${memoId}: ${errorMsg}`
);
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Service-Key': serviceKey,
},
body: JSON.stringify({
memoId,
userId,
error: errorMsg,
success: false,
timestamp: new Date().toISOString(),
}),
});
if (!response.ok) {
const text = await response.text();
console.error(`[Callback] Error notification failed: ${response.status} - ${text}`);
}
} catch (notifyErr: unknown) {
const msg = notifyErr instanceof Error ? notifyErr.message : String(notifyErr);
console.error(`[Callback] Failed to notify server of error: ${msg}`);
}
}
}

View file

@ -1,3 +0,0 @@
process.env.NODE_ENV = 'test';
process.env.SERVICE_KEY = 'test-service-key';
process.env.MEMORO_SERVER_URL = 'http://localhost:3015';

View file

@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ESNext"],
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"outDir": "dist",
"types": ["bun-types", "node"]
},
"include": ["src/**/*.ts", "vitest.config.ts"],
"exclude": ["node_modules", "dist"]
}

View file

@ -1,11 +0,0 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['src/**/*.test.ts'],
setupFiles: ['./src/test-setup.ts'],
clearMocks: true,
},
});

View file

@ -1,3 +0,0 @@
# Umami Analytics Configuration (optional)
# PUBLIC_UMAMI_WEBSITE_ID=your-website-id
# PUBLIC_UMAMI_URL=https://your-umami-instance.com/script.js

View file

@ -1,30 +0,0 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/
# Archive folder
.archive/
# Local Netlify folder
.netlify

View file

@ -1,112 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Memoro is a multilingual marketing website built with Astro for an AI-powered conversation documentation and note-taking app. The site supports German (de) as default and English (en) locales.
## Build Commands
```bash
npm run dev # Start development server (localhost:4321)
npm run build # Build production site to ./dist/
npm run preview # Preview production build locally
npm run astro check # Type-check the project. IMPORTANT: Use this after every change!
```
## Architecture
### Tech Stack
- **Framework**: Astro 5.3.0 with static site generation
- **Styling**: Tailwind CSS
- **Content**: MDX support with content collections
- **TypeScript**: Strict mode enabled
### Project Structure
```
src/
├── components/ # Reusable Astro components
├── content/ # Content collections with Zod schemas
│ ├── blog/ # Blog posts (de/en subfolders)
│ ├── team/ # Team member profiles
│ ├── features/ # Feature descriptions
│ ├── guides/ # User guides and tutorials
│ └── ... # Other collections (industries, testimonials, etc.)
├── i18n/ # Internationalization (ui.ts for translations)
├── layouts/ # Page layout templates
├── pages/ # Routes with [lang] dynamic routing
├── styles/ # Global CSS with Tailwind
└── utils/ # Utility functions
```
### Internationalization (i18n)
- **Default locale**: German (de)
- **Supported locales**: German (de), English (en)
- **Routing**: Prefix-based (e.g., /de/blog, /en/blog)
- **Middleware**: Automatically redirects to default locale if missing
- **Translations**: Centralized in `src/i18n/ui.ts`
- **Content**: Organized in language subfolders within collections
### Content Collections
All content uses Zod schemas for validation. Key collections:
- **blog**: Articles with metadata (title, description, pubDate, author, tags)
- **team**: Team profiles with roles and social links
- **features**: Product features with icons and categories
- **guides**: Tutorials with difficulty levels and duration
- **testimonials**: Customer testimonials
- **legal**: Legal pages (privacy, terms, etc.)
Each content type must include:
- `lang` field (either 'de' or 'en')
- Proper frontmatter matching the schema
- MDX content body
### Code Style Guidelines
#### Components
- Use PascalCase for component names (e.g., `BlogCard.astro`)
- Define Props interfaces at the top of component files
- Import order: external libraries first, then project files
#### TypeScript
- Always use Zod schemas for content validation
- Define interfaces for all component props
- Use strict type checking (enabled in tsconfig)
#### CSS
- Use Tailwind CSS utility classes
- Follow kebab-case for custom CSS classes
- Avoid inline styles
#### Error Handling
- Middleware handles 404s and missing locale redirects
- Use optional chaining for potentially undefined values
- Provide fallbacks for missing translations
### Important Implementation Notes
- Static site generation means no server-side runtime
- All content is pre-built at build time
- Dynamic routes use Astro's `getStaticPaths()`
- Sitemap generation includes all locales
- Images stored in `/public/images/` organized by type
### Testing
No test framework is currently configured. Consider manual testing of:
- All language routes
- Content collection validation
- Build process for production
- 404 handling and redirects

View file

@ -1,168 +0,0 @@
# PostHog A/B Testing Setup
Dieses Dokument beschreibt die PostHog-Integration für A/B-Testing auf der Memoro-Website.
## 1. Initiale Einrichtung
### PostHog-Account erstellen
1. Gehe zu [PostHog EU](https://eu.posthog.com)
2. Erstelle einen Account (EU-Region für GDPR-Compliance)
3. Erstelle ein neues Projekt "Memoro Landing"
### Environment-Variablen
Erstelle eine `.env`-Datei basierend auf `.env.example`:
```bash
PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
### Testen der Integration
1. Starte den Dev-Server: `npm run dev`
2. Akzeptiere Analytics-Cookies im Cookie-Banner
3. Öffne die Browser-Konsole und prüfe: `window.posthog` sollte verfügbar sein
4. Überprüfe im PostHog-Dashboard, ob Events ankommen
## 2. A/B-Tests erstellen
### Im PostHog-Dashboard
1. Navigiere zu "Feature Flags" → "New Feature Flag"
2. Erstelle ein neues Flag, z.B. `hero-cta-test`
3. Konfiguriere die Varianten:
- Control: Default (kein Wert)
- Variant B: `variant-b`
4. Setze die Rollout-Percentage (z.B. 50/50)
### Im Code implementieren
#### Option 1: Fertige Experiment-Komponente nutzen
```astro
---
import HeroCtaExperiment from '../components/experiments/HeroCtaExperiment.astro';
---
<HeroCtaExperiment />
```
#### Option 2: Custom Implementation
```astro
<div id="my-experiment" data-experiment="my-test">
<!-- Default content -->
</div>
<script>
import { getExperiment, trackEvent } from '../utils/experiments';
const variant = await getExperiment('my-test');
if (variant === 'variant-b') {
// Apply variant B changes
}
</script>
```
## 3. Verfügbare Utilities
### `getExperiment(key)`
Holt die aktuelle Variante für einen Test:
```javascript
const variant = await getExperiment('hero-cta-test');
// Returns: null | 'control' | 'variant-b' | ...
```
### `trackEvent(name, properties)`
Trackt ein Custom Event:
```javascript
trackEvent('button_clicked', {
button_id: 'download-cta',
page: 'home'
});
```
### `trackExperimentConversion(experiment, type)`
Trackt eine Conversion für einen A/B-Test:
```javascript
trackExperimentConversion('hero-cta-test', 'download_click');
```
### `applyExperimentClasses(elementId, experimentKey, variantClasses)`
Wendet CSS-Klassen basierend auf der Variante an:
```javascript
applyExperimentClasses('hero-section', 'hero-test', {
'control': 'bg-blue-500',
'variant-b': 'bg-green-500'
});
```
## 4. Best Practices
### Performance
- PostHog lädt nur nach Cookie-Consent
- Script ist asynchron und blockiert nicht
- Feature Flags werden gecached
### GDPR-Compliance
- Analytics nur mit expliziter Zustimmung
- EU-Server verwenden
- `person_profiles: 'identified_only'` für anonyme Nutzer
### Testing-Strategie
1. **Start klein**: Beginne mit unkritischen Elementen
2. **Messe richtig**: Definiere klare Success-Metriken
3. **Dokumentiere**: Halte Tests und Ergebnisse fest
4. **Iteriere**: Nutze Learnings für neue Tests
## 5. Geplante A/B-Tests
### Phase 1 (Sofort möglich)
- **Hero CTA**: "App herunterladen" vs. "Kostenlos testen"
- **Download Button Position**: Rechts vs. Links in Navigation
- **Testimonial Position**: Oben vs. Unten auf Homepage
### Phase 2 (Nach ersten Learnings)
- **Pricing Layout**: Grid vs. Tabelle
- **Feature-Reihenfolge**: Verschiedene Priorisierungen
- **Formulare**: Anzahl der Felder reduzieren
## 6. Monitoring & Analyse
### PostHog Dashboard
- **Experiments**: Übersicht aller laufenden Tests
- **Feature Flags**: Status und Rollout-Percentage
- **Insights**: Custom Dashboards für Conversions
### Wichtige Metriken
- Conversion Rate (Download-Klicks)
- Bounce Rate pro Variante
- Time on Page
- Scroll-Tiefe
## 7. Troubleshooting
### PostHog lädt nicht
1. Prüfe Cookie-Consent Status
2. Checke Browser-Konsole für Fehler
3. Verifiziere API-Key in `.env`
### Feature Flag gibt null zurück
1. Stelle sicher, dass PostHog geladen ist
2. Prüfe Flag-Name (Case-sensitive!)
3. Checke Rollout-Settings im Dashboard
### Events werden nicht getrackt
1. Öffne Network-Tab und suche nach `posthog.com/e/`
2. Prüfe, ob `autocapture: false` gesetzt ist
3. Nutze `posthog.debug()` für Details
## 8. Migration von Plausible
Aktuell laufen Plausible und PostHog parallel. Nach 2-4 Wochen:
1. Vergleiche Metriken beider Tools
2. Stelle sicher, dass PostHog alle benötigten Daten erfasst
3. Entferne Plausible-Komponente
4. Update Cookie-Consent Beschreibung
5. Entferne Plausible DNS-Prefetch
## Support
Bei Fragen oder Problemen:
- PostHog Docs: https://posthog.com/docs
- Astro + PostHog: https://posthog.com/tutorials/astro-ab-tests

View file

@ -1,48 +0,0 @@
# Astro Starter Kit: Basics
```sh
npm create astro@latest -- --template basics
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
│ └── favicon.svg
├── src/
│ ├── layouts/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
```
To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/).
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View file

@ -1,48 +0,0 @@
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';
import icon from 'astro-icon';
// https://astro.build/config
export default defineConfig({
image: {
service: {
entrypoint: 'astro/assets/services/sharp',
config: {
limitInputPixels: false,
},
},
domains: [],
remotePatterns: [],
},
integrations: [
mdx(),
tailwind(),
sitemap({
i18n: {
defaultLocale: 'de',
locales: {
de: 'de-DE',
en: 'en-US'
}
}
}),
icon({
include: {
mdi: ["*"]
}
})
],
contentCollections: true,
site: 'https://memoro.ai',
output: 'static',
i18n: {
locales: ['de', 'en'],
defaultLocale: 'de',
routing: {
prefixDefaultLocale: true,
redirectToDefaultLocale: false
}
}
});

View file

@ -1,15 +0,0 @@
Texte High Level
Du bist eine begabte Werbetexterin und Copywriterin.
Du hilfst dabei anhand von diesen Guidelines einen Text umzuschreiben:
Memoro Text Guidelines
Die 8 Prinzipien unserer Stimme:
Wir reden nicht um den heißen Brei herum. Wir nutzen kurze, klare, prägnante Sätze.
Wir informieren, und wollen helfen, nichts bewerben oder verkaufen. Mehrwert schaffen wir mit unserer Applikation.
Wir versuchen auf Du oder Sie zu verzichten, mehr wie ein Pressetext zu klingen.
Wir ziehen Vergleiche zu Memoro (Einer App die Gespräche aufzeichnet, aufschreibt und passend zusammenfasst, eine Wissenbibliothek und ein ständiger Assistent, der im Hintergrund bleibt und Zeit schafft und einem den Rücken freihält, die Brandfarbe ist Gelb (wie ein Gedanke) und die Formen sind fließend und rund)
Wir wollen andere mit unseren Gedanken anstecken und sie zum ausprobieren bewegen, Inspiration schaffen.
Wir wollen Partizipation fördern und andere an unserer Entwicklung teilhaben lassen und ihre Ideen und Funktion Wünsche, um für sie das beste Produkt bauen zu können.
Wir wollen Engaging mit unserem Publikum kommunizieren. Nutzen das AIDA Framework (Attention, Intrest, Desire, Action) im Aufbau unserer Texte.
Wir wollen das alle Nutzer alles verstehen, also kommunizieren wir in einfache Sprache: Statt DSGVO konform, sage lieber alle Daten werden sicher in Deutschland gespeichert. Wir reden auch nicht über KI, sondern über die besten Ohren, das intelligente mitschreiben und zusammenfassen etc. Ausserdem nutzen wir keine Worte wie revolutionierend oder ähnliches, wir sind bodenständig und ziehen lieber einen lustigen Vergleich.

View file

@ -1,80 +0,0 @@
# Image Prompts für Blog-Artikel
Diese Datei enthält bewährte Prompt-Vorlagen für die Generierung von Blog-Bildern.
## Blog-Header Icons/Symbole
### Minimale geometrische Symbole (mit transparentem Hintergrund)
**Vorlage für Kommunikation/KI-Themen:**
```
Minimal geometric icon design on transparent background. Two interlocking shapes: a rounded speech bubble (gradient from coral to orange) flowing into a crystalline hexagon (gradient from electric blue to purple). The shapes overlap in the center creating a small overlapping area in vibrant green. Clean vector style, no shadows, no background. Flat design with subtle gradients. Symbol represents human-AI communication. Centered composition, perfect for web use.
```
**Verwendung:** Prompt Engineering, KI-Kommunikation, Chat-Themen
---
### Variationen für andere Themen
**Für Produktivitäts-Themen:**
```
Minimal geometric icon design on transparent background. Three overlapping circles forming a Venn diagram. Left circle: orange gradient. Right circle: blue gradient. Bottom circle: purple gradient. Where all three overlap in the center: bright teal. Clean, minimal, flat design. No text, no decoration. Modern tech logo style.
```
**Für Transformation/Workflow-Themen:**
```
Minimalist vector symbol on transparent background: Single flowing arrow that transforms from organic curved line (warm orange gradient) to geometric angular shape (cool blue-purple gradient). The transformation happens in the middle with a smooth color transition. Ultra simple, modern icon design. No background, clean edges.
```
---
## Anpassungshinweise
- **Farben:** Hauptgradients können angepasst werden (orange/coral → blue/purple ist bewährt)
- **Form:** Grundformen können variiert werden (Kreise, Hexagone, Pfeile, etc.)
- **Stil:** Immer "minimal", "clean", "flat design", "no shadows", "transparent background"
- **Format:** Optimal für Web-Verwendung, zentrierte Komposition
## Bewährte Farbkombinationen
1. **Warm → Cool:** Orange/Coral → Blue/Purple
2. **Energie:** Yellow/Orange → Red/Pink
3. **Natur:** Green/Teal → Blue/Purple
4. **Professional:** Gray/Silver → Blue/Navy
## Technische Spezifikationen
- Format: PNG mit transparentem Hintergrund
- Stil: Flat Design, Vektor-ähnlich
- Schatten: Keine
- Text: Keiner
- Komposition: Zentriert
- Verwendung: Blog-Header, Icon-Elemente
---
## Neue Prompts für "KI als persönlicher Assistent"
### Option 1: Automatisierung-Symbol
```
Minimal geometric icon design on transparent background. Two interlocking gears: left gear organic and rounded (gradient from warm orange to coral), right gear precise and angular (gradient from electric blue to purple). Small floating elements around them representing automated tasks (tiny circles and triangles in teal). Clean vector style, no shadows, no background. Flat design with subtle gradients. Symbol represents automation and AI assistance. Centered composition, perfect for web use.
```
### Option 2: Zeit-Effizienz-Symbol
```
Minimalist vector symbol on transparent background: Clock face transforming into flowing energy streams. Clock portion: gradient from orange to yellow. Energy streams: gradient from blue to purple, flowing outward in elegant curves. Small productivity icons (tiny checklist, envelope, document) floating in the streams in vibrant green. Ultra simple, modern icon design. No background, clean edges.
```
### Option 3: Assistent-Verbindung-Symbol
```
Simple vector icon on transparent background: Central hub (hexagon in blue-purple gradient) connected to four smaller circles representing different tasks. Connections: flowing lines in orange gradient. Each circle different color: orange (meetings), teal (emails), purple (documents), green (tasks). Clean, minimal, flat design. No text, no decoration. Modern tech assistance logo style.
```
---
## Prompt für "KI-gestützte Entscheidungsfindung"
```
Minimal geometric icon design on transparent background. Decision tree visualization: Central diamond shape (gradient from coral to orange) with three branching paths flowing outward. Each path ends in a different geometric shape representing options: circle (blue gradient), triangle (purple gradient), square (teal gradient). The paths are elegant curved lines with subtle glow. Small analytical elements floating around (tiny data points, mini charts) in soft green. Clean vector style, no shadows, no background. Flat design with subtle gradients. Symbol represents AI-supported decision making. Centered composition, perfect for web use.
```

View file

@ -1,170 +0,0 @@
Recording Features
Feature: Perfektes Gehör
Beschreibung: Ein Klick genügt - Memoro hört aufmerksam zu und erfasst jedes Detail deiner Gespräche.
Hauptfunktionen:
Ein-Klick Aufnahme: Memoro ist sofort einsatzbereit, nach Tippen auf den großen runden Aufnahmeknopf.
Intelligentes Hören: Erkennt bis zu 15 verschiedene Sprecher in über 80 Sprachen und Dialekten.
Flexible Aufnahme: Nimm die verschiedensten Gespräche auf: Meetings & Konferenzen, Persönliche Notizen, Interviews & Vorträge, Dokumentation, Beratung
Zuverlässige Technik: Memoro ist komplett Offline-fähig. Jede Aufnahme wird auf deinem Gerät gespeichert und kann aus dem Audioarchiv erneut hochgeladen werden, wenn wieder mit dem Internet verbunden.
Bleibt im Hintergrund: Memoro ist sehr batterie-effizient. Du kannst dein Gerät auch sperren, oder die App wechseln, Memoro bleibt aktiv, ohne abzulenken.
Optionale Standortspeicherung: Wenn in den Einstellungen aktiviert wird, wird zusätzlich automatisch der Standort beim Beenden der Memo gespeichert. Dieser kann dann auf der Karte angesehen werden.
Feature: Aufnahme Sprachen
Beschreibung: Über 80 Sprachen und Dialekte mit automatischer Erkennung.
Hauptfunktionen:
Automatische Spracherkennung: Memoro erkennt automatisch die gesprochene Sprache, aus über 80 verschiedenen Sprachen und Dialekten.
20 Express-Sprachen: Diese Sprachen können in wenigen Sekunden verarbeitet werden und stehen fast unmittelbar zur Verfügung. Wird verwendet bei Aufnahmen bis zu 40 Minuten. Unterstützte Sprachen: Deutsch, Englisch, Französisch, Spanisch, Italienisch, Niederländisch, Polnisch, Schwedisch, Russisch, Portugiesisch, Arabisch, Hindi, Chinesisch, Japanisch, Koreanisch.
80+ Spachen: Mit 60+ Sprachen mit Standard-Transkription (diese braucht mehrere Minuten zur Verarbeitung). Memoro bietet die schnellste und qualitativ hochwertigste, DSGVO konforme Transkription auf dem Markt.
Dialekt-Erkennung: Memoro unterstützt verschiedene Dialekte, um eine möglichst gute Translationsqualität zu gewährleisten. Diese Varianten werden unterstützt: Regionales Deutsch (Deutschland, Österreich, Schweiz), Englisch-Varianten (USA, UK, Australien, Indien), Spanisch-Dialekte (Spanien, Mexiko, Argentinien, Kolumbien), Chinesische Varianten (Mandarin, Kantonesisch, Taiwanesisch)
Mehrsprachige Meetings: Automatischer Sprachenwechsel während der Aufnahme mit mehreren Sprachen, Sprechererkennung behält Sprachzuordnung bei, auch in unterschiedlichen Sprachen.
Spezialisierte Unterstützung: Memoro versteht die verschiedensten Fachsprachen: Medizinische Terminologie, Technisches Vokabular, Juristische Fachsprache, und viele mehr, je nach gewähltem Aufnahme-Blueprint.
Feature: Audio-Archiv
Beschreibung: Ihre persönliche Audioarchiv - alle Aufnahmen sicher gespeichert und jederzeit verfügbar
Hauptfunktionen:
Vollständiger Überblick über alle lokal gespeicherten Aufnahmen
Detaillierte Statistiken über Länge und Dateigröße der Aufnahmen
Einfaches exportieren der Audioaufnahmen, einfach über den Teilen Button im Audioplayer
Hilfreich für Vielnutzer, Projektmanager, Compliance
Feature: Recording Blueprints (Aufnahme Vorlagen)
Beschreibung: Vordefinierte Aufnahmevorlagen für perfekt strukturierte Ergebnisse
Hauptfunktionen:
Etliche Vorlagen für die verschiedensten Anwendungsfälle
Vorlagen für verschiedene Branchen: Von Bildung an Universitäten oder Schulen und Kindergärten, über Pflegedokumentation an Krankenhäusern oder Altersheimen, zu Baubesprechungen von Handwerkern oder Architekten, bis zur Dokumentation von Familiengeschichten. Und etliche weitere spezialisierte Vorlagen und Industrien. Psst: Bald können auch eigene Blueprints erstellt werden.
Feedback Funktion an Memoro, um den Blueprint zu verbessern.
Feature: Audio Upload
Beschreibung: Lade deine Aufnahmen hoch und lass sie automatisch analysieren
Hauptfunktionen:
MP3, WAV und M4A Formate unterstützt
Unbegrenzte Aufnahmelänge
Automatische Transkription
Einstellungen für Datum, Uhrzeit, Sprache und Vorlagen
Sofortige KI-Analyse
Verschlüsselte Übertragung
Sichere Cloud-Speicherung
Organization Features
Feature: Memories
Beschreibung: Entdecke die verschiedenen Analysen, die Memoro von deinen Gesprächen erstellt
Hauptfunktionen:
Explorative Memories: Erkenntnisse: Wichtige Punkte chronologisch zusammengefasst, Zusammenfassung: Automatische Formatierung als Protokoll, Vortrag oder Formular, Artikel: Strukturierte Aufbereitung der Inhalte
Exekutive Memories: Aufgaben: Erfasst Verantwortliche, Deadlines und Status, Termine: Sammelt Datum, Ort und Beschreibung
Feature: Tags zur Organisation
Beschreibung: Sortiere und finde deine Gespräche blitzschnell mit Tags und intelligentem Filter
Hauptfunktionen:
Tag-System mit flexibler Kategorisierung
Eigene Namen, Farbe und Emojis vergeben
Verschiedene Anwendungsfälle für Tags, zum Beispiel für Status Tracking wie “Offen” und “Erledigt”, oder für Themenschwerpunkte wie “Technologie” und “Sport”, oder für Arten von Gesprächen oder Gesprächspartnern.
Mehrere Tags pro Memo: Baue dein eigenes Memo Organisationssystem, und filtere es dynamisch auf der Memos Seite
Feature: Memo-Kombination
Beschreibung: Füge mehrere Memos zu einem umfassenden Dokument zusammen
Hauptfunktionen:
Mehrere Memos in einer neuen Memo kombinieren
Perfekt für mehrteilige Meetings, Geschichten, oder Sammlungen von Informationen.
Stelle deiner kombinierten Memo Fragen oder erstelle neue Memories.
Feature: Mehrsprachig auf allen Ebenen
Memoro unterstützt 24 Sprachen und zwei Dialekte für eine globale Kommunikation
Unterstützte Sprachen:
Europäische Sprachen: Deutsch, Englisch, Französisch, Spanisch, Italienisch, Portugiesisch, Niederländisch, Schwedisch, Polnisch
Osteuropäische: Russisch, Ukrainisch, Tschechisch
Asiatische Sprachen: Hindi, Chinesisch (Mandarin), Japanisch, Koreanisch, Vietnamesisch
Nahost & Afrika: Arabisch, Türkisch, Hebräisch
Feature: Übersetzen
Beschreibung: Überwinde Sprachbarrieren mit automatischer Übersetzung in 24 Sprachen
Hauptfunktionen:
Automatische Übersetzung in 24 Sprachen
Zweisprachige Ansicht
Erhalt des ursprünglichen Kontexts
Erkennt Fachbegriffe
Berücksichtigt kulturelle Nuancen
Sharing Features
Feature: Memos teilen
Beschreibung: Teile deine Gespräche nahtlos über alle deine Apps
Hauptfunktionen:
Teile deine Memo direkt über alle gängigen Kanäle wie Mail, Whatsapp, Telegram oder kopiere sie in ein Dokument
Nutze die Webapp unter web.memoro.ai um deine Inhalte schnell auf dem Computer oder anderen Geräten aufzurufen.
Schnelles kopieren des Transkriptes
Feature in Arbeit: Teilen von Memos in Memoro - gemeinsames Bearbeiten und erweitern der Memo
Customization Features
Feature: Mana Credits System
Beschreibung: Flexibles KI-Credit-System für jeden Bedarf
Vorteile:
- Fair: Zahlen Sie nur für das, was Sie nutzen
- Skalierbar: Verschiedene Pakete für jeden Bedarf
- Transparent: Klare Kostenübersicht
- Flexibel: Monatlich oder jährlich, plus Einmalkäufe
- 33% Rabatt bei Jahresabonnements
Mana-Kosten für Memoro:
- Aufnahme/Transkription: 2 Mana pro Minute (mindestens 10 Mana)
- Frage an Memo stellen: 5 Mana pro Frage
- Neue Memory erstellen: 5 Mana
- Blueprint anwenden: 5 Mana
- Memos kombinieren: 5 Mana pro kombiniertes Memo
Mana-Abonnements:
Mana Tropfen (Kostenlos) - 0 € / Monat:
- 150 Mana Startguthaben
- 5 Mana täglich (max. 150 Mana Guthaben)
- Perfekt zum Ausprobieren
Mana Fluss - 5,99 € / Monat (47,99 € / Jahr):
- 600 Mana Startguthaben
- 20 Mana täglich (max. 1.200 Mana Guthaben)
- Mana verschenken möglich
Mana Strom - 14,99 € / Monat (119,99 € / Jahr):
- 1.500 Mana Startguthaben
- 50 Mana täglich (max. 3.000 Mana Guthaben)
- Mana verschenken möglich
Mana See - 29,99 € / Monat (239,99 € / Jahr):
- 3.000 Mana Startguthaben
- 100 Mana täglich (max. 6.000 Mana Guthaben)
- Mana verschenken möglich
Mana Meer - 49,99 € / Monat (399,99 € / Jahr):
- 5.000 Mana Startguthaben
- 200 Mana täglich (max. 10.000 Mana Guthaben)
- Mana verschenken möglich
Mana-Pakete (Einmalkauf):
- Kleiner Mana Trank: 350 Mana für 4,99 €
- Mittlerer Mana Trank: 700 Mana für 9,99 €
- Großer Mana Trank: 1.400 Mana für 19,99 €
- Riesiger Mana Trank: 2.800 Mana für 39,99 €
Feature: Memoro anpassen
Beschreibung: Passen Sie Memoro perfekt an Ihre Bedürfnisse an
Anpassungsoptionen:
Erscheinungsbild: Dunkel & Hell Modus
Anzeigeelemente Aufnahme Seite: Ein und ausblenden von verschiedenen Elementen auf der Aufnahmeseite
Oberflächensprache (48 verschiedene), Aufnahme Sprache (82 verschiedene Sprachen), Übersetzungssprachen (57 verschiedene Sprachen zum Übersetzen)
Datenschutz & Sicherheit: Standortspeicherung opt-in, Analysedaten opt-out
Feature: Branchenspezifische Anpassung
Beschreibung: Maßgeschneiderte Lösungen für deine Branche mit spezialisierten Vorlagen und Integrationen
Hauptfunktionen:
Spezialisierte Vorlagen für verschiedene Branchen
Systemintegration mit APIs
Individuelle Anpassungen
Spezielle Compliance Lösungen
Branchenlösungen für: Gesundheitswesen, Rechtsberatung, Finanzdienstleistungen, Bildung, Consulting, Immobilien, IT & Software
Analytics Features
Feature: Detaillierte Statistiken
Beschreibung: Umfassende Einblicke in Ihre Produktivität und Nutzungsmuster
Statistik-Features:
Memos-Metriken: Gesamtanzahl, Länge, Wortanzahl, Trends, etc
Aufnahmedauer-Analysen: Gesamtzeit, Durchschnittliche Länge, etc
Wörter-Statistiken: Gesamtwortanzahl, Wörter pro Memo, etc
Erweiterte Analysen: Zeitverteilung, Tag-Analyse, Produktivitätsmuster, etc

View file

@ -1,301 +0,0 @@
# Das ist Memoro
# Memoro App: Revolutionierung der Gesprächsdokumentation und Gedankenerfassung
## 1\. Einführung
Memoro ist eine innovative mobile Anwendung, die darauf abzielt, die Art und Weise, wie Menschen Gespräche dokumentieren und Gedanken festhalten, zu revolutionieren. Entwickelt als Antwort auf die Herausforderungen des manuellen Mitschreibens und der Protokollführung, bietet Memoro eine intuitive Lösung für die automatisierte Erfassung, Transkription und Zusammenfassung von gesprochenen Inhalten.
## 2\. Hauptfunktionen
- **Aufnahme**: Ein zentraler Aufnahmeknopf ermöglicht das einfache Starten und Beenden von Aufnahmen.
- **Transkription**: Memoro transkribiert das Gesprochene Wort für Wort und erkennt dabei verschiedene Sprecher.
- **Zusammenfassung**: Die App fasst den Inhalt automatisch zusammen und extrahiert wichtige Informationen wie Aufgaben, Termine und Erkenntnisse.
- **Mehrsprachigkeit**: Unterstützung von 82 Sprachen bei der Aufnahme und Transkription. 56 Sprachen für die Übersetzung und 48 Sprachen für die App Oberfläche.
## 3\. Besondere Merkmale
- **Einfache Bedienung**: Die App ist intuitiv gestaltet und erfordert minimales Onboarding.
- **Sprachbarrieren überwinden**: Memoro unterstützt die Integration ausländischer Fachkräfte durch mehrsprachige Funktionen.
- **Fokus auf Face-to-Face-Gespräche**: Optimiert für persönliche Interaktionen und Gedankenerfassung.
- **Branchenspezifische Modi**: Anpassbare Modi für verschiedene Berufsgruppen und Anwendungsfälle.
- **Made in Germany**: Entwickelt nach höchsten Sicherheitsstandards mit Datenspeicherung in Deutschland.
## 4\. Zielgruppen und Anwendungsfälle
- **Studierende**: Vorlesungsmitschriften, Brainstorming für Abschlussarbeiten
- **Büroarbeiter**: Meetingprotokolle, E-Mail-Diktate
- **Handwerker**: Materiallisten, Baubesprechungen
- **Pflegekräfte**: Maßnahmenplanung, Dokumentation
- **Marketingexperten**: Interviewführung und \-analyse
- **Kreative**: Ideensammlung und Selbstreflexion
## 5\. Technische Details
- **Plattformen**: iOS (Apple App Store) und Android (Google Play Store)
- **iOS App**: https://apps.apple.com/de/app/memoro/id6451258411
- **Android App**: https://play.google.com/store/apps/details?id=com.memo.beta
- **Desktop-Zugang**: Lesefunktion für aufgenommene Memos
- **Integrationen**: Einfache Übertragung in andere Anwendungen durch Textformat
- **Erklärvideos**:
- **Deutsch**: https://www.youtube.com/watch?v=YTVbhzPY9eI (21.02.2024)
- **Englisch**: https://www.youtube.com/watch?v=bqDx4_V-zZU (21.02.2024)
## 6\. Preismodell
- **Kostenlose Version**: 180 Minuten/Monat, max. 50 Minuten/Aufnahme, 22 Memos/Tag
- **Plus**: 9€/Monat (600 Minuten/Monat, 100 Minuten/Memo)
- **Pro**: 23€/Monat (1800 Minuten/Monat, 200 Minuten/Memo)
- **Ultra**: 42€/Monat (4800 Minuten/Monat, 300 Minuten/Memo)
- 20% Rabatt bei jährlicher Abrechnung
- Sonderkonditionen für Bildungseinrichtungen und gemeinnützige Organisationen
## 7\. Datenschutz und Sicherheit
- DSGVO- und GDPR-konform
- Daten werden ausschließlich in Deutschland gespeichert
- Ende-zu-Ende-Verschlüsselung bei der Übertragung
- Tägliche Backups
- Keine Weitergabe an Dritte
- On-Premise-Lösungen für Unternehmen verfügbar
## 8\. Unternehmenswerte und Vision
- Inklusivität und Barrierefreiheit
- Verbesserung der Kommunikation und des gegenseitigen Verständnisses
- Demokratisierung des Zugangs zu KI-gestützten Dokumentationstools
- Fokus auf Zeitersparnis und effizienteres Arbeiten
## 9\. Erfolgsgeschichten und Statistiken
- 800 aktive Nutzer, davon 100 wöchentliche Nutzer
- Geschätzte Zeitersparnis von 2-6 Stunden pro Woche je nach Branche
- Reduzierung der Dokumentationsarbeit um bis zu 75%
- Erfolgreiche Anwendung bei Abschlussarbeiten und Forschungsprojekten
## 10\. Zukunftspläne und Entwicklung
- Kontinuierliche Verbesserung der Analysen
- Entwicklung neuer branchenspezifischer Modi
- Enge Zusammenarbeit mit Nutzern zur Produktoptimierung
- Anpassung an individuelle Kundenwünsche
## 11\. Kundenservice und Support
- Direkte Unterstützung über [kontakt@memoro.ai](mailto:kontakt@memoro.ai)
- Ausführliche Online-Dokumentation
- Schnelle Reaktion auf Nutzerfeedback und \-probleme
## 12\. Marketing und Kommunikation
- **Kernbotschaften**: Freiheit, Sicherheit, Ruhe, Verständnis
- **Emotionale Assoziationen**: Konzentration auf den Moment, Überwindung von Sprachbarrieren, verbesserte Selbstreflexion
- **Terminologie**:
- "Memoro" \- die App
- "Memos" \- einzelne Aufnahmen oder Analysen
- "Memories" \- Analyseabschnitte (z.B. Erkenntnisse, Termine, Aufgaben)
Memoro positioniert sich als unverzichtbares Tool für effiziente Kommunikation und Dokumentation in einer zunehmend globalisierten und schnelllebigen Arbeitswelt. Durch seine Benutzerfreundlichkeit, Mehrsprachigkeit und branchenübergreifende Anwendbarkeit hat Memoro das Potenzial, die Art und Weise, wie wir Informationen erfassen und verarbeiten, grundlegend zu verändern.
Memoro Mehrwert:
1. Definition und Bedeutung von Mehrwert:
- Mehrwert wird definiert als alle Vorteile und Nutzen, die zusätzlich zu einem Produkt oder einer Dienstleistung entstehen. Dies umfasst nicht nur die Funktionalität eines Produkts oder einer App, sondern auch alle damit verbundenen positiven Auswirkungen.
- Der Mehrwert ist entscheidend für Marketing, Vertrieb, Schaffung von Wettbewerbsvorteilen, Kundenbindung und Rentabilität. Ein Unternehmen, das den Mehrwert seiner Produkte und Dienstleistungen gut kennt und nutzt, kann erfolgreicher agieren.
2. Arten von Mehrwert:
- Mehrwert durch das Produkt selbst: Dies bezieht sich auf die grundlegenden Funktionen und Anwendungen eines Produkts oder einer App. Hier wurde diskutiert, wie ein Produkt spezifische Probleme löst und welche Vorteile es bietet.
- Mehrwert durch Prozesse rund um das Produkt: Dies umfasst die Vorteile, die durch die Nutzung des Produkts entstehen, sowie die Verbindungen und Prozesse, die dadurch optimiert werden.
- Mehrwert durch Personen im Unternehmen: Der Fokus lag auf den Kenntnissen, Erfahrungen und Fähigkeiten der Mitarbeiter, die sie in ihrem Bereich und möglicherweise auch außerhalb davon anbieten können.
- Mehrwert durch den entstehenden Profit für Kunden: Dies bezieht sich auf den finanziellen Gewinn, den Kunden durch die Nutzung eines Produkts erzielen, sowie andere Arten von Mehrwert, die nicht direkt monetär sind.
3. Nutzung und Ergebnisse von Mehrwert:
- Mehr Zeit für die eigentliche Arbeit: Durch die Nutzung des Produkts werden repetitiven und zeitaufwändigen Aufgaben reduziert, wodurch mehr Zeit für die Kernaufgaben bleibt.
- Weniger nervige Arbeit: Insbesondere das Führen von Protokollen wird erleichtert, was oft als unangenehme Pflicht empfunden wird.
- Weniger Missverständnisse und Stress in Gesprächen: Die Verfügbarkeit von Protokollen und Aufzeichnungen führt zu klareren Kommunikationen und weniger Missverständnissen.
- Unmittelbare Verfügbarkeit von Informationen: Protokolle und Aufzeichnungen stehen sofort zur Verfügung, was die Effizienz und Transparenz verbessert.
- Verbesserte Qualität der Arbeit: Durch detailliertere und spezifischere Dokumentationen wird die Arbeitsqualität gesteigert.
4. Erfahrungen und Wissen im Unternehmen:
- Breites Wissen in kreativem Design: Das Unternehmen verfügt über umfangreiche Kenntnisse in Bereichen wie Brand Design, Grafikdesign, UX/UI-Design, Film, Musik und Licht.
- Rapid Prototyping: Die Fähigkeit, schnell Prototypen zu erstellen und zu testen, wurde als eine der Kernkompetenzen hervorgehoben.
- KI-Kompetenzen: Das Unternehmen hat tiefgehende Erfahrungen in der Anwendung und Entwicklung von Künstlicher Intelligenz und weiß, welche Technologien zukunftsträchtig sind und welche vermieden werden sollten.
- Kundennähe und proaktive Zusammenarbeit: Eine enge Zusammenarbeit mit Kunden und die Fähigkeit, sich schnell auf deren Bedürfnisse einzustellen, wurden als besondere Stärken genannt.
- Marktkenntnis: Das Unternehmen kennt den deutschen und Schweizer Markt gut und kann seine Produkte entsprechend anpassen.
5. Vermittlung der Expertise:
- Demos und Workshops: Die Durchführung von Demonstrationen und Workshops, um die Funktionalität und Vorteile des Produkts zu zeigen.
- Nutzung der App als Beispiel: Die App selbst dient als lebendiges Beispiel für die eigenen Kompetenzen.
- Networking und Veranstaltungen: Teilnahme an und Organisation von Events wie Hackathons zur Förderung von Kollaborationen und Innovationsaustausch.
- Webseite und Social Media: Nutzung von Blogs, LinkedIn und anderen Plattformen, um Wissen und Updates zu teilen.
6. Emotionale Aspekte der Nutzung:
- Erleichterung und Sicherheit: Nutzer fühlen sich erleichtert und sicher, da sie wissen, dass wichtige Informationen aufgezeichnet und zugänglich sind.
- Selbstbestimmtheit und Selbstwertgefühl: Die App stärkt das Selbstvertrauen und das Gefühl der Selbstbestimmtheit, da Nutzer ihre Gedanken und Ideen festhalten können.
- Gemeinschaftsgefühl: Die Möglichkeit, Wissen und Informationen zu teilen, fördert ein Gefühl der Verbundenheit und Zusammenarbeit.
- Vorfreude und Motivation: Nutzer freuen sich auf regelmäßige Updates und Verbesserungen, die die App noch nützlicher machen.
- Professionelle Unterstützung: Die App wird als professionelles Tool wahrgenommen, das im Alltag eine wertvolle Unterstützung bietet.
- Ermöglichung und Empowerment: Insbesondere für Nutzer mit sprachlichen oder technischen Einschränkungen bietet die App die Möglichkeit, sich besser auszudrücken und einzubringen.
- Gelassenheit und Ausgeglichenheit: Die Nutzung der App trägt zu einem Gefühl der Ruhe und Ausgeglichenheit bei, da Nutzer wissen, dass wichtige Informationen sicher gespeichert sind.
- Aktivierung und Entdeckungslust: Die Möglichkeit, jederzeit Gedanken und Ideen festzuhalten, motiviert die Nutzer, aktiv zu bleiben und Neues zu entdecken.
# Das Memoro Team
Unser Team besteht aus erfahrenen Experten aus den Bereichen künstliche Intelligenz, Softwareentwicklung, Datensicherheit und Branchenspezialisten. Gemeinsam arbeitet das Team daran, Memoro kontinuierlich zu verbessern und an die Bedürfnisse der Kunden anzupassen.
## Till Schneider
Till Schneider bringt als erfahrener Filmemacher und Mediendesigner eine Fülle von Kreativität und technischem Know-how in das Memoro-Team ein. Mit über einem Jahrzehnt Erfahrung in der digitalen Medienproduktion hat Till eine beeindruckende Karriere aufgebaut, die ihn optimal für seine Rolle bei Memoro qualifiziert.
Tills Expertise erstreckt sich über ein breites Spektrum der digitalen Medienlandschaft. Seine Leidenschaft für innovative Technologien und interaktive Medien spiegelt sich in seiner Arbeit wider. Er hat maßgeblich an der Entwicklung eines interaktiven Film-Tools mitgewirkt, was seine Fähigkeit zur Konzeption und Umsetzung komplexer digitaler Projekte unter Beweis stellt. Diese Erfahrung ist besonders wertvoll für die kontinuierliche Verbesserung und Erweiterung der Memoro-App.
In seiner Karriere hat Till über 80 selbständige Filmproduktionen realisiert, darunter Projekte für namhafte Unternehmen und öffentliche Einrichtungen. Diese Vielseitigkeit in der Projektarbeit hat sein Verständnis für unterschiedliche Kundenbedürfnisse geschärft eine Fähigkeit, die bei der Weiterentwicklung von Memoro von unschätzbarem Wert ist. Seine Erfahrung in der Kundenbetreuung und \-beratung ermöglicht es dem Team, die Anforderungen der Nutzer besser zu verstehen und in die Produktentwicklung einfließen zu lassen.
Tills akademischer Hintergrund in Mediendesign, mit Schwerpunkten in UX/UI Design, Bewegtbild und interaktiven Medien, bildet eine solide Grundlage für die Gestaltung benutzerfreundlicher und ansprechender Interfaces. Seine Bachelorarbeit, die sich mit der Gestaltung einer Lernplattform befasste, zeigt sein Interesse an der Entwicklung von Tools, die das Lernen und die Informationsverarbeitung erleichtern ein Kernaspekt der Mission von Memoro.
Als ehemaliger Geschäftsführer eines kleinen Teams bringt Till wertvolle Führungserfahrung mit. Er versteht die Herausforderungen der Teamkoordination und Projektleitung, was für die agile Entwicklung und das Wachstum von Memoro von großer Bedeutung ist. Seine Fähigkeit, komplexe Projekte von der Konzeption bis zur Umsetzung zu begleiten, trägt wesentlich zur effizienten Realisierung der Unternehmensziele bei.
Tills technische Fähigkeiten umfassen den professionellen Umgang mit verschiedenen Kamerasystemen, fortgeschrittene Kenntnisse in der Postproduktion von Film und Fotografie sowie Expertise in Branding und Corporate Design. Diese vielseitigen Kompetenzen ermöglichen es ihm, bei Memoro in verschiedenen Bereichen wertvolle Beiträge zu leisten, von der visuellen Gestaltung der App bis hin zur Erstellung von Marketingmaterialien.
Mit seiner Kombination aus kreativer Vision, technischem Fachwissen und Führungserfahrung ist Till Schneider eine treibende Kraft hinter der Innovation bei Memoro. Seine Fähigkeit, komplexe Ideen in benutzerfreundliche Lösungen zu übersetzen, macht ihn zu einem unverzichtbaren Mitglied des Teams, das kontinuierlich daran arbeitet, die Memoro-App zu verbessern und an die sich wandelnden Bedürfnisse der Nutzer anzupassen.
## Tobias Müller
Tobias Müller bringt als Full-Stack-Entwickler eine beeindruckende Bandbreite an technischen Fähigkeiten und Projekterfahrungen in das Memoro-Team ein. Mit seinem Hintergrund in Software Engineering und seiner Expertise in modernen Web-Technologien ist er bestens gerüstet, um die technische Entwicklung und Innovation bei Memoro voranzutreiben.
Tobias' Fachkenntnisse umfassen ein breites Spektrum an Programmiersprachen und Frameworks, mit besonderem Fokus auf JavaScript-basierte Technologien wie React.js, Vue.js und Node.js. Diese Expertise ist von unschätzbarem Wert für die Weiterentwicklung und Optimierung der Memoro-App, insbesondere im Hinblick auf Benutzerfreundlichkeit und Leistungsfähigkeit. Seine Erfahrung mit SQL- und NoSQL-Datenbanken ermöglicht es ihm, effiziente und skalierbare Datenlösungen für Memoro zu implementieren, was angesichts der Datenintensität der App von großer Bedeutung ist.
In seiner bisherigen Laufbahn hat Tobias an einer Vielzahl anspruchsvoller Projekte mitgewirkt, die seine Fähigkeit zur Umsetzung komplexer Softwarelösungen unter Beweis stellen. Besonders hervorzuheben ist seine Erfahrung in der Entwicklung von KI-gesteuerten Assistenten und in der Konzeption neuer Software-Architekturen. Diese Kompetenzen sind für Memoro von großem Wert, da sie direkt zur Verbesserung der KI-gestützten Transkriptions- und Zusammenfassungsfunktionen der App beitragen können.
Ein besonderer Mehrwert liegt in Tobias' Erfahrung mit Cloud-Technologien, insbesondere mit Azure und Google Cloud. Diese Expertise ist entscheidend für die Skalierbarkeit und Zuverlässigkeit der Memoro-Infrastruktur. Seine Kenntnisse in Bereichen wie CI/CD-Pipelines und Containerisierung mit Docker tragen dazu bei, die Entwicklungs- und Bereitstellungsprozesse bei Memoro zu optimieren und zu beschleunigen.
Mit seiner Kombination aus technischer Expertise, Projekterfahrung und innovativem Denken ist Tobias Müller hervorragend positioniert, um wesentlich zur technologischen Weiterentwicklung von Memoro beizutragen. Seine Fähigkeit, komplexe technische Herausforderungen zu meistern und dabei stets den Nutzen für den Endanwender im Blick zu behalten, macht ihn zu einem wertvollen Mitglied des Memoro-Teams.
# Aleksandra Vasileva
Aleksandra Vasileva, die von allen Alex genannt wird, bringt eine einzigartige Kombination aus Marketing-Expertise und Medienerfahrung in das Memoro-Team ein. Mit ihrem Hintergrund in Internet und Online Marketing sowie ihrer praktischen Erfahrung in der TV-Produktion ist Alex bestens gerüstet, um Memoros Marktpräsenz zu stärken und die Nutzerkommunikation zu optimieren.
Alex' Studium des Internet und Online Marketings an der Hochschule Ravensburg-Weingarten hat ihr ein solides Fundament in den Bereichen Social Media Marketing, Online Marketing und Projektmanagement im Marketing vermittelt. Diese Kenntnisse sind von unschätzbarem Wert für Memoro, insbesondere wenn es darum geht, die App einem breiteren Publikum zugänglich zu machen und die Nutzerbindung zu erhöhen.
Ihre Erfahrung als Junior TV Production Manager bei Regio TV Bodensee hat Alex' Fähigkeiten in der Content-Erstellung und \-Organisation geschärft. Die Erstellung von Programmlisten und Sendungsinhalten sowie die Organisation von Online-Inhalten mit WordPress sind Kompetenzen, die direkt auf die Content-Strategie von Memoro übertragen werden können. Diese Fähigkeiten sind besonders wertvoll, um die Benutzeroberfläche der App intuitiv und informativ zu gestalten und regelmäßige Updates für die Nutzer zu planen.
Während ihres Praktikums bei Bitzilla GmbH sammelte Alex wertvolle Erfahrungen in der Entwicklung und Umsetzung von Social Media Marketing Kampagnen. Ihre Fähigkeit, Events zu organisieren und Content-Marketing-Pläne zu erstellen, wird Memoro dabei helfen, eine kohärente und ansprechende Online-Präsenz aufzubauen. Die Erstellung von Social Media Inhalten, Beiträgen und Bildern ist eine Kompetenz, die für die Vermarktung von Memoro in verschiedenen digitalen Kanälen von großer Bedeutung ist.
Alex' Verständnis für SEO und professionelles Schreiben für Websites, Apps und Blogs ist ein wesentlicher Vorteil für Memoro. Diese Fähigkeiten können genutzt werden, um die Sichtbarkeit der App in Suchmaschinen zu verbessern und ansprechende, informative Inhalte für potenzielle und bestehende Nutzer zu erstellen.
Ihre Erfahrung in der Usability-Testbegleitung ist besonders wertvoll für Memoro. In Zusammenarbeit mit den Entwicklern kann Alex dazu beitragen, die Benutzerfreundlichkeit der App kontinuierlich zu verbessern und sicherzustellen, dass sie den Bedürfnissen und Erwartungen der Nutzer entspricht.
Alex' mehrsprachige Fähigkeiten sie spricht fließend Bulgarisch, Englisch und Deutsch sind ein großer Vorteil für Memoro, insbesondere im Hinblick auf die internationale Ausrichtung und Expansion des Unternehmens. Ihre Fähigkeit, in verschiedenen Sprachen zu kommunizieren, kann dazu beitragen, Marketingmaterialien und Nutzerkommunikation für verschiedene Märkte zu lokalisieren und anzupassen.
Ihre vielfältigen Interessen, die von Geschichte über Nachhaltigkeit bis hin zu Physik und Astrophysik reichen, verleihen Alex eine breite Perspektive, die bei der Entwicklung von Marketingstrategien für verschiedene Zielgruppen von Vorteil sein kann. Dies ist besonders relevant für Memoro, da die App in verschiedenen Bereichen und von unterschiedlichen Nutzergruppen eingesetzt werden kann.
Mit ihrer Kombination aus theoretischem Wissen und praktischer Erfahrung im digitalen Marketing, gepaart mit ihren Fähigkeiten in der Medienproduktion, ist Alex hervorragend positioniert, um die Marketingbemühungen von Memoro zu leiten und zu optimieren. Ihre Kreativität, ihr technisches Verständnis und ihre kommunikativen Fähigkeiten machen sie zu einem wertvollen Mitglied des Teams, das wesentlich zur Steigerung der Bekanntheit und Attraktivität der Memoro-App beitragen kann.
## Das Umfeld und die Community von Memoro
# Unsere Zielgruppe
# Datenschutz, Sicherheit und Infrastruktur
## Einleitung
Memoro, eine innovative App zur Gesprächsdokumentation und Gedankenerfassung, legt höchsten Wert auf Datenschutz und Sicherheit. Als in Deutschland entwickelte Anwendung erfüllt Memoro die strengsten Qualitäts- und Sicherheitsstandards. Dieses Dokument bietet einen umfassenden Überblick über die Datenschutzpraktiken, Sicherheitsmaßnahmen und die zugrunde liegende Infrastruktur von Memoro.
## Datenschutz
Memoro ist vollständig DSGVO- und GDPR-konform. Das Unternehmen hat strikte Datenschutzrichtlinien implementiert, um die Privatsphäre seiner Nutzer zu schützen. Alle personenbezogenen Daten werden mit größter Sorgfalt behandelt und ausschließlich für die vorgesehenen Zwecke verwendet.
Folgende Datenschutzmaßnahmen sind bei Memoro implementiert:
- Daten werden ausschließlich in Deutschland gespeichert, was eine hohe Datensicherheit und die Einhaltung strenger EU-Datenschutzgesetze gewährleistet.
- Es erfolgt keine Weitergabe von Nutzerdaten an Dritte, es sei denn, dies ist gesetzlich vorgeschrieben oder der Nutzer hat ausdrücklich zugestimmt.
- Nutzer haben volle Kontrolle über ihre Daten und können jederzeit Auskunft, Berichtigung oder Löschung ihrer personenbezogenen Daten verlangen.
## Sicherheit
Die Sicherheit der Nutzerdaten hat bei Memoro oberste Priorität. Das Unternehmen setzt modernste Sicherheitstechnologien ein, um die Integrität und Vertraulichkeit aller gespeicherten und übertragenen Daten zu gewährleisten.
Zu den wichtigsten Sicherheitsmaßnahmen gehören:
- Ende-zu-Ende-Verschlüsselung bei der Datenübertragung, um unbefugten Zugriff zu verhindern.
- Tägliche Backups aller Daten, um Datenverlust zu vermeiden und eine schnelle Wiederherstellung im Notfall zu ermöglichen.
- Strenge Zugriffskontrollen und Berechtigungsmanagement innerhalb des Unternehmens, um sicherzustellen, dass nur autorisiertes Personal Zugang zu sensiblen Daten hat.
- Regelmäßige Sicherheitsaudits und Penetrationstests, um potenzielle Schwachstellen frühzeitig zu erkennen und zu beheben.
Für Unternehmen, die besonders hohe Sicherheitsanforderungen haben, bietet Memoro On-Premise-Lösungen an. Diese ermöglichen es Kunden, die Memoro-Infrastruktur in ihrer eigenen IT-Umgebung zu betreiben und so die volle Kontrolle über ihre Daten zu behalten.
## Infrastruktur
Die technische Infrastruktur von Memoro wurde sorgfältig konzipiert, um höchste Leistung, Skalierbarkeit und Sicherheit zu gewährleisten. Das Unternehmen nutzt eine Kombination aus eigenen Systemen und bewährten Cloud-Diensten, um eine zuverlässige und effiziente Plattform bereitzustellen.
Kernelemente der Memoro-Infrastruktur sind:
- Datenspeicherung: Memoro nutzt die Firebase Cloud (Google Cloud) für die Speicherung von Nutzerdaten, Eingaben, Abrechnungsdaten und Memos (einschließlich Transkripte und Zusammenfassungen). Die Datenbank-Server befinden sich in Frankfurt, Deutschland, was kurze Latenzzeiten für europäische Nutzer und die Einhaltung strenger EU-Datenschutzbestimmungen gewährleistet.
- Audioverarbeitung: Für die Transkription von Audiodateien verwendet Memoro Azure Speech, das ebenfalls in Frankfurt gehostet wird. Dies ermöglicht präzise und schnelle Transkriptionen bei gleichzeitiger Einhaltung der Datenschutzbestimmungen.
- KI-gestützte Analysen: Die Erstellung von Zusammenfassungen wird durch Azure OpenAI unterstützt, das in Paris gehostet wird. Wichtig zu betonen ist, dass die verarbeiteten Daten nicht zum Training der KI-Modelle genutzt werden, was den Schutz der Nutzerinformationen zusätzlich stärkt.
- Anwendungshosting: Die Memoro-App ist sowohl für iOS (über den Apple App Store) als auch für Android (über den Google Play Store) verfügbar. Zusätzlich gibt es einen Desktop-Zugang, der es Nutzern ermöglicht, ihre aufgenommenen Memos zu lesen und zu verwalten.
Die Infrastruktur von Memoro ist so konzipiert, dass sie problemlos skaliert werden kann, um mit dem Wachstum der Nutzerbasis Schritt zu halten. Regelmäßige Leistungsoptimierungen und Kapazitätserweiterungen stellen sicher, dass die App auch bei hoher Auslastung reibungslos funktioniert.
## Datenspeicherung und \-verarbeitung
Memoro verarbeitet verschiedene Arten von Daten, um seinen Nutzern einen umfassenden Service zu bieten. Dazu gehören:
- Sprachaufnahmen
- Transkriptionen der Sprachaufnahmen
- Zusammenfassungen der Transkriptionen
- Nutzungsdaten (z.B. IP-Adresse, Gerätetyp, Betriebssystem)
- Diagnosedaten und Fehlerberichte
Die Speicherdauer dieser Daten ist wie folgt geregelt:
- Nutzungsdaten werden maximal 26 Monate nach der Erfassung aufbewahrt.
- Sprachaufnahmen, Transkriptionen und Zusammenfassungen bleiben für die Dauer des Nutzungsverhältnisses gespeichert und werden spätestens 6 Monate nach Beendigung des Nutzungsverhältnisses gelöscht.
- Diagnosedaten und Fehlerberichte werden maximal 90 Tage nach der Erfassung aufbewahrt.
Bei den verwendeten Azure-Diensten gelten folgende Speicherfristen:
- Azure Speech: Daten werden maximal 31 Tage gespeichert.
- Azure OpenAI: Daten werden maximal 30 Tage gespeichert.
Diese Speicherfristen gewährleisten, dass Memoro seinen Nutzern einen zuverlässigen Service bieten kann, während gleichzeitig der Datenschutz gewahrt bleibt.
## Kontinuierliche Verbesserung und Nutzerfeedback
Memoro legt großen Wert auf die kontinuierliche Verbesserung seiner Dienste. Zu diesem Zweck werden anonymisierte Nutzungsanalysen durchgeführt, die dabei helfen, die App stetig zu optimieren und an die Bedürfnisse der Nutzer anzupassen. Hierbei kommt Google Analytics zum Einsatz, wobei streng darauf geachtet wird, dass keine personenbezogenen Daten in die Analysen einfließen.
Zur Erkennung und Analyse von App-Fehlern nutzt Memoro Firebase Crashlytics. Dies ermöglicht es dem Entwicklungsteam, auftretende Probleme schnell zu identifizieren und zu beheben, was zu einer stabilen und zuverlässigen Nutzererfahrung beiträgt.
Memoro ermutigt seine Nutzer aktiv, Feedback zu geben und Verbesserungsvorschläge einzureichen. Dieses Feedback wird sorgfältig geprüft und fließt in die Weiterentwicklung der App ein. Nutzer können sich jederzeit mit Fragen oder Anliegen an den Kundenservice unter [kontakt@memoro.ai](mailto:kontakt@memoro.ai) wenden.
## Kernbotschaften
Bei der Kommunikation über Memoros Datenschutz, Sicherheit und Infrastruktur sollten folgende Kernbotschaften im Mittelpunkt stehen:
1. **Made in Germany**: Memoro wird vollständig in Deutschland entwickelt und folgt den höchsten Qualitäts- und Sicherheitsstandards. Dies unterstreicht das Engagement für Exzellenz und Vertrauenswürdigkeit.
2. **DSGVO-Konformität**: Memoro erfüllt alle Anforderungen der Datenschutz-Grundverordnung (DSGVO). Dies gewährleistet, dass die Privatsphäre der Nutzer geschützt und ihre Rechte in Bezug auf ihre persönlichen Daten respektiert werden.
3. **Datenspeicherung in Deutschland**: Alle Nutzerdaten werden ausschließlich in deutschen Rechenzentren gespeichert. Dies garantiert die Einhaltung strenger europäischer Datenschutzgesetze und minimiert das Risiko des Zugriffs durch ausländische Behörden.
4. **Ende-zu-Ende-Verschlüsselung**: Memoro verwendet modernste Verschlüsselungstechnologien, um die Sicherheit der Nutzerdaten während der Übertragung zu gewährleisten. Dies schützt vor unbefugtem Zugriff und Datenmissbrauch.
5. **Keine Weitergabe an unbefugte Dritte**: Memoro verpflichtet sich, Nutzerdaten nicht an Dritte weiterzugeben, es sei denn, dies ist gesetzlich vorgeschrieben oder der Nutzer hat ausdrücklich zugestimmt. Dies unterstreicht das Engagement für den Schutz der Privatsphäre der Nutzer.
6. **Transparenz und Kontrolle**: Nutzer haben volle Kontrolle über ihre Daten und können jederzeit Auskunft, Berichtigung oder Löschung ihrer personenbezogenen Daten verlangen. Diese Transparenz fördert das Vertrauen in Memoro.
7. **Kontinuierliche Verbesserung**: Memoro investiert ständig in die Verbesserung seiner Sicherheitsmaßnahmen und Datenschutzpraktiken. Regelmäßige Audits und Updates gewährleisten, dass die App immer auf dem neuesten Stand der Technik ist.
8. **Branchenführende Technologien**: Durch die Nutzung von Azure Speech und Azure OpenAI bietet Memoro fortschrittliche KI-Funktionen, ohne dabei Kompromisse beim Datenschutz einzugehen. Die Daten werden nicht zum Training der KI-Modelle verwendet.
9. **Flexible Lösungen für Unternehmen**: Mit On-Premise-Optionen bietet Memoro auch für Unternehmen mit besonders hohen Sicherheitsanforderungen passende Lösungen. Dies unterstreicht die Anpassungsfähigkeit und das Verständnis für unterschiedliche Sicherheitsbedürfnisse.
10. **Vertrauenswürdiger Partner**: Memoro positioniert sich als vertrauenswürdiger Partner für Einzelpersonen und Unternehmen, der die Bedeutung von Datenschutz und Sicherheit in der digitalen Welt versteht und priorisiert.
Diese Kernbotschaften sollten in allen Kommunikationskanälen konsistent vermittelt werden, sei es in Marketingmaterialien, Kundengesprächen oder bei der Produktpräsentation. Sie unterstreichen Memoros Engagement für Datenschutz, Sicherheit und technologische Innovation und differenzieren das Unternehmen in einem zunehmend wettbewerbsintensiven Markt.
# Memoro Kontakt
Webseite: [https://www.memoro.ai/](https://www.memoro.ai/)
LinkedIn: [https://www.linkedin.com/company/memoroai](https://www.linkedin.com/company/memoroai)
Instagram: [https://www.instagram.com/memoroai/](https://www.instagram.com/memoroai/)
WhatsApp: [+41 79 370 88 99](https://wa.me/41793708899)
Adresse: Reichenaustraße 11a, 78467 Konstanz

View file

@ -1,242 +0,0 @@
# Blueprint-Ideen für das Handwerk - Handwerker (Final)
## Die 4 wichtigsten Blueprints - NUR mit den 8 verfügbaren System-Prompts (ohne Schlüsselpunkte)
---
## 1. Kundengespräch & Angebotserstellung / Customer Meeting & Quote Preparation
**Kategorie**: Handwerk
**Farbe**: #FF6F00
### Beschreibung
**Deutsch**: Dokumentiert Kundenwünsche, technische Anforderungen und Preisabsprachen. Erstellt strukturierte Grundlagen für Angebote und Auftragsbestätigungen.
**English**: Documents customer requirements, technical specifications, and price agreements. Creates structured foundations for quotes and order confirmations.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Kompakte Projektübersicht für die Kalkulation
2. **Ausführliche Zusammenfassung** (Sort Order: 2)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Detaillierte Kundenanforderungen und Leistungsumfang
3. **Aufgaben & Termine** (Sort Order: 3)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Besichtigungstermine, Lieferzeiten, Fertigstellungsdaten
4. **Offene Fragen** (Sort Order: 4)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Klärungsbedarf für Material, Technik oder Genehmigungen
---
## 2. Baustellendokumentation & Qualitätssicherung / Site Documentation & Quality Control
**Kategorie**: Handwerk
**Farbe**: #FF6F00
### Beschreibung
**Deutsch**: Erfasst Baufortschritt, Mängel, Abnahmen und wichtige Entscheidungen. Perfekt für Gewährleistung und Nachweise.
**English**: Records construction progress, defects, approvals, and important decisions. Perfect for warranty and documentation.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Tagesübersicht Baufortschritt
2. **Ausführliche Zusammenfassung** (Sort Order: 2)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Detaillierte Tagesprotokolle und Fortschrittsdokumentation
3. **Aufgaben & Termine** (Sort Order: 3)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Nacharbeiten, Abnahmetermine, Materialbestellungen
4. **Beantwortete Fragen & Antworten** (Sort Order: 4)
- Prompt ID: `47ce3340-e8c6-437c-928d-854c55589491`
- Memory Title: Q&A / Questions & Answers
- Technische Klärungen und Lösungen
---
## 3. Team-Besprechung & Arbeitsplanung / Team Meeting & Work Planning
**Kategorie**: Handwerk
**Farbe**: #FF6F00
### Beschreibung
**Deutsch**: Strukturiert Teambesprechungen, Arbeitseinteilung und Projektkoordination. Dokumentiert Zuständigkeiten und Arbeitsabläufe.
**English**: Structures team meetings, work allocation, and project coordination. Documents responsibilities and workflows.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Wochenplanung kompakt
2. **Aufgaben & Termine** (Sort Order: 2)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Arbeitseinteilung, Deadlines, Materialvorbereitung
3. **Gesammelte Ideen & Vorschläge** (Sort Order: 3)
- Prompt ID: `8cdc89a5-2f76-4d50-a93d-0c177c3e73ab`
- Memory Title: Ideen & Vorschläge / Ideas & Suggestions
- Prozessverbesserungen und Lösungsansätze
4. **Offene Fragen** (Sort Order: 4)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Klärungsbedarf mit Auftraggeber oder Lieferanten
---
## 4. Fachliche Weiterbildung & Schulungen / Professional Training & Education
**Kategorie**: Handwerk
**Farbe**: #FF6F00
### Beschreibung
**Deutsch**: Dokumentiert Schulungen, neue Techniken und Zertifizierungen. Erstellt Wissensdatenbank für Mitarbeiter und Nachweise für Kunden.
**English**: Documents training, new techniques, and certifications. Creates knowledge base for employees and proof for customers.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Schulung auf einen Blick
2. **Ausführliche Zusammenfassung** (Sort Order: 2)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Vollständige Schulungsdokumentation mit allen Details
3. **Beantwortete Fragen & Antworten** (Sort Order: 3)
- Prompt ID: `47ce3340-e8c6-437c-928d-854c55589491`
- Memory Title: Q&A / Questions & Answers
- Technisches Fachwissen zum Nachschlagen
4. **Blogbeitrag** (Sort Order: 4)
- Prompt ID: `2c6a6e47-1d0c-441f-9449-b5d908bffba2`
- Memory Title: Blogbeitrag / Blog Post
- Für Firmen-Website oder Kundennewsletter
---
## Implementierungsdetails
### JSON-Struktur für Supabase
```json
{
"category_id": "handwerk-category-id",
"blueprints": [
{
"name": {
"de": "Kundengespräch & Angebotserstellung",
"en": "Customer Meeting & Quote Preparation"
},
"description": {
"de": "Dokumentiert Kundenwünsche und erstellt Angebotsgrundlagen",
"en": "Documents customer requirements and creates quote foundations"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"c576e875-5a52-4f6a-abb7-0c62c945af78"
]
},
{
"name": {
"de": "Baustellendokumentation & Qualitätssicherung",
"en": "Site Documentation & Quality Control"
},
"description": {
"de": "Erfasst Baufortschritt, Mängel und Abnahmen",
"en": "Records construction progress, defects, and approvals"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"47ce3340-e8c6-437c-928d-854c55589491"
]
},
{
"name": {
"de": "Team-Besprechung & Arbeitsplanung",
"en": "Team Meeting & Work Planning"
},
"description": {
"de": "Strukturiert Teambesprechungen und Arbeitseinteilung",
"en": "Structures team meetings and work allocation"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"8cdc89a5-2f76-4d50-a93d-0c177c3e73ab",
"c576e875-5a52-4f6a-abb7-0c62c945af78"
]
},
{
"name": {
"de": "Fachliche Weiterbildung & Schulungen",
"en": "Professional Training & Education"
},
"description": {
"de": "Dokumentiert Schulungen und neue Techniken",
"en": "Documents training and new techniques"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"47ce3340-e8c6-437c-928d-854c55589491",
"2c6a6e47-1d0c-441f-9449-b5d908bffba2"
]
}
]
}
```
## Vorteile dieser finalen Version
### ✅ 100% Kompatibilität
- **NUR die 8 tatsächlich verfügbaren Prompts** werden verwendet (ohne Schlüsselpunkte)
- Keine Phantasie-IDs oder nicht existierende Prompts
- Sofort implementierbar ohne Backend-Änderungen
### ✅ Vollständige Abdeckung
- **Kundenbetreuung**: Von Erstgespräch bis Angebot
- **Projektdokumentation**: Lückenlose Baustellendokumentation
- **Teamorganisation**: Effiziente Arbeitsplanung
- **Qualifikation**: Weiterbildung und Zertifizierungen
### ✅ Praktischer Nutzen für Handwerker
- **Rechtssicherheit**: Dokumentation für Gewährleistung und Beweissicherung
- **Effizienz**: Strukturierte Arbeitsabläufe und klare Zuständigkeiten
- **Qualität**: Systematische Erfassung von Mängeln und Nacharbeiten
- **Kundenbindung**: Professionelle Dokumentation und Kommunikation
- **Wissensmanagement**: Schulungsinhalte und Best Practices im Team teilen
### ✅ Handwerksspezifische Anwendungsfälle
- Mängelprotokolle und Abnahmen
- Materialbestellungen und Liefertermine
- Arbeitszeiten und Leistungserfassung
- Technische Klärungen und Normen
- Kundenwünsche und Änderungen
- Sicherheitsunterweisungen
### ✅ Einfache Implementierung
- Direkt in Supabase einfügbar
- Keine neuen Prompts nötig
- Verwendet bestehende Infrastruktur
- Mehrsprachigkeit bereits integriert (DE, EN, IT, FR, ES)

View file

@ -1,244 +0,0 @@
# Blueprint-Ideen für den Büro-Kontext / Office (Final)
## Die 4 wichtigsten Blueprints - NUR mit den 8 verfügbaren System-Prompts (ohne Schlüsselpunkte)
---
## 1. Meeting-Protokoll & Follow-Up / Meeting Minutes & Follow-Up
**Kategorie**: Büro / Office
**Farbe**: #2196F3
### Beschreibung
**Deutsch**: Erstellt strukturierte Meeting-Protokolle mit Entscheidungen, Aufgaben und Terminen. Perfekt für effiziente Nachbereitung und klare Verantwortlichkeiten.
**English**: Creates structured meeting minutes with decisions, tasks, and deadlines. Perfect for efficient follow-up and clear responsibilities.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Meeting-Ergebnisse auf einen Blick
2. **Ausführliche Zusammenfassung** (Sort Order: 2)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Vollständiges Protokoll mit allen Diskussionspunkten
3. **Aufgaben & Termine** (Sort Order: 3)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Action Items mit Verantwortlichen und Deadlines
4. **Offene Fragen** (Sort Order: 4)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Themen für das nächste Meeting
---
## 2. Brainstorming & Ideenentwicklung / Brainstorming & Idea Development
**Kategorie**: Büro / Office
**Farbe**: #2196F3
### Beschreibung
**Deutsch**: Erfasst und strukturiert kreative Sessions, Workshops und Strategieentwicklung. Dokumentiert alle Ideen und priorisiert Umsetzungsschritte.
**English**: Captures and structures creative sessions, workshops, and strategy development. Documents all ideas and prioritizes implementation steps.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Session-Ergebnis kompakt
2. **Gesammelte Ideen & Vorschläge** (Sort Order: 2)
- Prompt ID: `8cdc89a5-2f76-4d50-a93d-0c177c3e73ab`
- Memory Title: Ideen & Vorschläge / Ideas & Suggestions
- Alle Konzepte nach Umsetzbarkeit sortiert
3. **Aufgaben & Termine** (Sort Order: 3)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Nächste Schritte zur Umsetzung
4. **Blogbeitrag** (Sort Order: 4)
- Prompt ID: `2c6a6e47-1d0c-441f-9449-b5d908bffba2`
- Memory Title: Blogbeitrag / Blog Post
- Für internes Wissensmanagement oder Intranet
---
## 3. Projektbesprechung & Statusupdate / Project Meeting & Status Update
**Kategorie**: Büro / Office
**Farbe**: #2196F3
### Beschreibung
**Deutsch**: Dokumentiert Projektfortschritt, Meilensteine und Hindernisse. Ideal für Steering Committees, Sprint Reviews und Stakeholder-Updates.
**English**: Documents project progress, milestones, and obstacles. Ideal for steering committees, sprint reviews, and stakeholder updates.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Projektstatus Executive Summary
2. **Ausführliche Zusammenfassung** (Sort Order: 2)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Detaillierter Fortschrittsbericht
3. **Aufgaben & Termine** (Sort Order: 3)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Meilensteine und kritische Pfade
4. **Offene Fragen** (Sort Order: 4)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Risiken und Eskalationsbedarf
---
## 4. Kommunikations-Content / Communication Content
**Kategorie**: Büro / Office
**Farbe**: #2196F3
### Beschreibung
**Deutsch**: Verwandelt Besprechungen und Präsentationen in professionelle Kommunikationsinhalte für verschiedene Kanäle und Zielgruppen.
**English**: Transforms meetings and presentations into professional communication content for various channels and audiences.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Management Summary für Führungsebene
2. **Blogbeitrag** (Sort Order: 2)
- Prompt ID: `2c6a6e47-1d0c-441f-9449-b5d908bffba2`
- Memory Title: Blogbeitrag / Blog Post
- Für Intranet oder Unternehmens-Blog
3. **Social Media Posts** (Sort Order: 3)
- Prompt ID: `b2e39e0a-ec1f-4d0e-813d-f1a08493332b`
- Memory Title: Social Media Posts
- LinkedIn, Twitter für Corporate Communications
4. **Ausführliche Zusammenfassung** (Sort Order: 4)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Hintergrundinformationen für Newsletter
---
## Implementierungsdetails
### JSON-Struktur für Supabase
```json
{
"category_id": "office-category-id",
"blueprints": [
{
"name": {
"de": "Meeting-Protokoll & Follow-Up",
"en": "Meeting Minutes & Follow-Up"
},
"description": {
"de": "Strukturierte Meeting-Protokolle mit Aufgaben und Entscheidungen",
"en": "Structured meeting minutes with tasks and decisions"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"c576e875-5a52-4f6a-abb7-0c62c945af78"
]
},
{
"name": {
"de": "Brainstorming & Ideenentwicklung",
"en": "Brainstorming & Idea Development"
},
"description": {
"de": "Erfasst kreative Sessions und priorisiert Umsetzungsschritte",
"en": "Captures creative sessions and prioritizes implementation steps"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"8cdc89a5-2f76-4d50-a93d-0c177c3e73ab",
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"2c6a6e47-1d0c-441f-9449-b5d908bffba2"
]
},
{
"name": {
"de": "Projektbesprechung & Statusupdate",
"en": "Project Meeting & Status Update"
},
"description": {
"de": "Dokumentiert Projektfortschritt und Meilensteine",
"en": "Documents project progress and milestones"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"c576e875-5a52-4f6a-abb7-0c62c945af78"
]
},
{
"name": {
"de": "Kommunikations-Content",
"en": "Communication Content"
},
"description": {
"de": "Professionelle Inhalte für verschiedene Kanäle",
"en": "Professional content for various channels"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"2c6a6e47-1d0c-441f-9449-b5d908bffba2",
"b2e39e0a-ec1f-4d0e-813d-f1a08493332b",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4"
]
}
]
}
```
## Vorteile dieser finalen Version
### ✅ 100% Kompatibilität
- **NUR die 8 tatsächlich verfügbaren Prompts** werden verwendet (ohne Schlüsselpunkte)
- Keine Phantasie-IDs oder nicht existierende Prompts
- Sofort implementierbar ohne Backend-Änderungen
### ✅ Vollständige Abdeckung
- **Meetings**: Effiziente Protokollierung und Nachverfolgung
- **Innovation**: Strukturierte Ideenentwicklung und Workshops
- **Projektmanagement**: Lückenlose Statusdokumentation
- **Kommunikation**: Multi-Channel Content-Erstellung
### ✅ Praktischer Nutzen für Büro-Mitarbeiter
- **Zeitersparnis**: Automatische Protokollerstellung statt manueller Notizen
- **Klarheit**: Eindeutige Aufgabenverteilung und Verantwortlichkeiten
- **Transparenz**: Nachvollziehbare Entscheidungen und Projektfortschritte
- **Effizienz**: Ein Gespräch, mehrere Outputs (Protokoll, Blog, Social Media)
- **Compliance**: Revisionssichere Dokumentation wichtiger Meetings
### ✅ Büro-spezifische Anwendungsfälle
- Board-Meetings und Vorstandssitzungen
- Agile Sprint Reviews und Retrospektiven
- Kundenpräsentationen und Pitches
- Strategieworkshops und OKR-Planungen
- Team-Meetings und Jour Fixes
- Change Management Kommunikation
- Internal Communications
- Stakeholder Updates
### ✅ Einfache Implementierung
- Direkt in Supabase einfügbar
- Keine neuen Prompts nötig
- Verwendet bestehende Infrastruktur
- Mehrsprachigkeit bereits integriert (DE, EN, IT, FR, ES)

View file

@ -1,234 +0,0 @@
# Blueprint-Ideen für den Universitären Kontext - Studenten (FINAL)
## Die 4 wichtigsten Blueprints - NUR mit den 8 verfügbaren System-Prompts
---
## 1. Vorlesungsanalyse / Lecture Analysis
**Kategorie**: Universität
**Farbe**: #9C27B0
### Beschreibung
**Deutsch**: Umfassende Analyse von Vorlesungen mit automatischer Erstellung von Zusammenfassungen und offenen Fragen für die Prüfungsvorbereitung.
**English**: Comprehensive analysis of lectures with automatic creation of summaries and open questions for exam preparation.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Kurzzusammenfassung** (Sort Order: 1)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Schneller Überblick über die Vorlesung
2. **Ausführliche Zusammenfassung** (Sort Order: 2)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Detaillierte Nachbereitung
3. **Offene Fragen** (Sort Order: 3)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Für Sprechstunden und Verständnisfragen
4. **Beantwortete Fragen & Antworten** (Sort Order: 4)
- Prompt ID: `47ce3340-e8c6-437c-928d-854c55589491`
- Memory Title: Q&A / Questions & Answers
- Perfekt für Lernkarten
---
## 2. Seminar & Gruppenarbeit / Seminar & Group Work
**Kategorie**: Universität
**Farbe**: #9C27B0
### Beschreibung
**Deutsch**: Perfekt für Seminardiskussionen und Gruppenarbeiten - erfasst Aufgaben, Ideen und erstellt strukturierte Dokumentation.
**English**: Perfect for seminar discussions and group work - captures tasks, ideas, and creates structured documentation.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Aufgaben & Termine** (Sort Order: 1)
- Prompt ID: `7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
- Memory Title: Aufgaben & Termine / Tasks & Appointments
- Mit Verantwortlichkeiten und Deadlines
2. **Gesammelte Ideen & Vorschläge** (Sort Order: 2)
- Prompt ID: `8cdc89a5-2f76-4d50-a93d-0c177c3e73ab`
- Memory Title: Ideen & Vorschläge / Ideas & Suggestions
- Brainstorming und kreative Ansätze
3. **Kurzzusammenfassung** (Sort Order: 3)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Hauptergebnisse der Diskussion
4. **Offene Fragen** (Sort Order: 4)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Für die nächste Sitzung
---
## 3. Prüfungsvorbereitung / Exam Preparation
**Kategorie**: Universität
**Farbe**: #9C27B0
### Beschreibung
**Deutsch**: Speziell für die intensive Prüfungsvorbereitung - verwandelt Lernmaterial in strukturierte Lernhilfen mit Q&A und Zusammenfassungen.
**English**: Specifically for intensive exam preparation - transforms study material into structured learning aids with Q&A and summaries.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Beantwortete Fragen & Antworten** (Sort Order: 1)
- Prompt ID: `47ce3340-e8c6-437c-928d-854c55589491`
- Memory Title: Q&A / Questions & Answers
- Perfekt für Lernkarten und Selbsttest
2. **Kurzzusammenfassung** (Sort Order: 2)
- Prompt ID: `c4009bef-4504-4af7-86f5-f896a2412a0a`
- Memory Title: Kurzzusammenfassung / Executive Summary
- Schnelle Wiederholung vor der Prüfung
3. **Ausführliche Zusammenfassung** (Sort Order: 3)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Detailliertes Prüfungsmaterial
4. **Offene Fragen** (Sort Order: 4)
- Prompt ID: `c576e875-5a52-4f6a-abb7-0c62c945af78`
- Memory Title: Offene Fragen / Open Questions
- Identifiziert Wissenslücken
---
## 4. Content-Erstellung für Studienarbeiten / Academic Content Creation
**Kategorie**: Universität
**Farbe**: #9C27B0
### Beschreibung
**Deutsch**: Verwandelt Recherche und Diskussionen in strukturierte Inhalte für Hausarbeiten, Präsentationen und wissenschaftliche Blogs.
**English**: Transforms research and discussions into structured content for term papers, presentations, and academic blogs.
### Verwendete Prompts (100% VERFÜGBAR)
1. **Ausführliche Zusammenfassung** (Sort Order: 1)
- Prompt ID: `4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
- Memory Title: Ausführliche Zusammenfassung / Detailed Summary
- Basis für Literaturarbeit
2. **Blogbeitrag** (Sort Order: 2)
- Prompt ID: `2c6a6e47-1d0c-441f-9449-b5d908bffba2`
- Memory Title: Blogbeitrag / Blog Post
- Für wissenschaftliche Blogs oder Artikel
3. **Social Media Posts** (Sort Order: 3)
- Prompt ID: `b2e39e0a-ec1f-4d0e-813d-f1a08493332b`
- Memory Title: Social Media Posts
- Für akademisches Networking (LinkedIn)
4. **Gesammelte Ideen & Vorschläge** (Sort Order: 4)
- Prompt ID: `8cdc89a5-2f76-4d50-a93d-0c177c3e73ab`
- Memory Title: Ideen & Vorschläge / Ideas & Suggestions
- Kreative Ansätze für Arbeiten
---
## Implementierungsdetails
### JSON-Struktur für Supabase
```json
{
"category_id": "b26c7a49-187d-4429-9dc6-ba55de512a8d",
"blueprints": [
{
"name": {
"de": "Vorlesungsanalyse",
"en": "Lecture Analysis"
},
"description": {
"de": "Umfassende Analyse von Vorlesungen mit Zusammenfassungen und Q&A",
"en": "Comprehensive lecture analysis with summaries and Q&A"
},
"prompts": [
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"c576e875-5a52-4f6a-abb7-0c62c945af78",
"47ce3340-e8c6-437c-928d-854c55589491"
]
},
{
"name": {
"de": "Seminar & Gruppenarbeit",
"en": "Seminar & Group Work"
},
"description": {
"de": "Erfasst Aufgaben, Ideen und Diskussionsergebnisse",
"en": "Captures tasks, ideas, and discussion results"
},
"prompts": [
"7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48",
"8cdc89a5-2f76-4d50-a93d-0c177c3e73ab",
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"c576e875-5a52-4f6a-abb7-0c62c945af78"
]
},
{
"name": {
"de": "Prüfungsvorbereitung",
"en": "Exam Preparation"
},
"description": {
"de": "Strukturierte Lernhilfen mit Q&A und Zusammenfassungen",
"en": "Structured learning aids with Q&A and summaries"
},
"prompts": [
"47ce3340-e8c6-437c-928d-854c55589491",
"c4009bef-4504-4af7-86f5-f896a2412a0a",
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"c576e875-5a52-4f6a-abb7-0c62c945af78"
]
},
{
"name": {
"de": "Content-Erstellung für Studienarbeiten",
"en": "Academic Content Creation"
},
"description": {
"de": "Strukturierte Inhalte für Hausarbeiten und Präsentationen",
"en": "Structured content for term papers and presentations"
},
"prompts": [
"4370cb68-d676-4b93-8afd-2fb7c4ad78c4",
"2c6a6e47-1d0c-441f-9449-b5d908bffba2",
"b2e39e0a-ec1f-4d0e-813d-f1a08493332b",
"8cdc89a5-2f76-4d50-a93d-0c177c3e73ab"
]
}
]
}
```
## Vorteile dieser finalen Version
### ✅ 100% Kompatibilität
- **NUR die 8 tatsächlich verfügbaren Prompts** werden verwendet
- Keine nicht existierenden Prompts (wie "Schlüsselpunkte")
- Sofort implementierbar ohne Backend-Änderungen
### ✅ Vollständige Abdeckung
- **Vorlesungen**: Nachbereitung und Verständnis
- **Gruppenarbeit**: Organisation und Ideensammlung
- **Prüfungen**: Strukturierte Vorbereitung
- **Wissenschaftliches Schreiben**: Content-Erstellung
### ✅ Praktischer Nutzen
- Jeder Blueprint löst ein reales studentisches Problem
- Sinnvolle Kombination der verfügbaren Prompts
- Mehrsprachigkeit bereits integriert (DE, EN, IT, FR, ES)
### ✅ Einfache Implementierung
- Direkt in Supabase einfügbar
- Keine neuen Prompts nötig
- Verwendet bestehende Infrastruktur
## Wichtiger Hinweis
Der Prompt "Schlüsselpunkte" (ID: 9b411221-6f52-4534-9ea9-dd1904259e8c) existiert NICHT in der Datenbank und wurde in dieser finalen Version komplett entfernt.

File diff suppressed because it is too large Load diff

View file

@ -1,316 +0,0 @@
# Personas Erstellungs-Guide
## Überblick
Personas sind detaillierte, datenbasierte Profile unserer Zielkunden. Sie helfen uns, Produkt- und Marketing-Entscheidungen aus Kundenperspektive zu treffen.
## Dateistruktur
### Speicherort
```
src/content/_personas/
├── de/ # Deutsche Personas
│ └── [slug].mdx # z.B. handwerksmeister-thomas.mdx
└── en/ # Englische Personas
└── [slug].mdx
```
### Namenskonvention
- Kleinschreibung mit Bindestrichen
- Format: `[rolle]-[vorname].mdx`
- Beispiele:
- `handwerksmeister-thomas.mdx`
- `startup-founder-alex.mdx`
- `projektmanagerin-sabine.mdx`
## Persona-Struktur
### 1. Frontmatter (YAML)
Die Persona-Datei beginnt mit strukturierten Metadaten zwischen `---` Markierungen:
```yaml
---
# PFLICHTFELDER
name: "Thomas Bauer" # Vollständiger Name
title: "Der digitale Handwerksmeister" # Beschreibender Titel
lang: "de" # Sprache (de/en)
# DEMOGRAFISCHE DATEN
demographics:
age: 45 # Alter oder Altersbereich (z.B. "35-45")
gender: "male" # male/female/diverse/unspecified
location: "Augsburg, Deutschland" # Stadt, Land
education: "Meisterbrief" # Bildungsabschluss
income: "75.000-95.000€/Jahr" # Optional: Einkommensspanne
familyStatus: "Verheiratet, 3 Kinder" # Optional: Familienstand
# BERUFLICHES PROFIL
professional:
jobTitle: "Geschäftsführender Elektromeister"
company: "Bauer Elektrotechnik GmbH" # Optional: Firmenname/typ
companySize: "12 Mitarbeiter"
industry: "Handwerk - Elektrotechnik"
experience: "22 Jahre"
responsibilities: # Array von Hauptaufgaben
- "Betriebsführung"
- "Kundenakquise"
teamSize: "11 Mitarbeiter" # Optional
# PSYCHOGRAFISCHE MERKMALE
psychographics:
personality: # Persönlichkeitsmerkmale
- "praktisch"
- "qualitätsbewusst"
values: # Werte und Überzeugungen
- "Handwerksqualität"
- "Zuverlässigkeit"
motivations: # Was treibt die Person an?
- "Betrieb modernisieren"
frustrations: # Pain Points
- "Zu viel Bürokratie"
goals: # Persönliche/berufliche Ziele
- "Digitalisierung vorantreiben"
# VERHALTEN
behavior:
techSavviness: "intermediate" # beginner/intermediate/advanced/expert
workStyle: # Arbeitsweise
- "Früh auf Baustelle"
tools: # Genutzte Tools
- "WhatsApp Business"
communicationPreference: # Kommunikationskanäle
- "Persönlich"
- "WhatsApp"
buyingBehavior: "Braucht Empfehlungen"
informationSources: # Wo informiert sich die Person?
- "Handwerkskammer"
# MEMORO-KONTEXT
memoroContext:
useCase: # Wie würde Memoro genutzt?
- "Baustellenprotokolle"
benefits: # Welche Vorteile sind relevant?
- "Rechtssicherheit"
concerns: # Bedenken/Hindernisse
- "Datenschutz"
features: # Wichtigste Features
- "Offline-Funktion"
priceSensitivity: "medium" # low/medium/high
adoptionLikelihood: "medium" # low/medium/high/very-high
influencers: # Optional: Wer beeinflusst Entscheidung?
- "Andere Handwerker"
# USER STORY
userStory: |
Detaillierte Erzählung eines typischen Arbeitstages...
# SZENARIEN (Optional)
scenarios:
- title: "Baustellenbegehung"
description: "..."
outcome: "..."
# ZITATE (Optional)
quotes:
- "Ich verbringe mehr Zeit mit Papierkram als auf der Baustelle"
# MARKETING
marketing:
segment: "secondary" # primary/secondary/tertiary
channels: # Marketing-Kanäle
- "Handwerkskammer"
messaging: # Kernbotschaften
- "Rechtssicherheit"
contentPreferences: # Content-Formate
- "Praxisberichte"
# META-INFORMATIONEN
status: "active" # draft/active/archived
visibility: "internal" # internal/team/stakeholders
tags:
- "handwerk"
- "b2b"
relatedPersonas: # Optional: Verwandte Personas
- "architekt-claudia"
# ZEITSTEMPEL
createdAt: 2024-12-01T10:00:00Z
lastUpdated: 2024-12-01T10:00:00Z
validatedAt: 2024-11-28T10:00:00Z # Optional
# VERANTWORTLICHKEITEN
owner: "Marketing Team"
contributors: # Optional
- "Vertrieb"
---
```
### 2. Content-Bereich (Markdown)
Nach dem Frontmatter folgt der narrative Teil in Markdown:
```markdown
## Detaillierte Persona-Beschreibung
[Einführender Absatz über die Persona und ihre Bedeutung]
### Kernherausforderungen
1. **Challenge 1**: Beschreibung
2. **Challenge 2**: Beschreibung
### Memoro Value Proposition
[Wie löst Memoro die Probleme dieser Persona?]
### Kommunikationsansatz
[Wie sollten wir diese Persona ansprechen?]
```
## Schritt-für-Schritt Anleitung
### 1. Research & Datensammlung
Bevor du eine Persona erstellst:
- Analysiere vorhandene Kundendaten
- Führe Interviews mit echten Kunden
- Sammle Feedback von Vertrieb und Support
- Recherchiere Branchentrends
### 2. Datei erstellen
```bash
# Neue Persona-Datei anlegen
touch src/content/_personas/de/[rolle]-[name].mdx
```
### 3. Frontmatter ausfüllen
Kopiere die Struktur von einer bestehenden Persona und passe an:
- Alle Pflichtfelder müssen ausgefüllt sein
- Nutze realistische, spezifische Details
- Vermeide Stereotypen
### 4. User Story schreiben
Die User Story sollte:
- Einen typischen Tag/Situation beschreiben
- Konkrete Pain Points zeigen
- Emotional nachvollziehbar sein
- 3-5 Absätze lang sein
### 5. Content-Bereich verfassen
Ergänze:
- Zusammenfassung der Persona
- 3-5 Kernherausforderungen
- Memoro's Value Proposition
- Kommunikationsempfehlungen
### 6. Review & Validierung
- [ ] Lasse die Persona vom Team reviewen
- [ ] Validiere mit echten Kundendaten
- [ ] Aktualisiere `validatedAt` Datum
- [ ] Setze Status auf `active`
## Best Practices
### DO's ✅
- **Spezifisch sein**: "45 Jahre, Elektromeister" statt "mittleres Alter, Handwerker"
- **Echte Zitate verwenden**: Aus Kundeninterviews
- **Pain Points priorisieren**: Die wichtigsten zuerst
- **Konsistent bleiben**: Persona-Details sollten zusammenpassen
- **Regelmäßig aktualisieren**: Quartalsweise Review
### DON'Ts ❌
- **Keine Fantasie-Personas**: Basiere auf echten Daten
- **Keine Stereotypen**: Vermeide Klischees
- **Nicht zu vage**: "mag Technologie" → "nutzt WhatsApp Business täglich"
- **Nicht zu viele**: 3-5 Kern-Personas reichen meist
- **Nicht statisch**: Personas entwickeln sich weiter
## Persona-Typen für Memoro
### Primäre Zielgruppe
- Projektmanager:innen
- Team-Leads
- Consultants
- Sales Manager
### Sekundäre Zielgruppe
- Startup-Gründer:innen
- Handwerker:innen
- Coaches & Trainer
- Freelancer
### Tertiäre Zielgruppe
- HR-Manager:innen
- Forscher:innen
- Journalist:innen
- Anwält:innen
## Verwendung der Personas
### Im Produkt
- Feature-Priorisierung
- UX-Entscheidungen
- Onboarding-Flows
### Im Marketing
- Content-Strategie
- Kampagnen-Planung
- Messaging & Positioning
### Im Vertrieb
- Pitch-Anpassung
- Objection Handling
- Use-Case-Demos
## Wartung & Pflege
### Monatlich
- Neue Insights aus Kundengesprächen einarbeiten
### Quartalsweise
- Vollständiger Review aller aktiven Personas
- Validierung mit aktuellen Kundendaten
- Archivierung veralteter Personas
### Jährlich
- Strategische Überprüfung der Persona-Landschaft
- Neue Personas bei Markterweiterung
## Tools & Ressourcen
### Templates
- Basis-Template: `src/content/_personas/template.mdx.example`
- Interview-Leitfaden: `docs/persona-interview-guide.md`
### Analyse-Tools
- Customer Analytics Dashboard
- Support-Ticket-Analyse
- Sales-Call-Recordings
### Validierung
- A/B-Tests mit Persona-basiertem Content
- Conversion-Tracking nach Persona-Segment
- Regelmäßige Customer Surveys
## Kontakt & Support
**Fragen zu Personas?**
- Marketing Team: marketing@memoro.ai
- Product Team: product@memoro.ai
**Neue Persona vorschlagen?**
- Erstelle ein Issue im Repository
- Oder kontaktiere direkt das Marketing Team
---
*Letzte Aktualisierung: Dezember 2024*

View file

@ -1,313 +0,0 @@
programmier.bar
Deep Dive 168
Low Code mit Till Schneider & Tobias Müller
13.12.2024
// Podcast
// Deep Dive 168
Shownotes
Die meisten Entwickler:innen schreiben gerne Code. Aber muss das wirklich immer sein? Und ist das in jedem Szenario wirklich sinnvoll?
In dieser Episode sprechen Garrelt und Jan mit Till und Tobias, den Köpfen hinter memoro.ai, über ihre Erfahrung mit Low-Code-Tools zur App-Entwicklung. Till und Tobias haben im Rahmen eines Hackathons die Idee für die Code-Minimierung entwickelt und anschließend ohne konventionelle Entwicklungsarbeit umgesetzt. Ein Beweis, dass man auch mit Low-Code-Tools moderne Apps an Endkund:innen ausliefern kann.
Wir unterhalten uns über die Erfahrungen, die sie auf ihrer Reise gemacht haben: Von der Auswahl der richtigen Tools über den Einstieg und die Lernkurve bis hin zur Kollaboration zwischen Entwickler:innen und Designer:innen erforschen wir alle Facetten des Projektes.
Aber wir beleuchten auch, wann und wie ein solcher Ansatz an seine Grenzen stoßen kann. Wir fragen uns, ob das unausweichlich ist und wie am besten damit umgegangen werden sollte. Außerdem stellen wir die alles entscheidende Frage: Können Till und Tobias den Ansatz wirklich empfehlen und würden sie es nochmal genauso machen?
Transkript:
Jan
Hallo und herzlich willkommen zu einem neuen Deepdive in der programmier.bar. Heute mit einem ganz besonders spannenden Thema, nämlich einem Thema, was immer die Gemüter spaltet. Wir sprechen einmal über No Code beziehungsweise Low Code Lösungen. Und mit mir im virtuellen Studio in der unteren rechten Ecke meines Screens sitzt der Garelt. Hi, Garelt. Hi. Garelt, wie oft hast Du schon mit so Low Code und Low Code Lösungen gearbeitet?
Garrelt
Oh, wir hatten das in der Schule. Da hatten wir was zum Zusammenklicken, das hieß Scratch. Da hab ich ein megageiles Spiel gemacht, hab eine Eins bekommen und meine Schülerkameraden haben auch eine Eins bekommen für Trash Spiele, aber na ja, ist nicht mein Ding.
Jan
Die Zuhörerinnen da draußen, die jetzt nicht sehen, wie jung Du noch aussiehst, fragen sich natürlich, wann warst Du in der Schule und hast Scratch programmiert? Wie lang ist das her?
Garrelt
Das war zweitausendvierzehn, also genau zehn Jahre.
Jan
Zweitausendvierzehn. Zweitausendvierzehn hatt ich schon meinen Uniabschluss in der Tasche, dementsprechend länger ist mein Informatikunterricht auch schon her, ja. Aber bei mir gab's keinen Low Code in der Schule. So Fernsehsieg gab's damals noch nicht, ja. Wir haben mit Tobi Pascal und c irgendwie programmiert. Und weil ich dementsprechend ganz wenig Ahnung von Low Code und No Code hab, sondern nur viel Meinung dazu, haben wir uns noch Leute eingeladen, die uns mit viel Wissen zur Seite stehen können. Wir haben hier Till und Tobias von Memoro. Hallo Till.
Till Schneider
Hi Jan.
Jan
Grüß dich. Hallo Tobias. Hi. Tobi passt übrigens. Tobi auch wunderbar, kein Thema. Ihr habt zusammen Memoro gemacht mit noch ein paar anderen Leuten. Vielleicht wollt ihr einmal ganz kurz erklären, was Memoro ist, wie ihr auf die Idee gekommen seid und dann sprechen wir einmal ans Eingemachte.
Till Schneider
Jawoll, gerne. Wir haben Memoro vor so eineinhalb Jahren circa gestartet auf 'nem Hackathon hier in Konstanz am Bodensee. Ich kam da mit der Idee die Ecke, dass ich doch gerne meine Gedanken und Gespräche aufnehmen will. Also ich hatte das Hauptproblem, dass ziemlich viel Projekte parallel liefen und ich nicht hinterherkam irgendwie mit der Organisation, mit der Strukturierung von allem. Und dann hab ich mir so angewöhnt, abends 'n Spaziergang zu machen und das einfach so mir von der Seele zu reden. Und dann hab ich das mal getestet, aufm iPhone einfach Sprachnotiz aufgenommen, Sprachnotiz hochgeladen, transkribieren lassen, das dann in Chat GPT reinkopiert und in Sachen gefragt über den Text. Und das kam mir gleich vor wie sone kleine Superkraft, weil man sich selber halt supergut reflektiert bekommt und und recht unstrukturiert reden kann und strukturiert das Ganze zurückbekommt und halt son Überblick bekommt. Also es war wie son 'n kleiner kleiner seelischer Doktor, kann man fast sagen. Jemand, der mir dann zuhört und dann konnte man sich einfach son bisschen entleeren. Und das hat sich einfach so toll angefühlt, dass ich das dann vorgestellt habe auf dem Heck gefahren, als einfach nur eine eine grobe Idee. Und dann hat sich direkt 'n Team aus fünf, sechs Leuten eigentlich die Idee formiert und wir haben zwei Tage lang gebrainstormt. Wir haben sehr viel waren sehr viel spazieren, haben sehr viel geredet, das ist natürlich alles direkt aufgenommen und hatten dann eigentlich 'n super Startschuss rein in die in das neue Start-up, da wir diese ganzen Gespräche festgehalten haben und direkt wussten, was wo wir hinwollen. Und Tobi war auch direkt von Anfang an dabei und wir zwei haben dann tatsächlich direkt gestartet. Wir sind das Kernteam, wir haben angefangen zu entwickeln, die anderen unterstützen son bisschen. Und wir hatten dann nach zwei Wochen eigentlich einen einen eine Alpha und haben die verteilt an Leute, haben die Menschen in die Hand gedrückt und haben gefragt, wie sie das Tool gerne nutzen könnten. Und dann kamen sehr viele Anfragen aus verschiedenen Industrien, aus verschiedenen Branchen, die 'n großen Mehrwert daran sehen. Und heute sind wir jetzt bei tausenddreihundert Nutzern, haben auch Paying Subscriber schon und haben 'n Stipendium gewonnen, was uns 'n Jahr lang finanziert und haben auch sehr viel Platz und Raum, uns mit neuer KI Technologie grundsätzlich auseinanderzusetzen.
Jan
Das, was Du jetzt natürlich nur so halb implizit gesagt hast, ist, dass ihr natürlich Memoro komplett mit Low Code No Code Toolchain gebaut habt, sozusagen, ja? Ja, genau. Darüber wollen wir einmal einmal sprechen. Deswegen meine erste Frage vielleicht so an Tobi. Was macht man als Entwickler in soner No Code Low Code Bude? So wird wird man da überhaupt gebraucht Fragezeichen,
Tobias Müller
ne? Tatsächlich ist es gar nicht so gar nicht so falsch, weil auch als Coder überlegt man sich, wie kommt man schnell zu Ziel? Und ich komm auch son bisschen ausm Start up Bereich oder auf Hecertons. Und da muss alles immer schnell gehen. Und da braucht man natürlich das das Verständnis für Code, wie Programme aufgebaut sind. Aber es hilft einem unglaublich, mit sonem No Code Low Code Bilder einfach mal schnell 'n Prototypen zusammenzuklicken. Und da das ergänzt sich, find ich, super. Das ist gar kein Widerspruch zueinander.
Garrelt
Würdest Du sagen, man ist mit Low Code, mit dem Zusammenklicken wirklich schneller, wenn man gleich gut das auch entwickeln könnte? Einfach vom Speed her, wenn man beides gut kann.
Tobias Müller
Man muss 'n bisschen differenzieren zu früh und heute. Heute hat man KI. Da ist vielleicht tatsächlich son bisschen die Überlegung, hat man mit 'ner KI vielleicht schneller mal 'n Projekt initiiert und schnell mal 'n Code druntergeschrieben, grade wenn's 'n bisschen was Spezielleres ist. Aber allgemein, wenn man einfach mal nur was darstellen will, das heißt ja so schön, Dann dann kann man einfach mal schnell 'n Gefühl dafür bekommen und auch einfach mal einfache Logiken damit umsetzen, keine Ahnung, Kalkulation oder irgendwelche Sachen zu generieren und so weiter. Und vielleicht schreibt man dann durchaus auch mal 'n Kostencode. Aber da geht's halt alles wirklich Geschwindigkeit. Im Hackathon hat man nicht viel Zeit.
Till Schneider
Was ich dann noch ergänzen könnte vielleicht, und das war eigentlich so auch, das, wir hätten gar nicht, ich hätte nicht anfangen können, Tobi zu helfen, wenn wir nicht mit Low Code, Low Code gestartet wären. Also ich bin vom Designhintergrund und gut, ich hab mal bisschen Tailwind HTML CSS son bisschen rumgewurstelt, aber ich hatte dadurch halt die Möglichkeit, superschnell das Frontend zu iterieren. Und wir haben dann im Endeffekt innerhalb von, was war's, 'nem Jahr oder hatten wir über vierhundert Versionsnummern durchgeschoben und da hat sich auch in jeder was geändert. Also da war dann 'n ordentlicher Druck im Kessel und now 2 be im wir haben mehr aufs mehr im Backend und guckt, dass das Zeug sauber funktioniert, hat dann ja in Python das Backend geschrieben, also Backend nicht nicht Low Cop, No Code. Wir haben mal Tests gemacht mit mit anderen Tools auch und haben kleine kleine Backends Low Carb, No Code technisch aufgebaut. Aber sonst hätt die Zusammenarbeit mit uns gar nicht, wir hätten gar nicht starten können, hätten wir den Stack nicht gewählt.
Jan
Genau. Vielleicht machen wir da noch mal zweieinhalb Schritte zurück. Jetzt warst Du schon bei den vierhundert Versionen, die ihr da irgendwie durchgeschippt habt. Aber bevor ihr angefangen habt, also ihr hattet, ne, über das Thema son bisschen gesprochen, ihr hattet irgendwie 'n grobes Konzept. So weit sind ja, ich sag mal, Codeentwicklung und No Code Entwicklung sich ja auch gleich und ähnlich, ja. Erst mal muss man darüber sprechen, was man so baut, bevor man anfängt, das hilft. Und dann wollt ihr irgendwann anfangen. Wie habt ihr euch denn für ein spezielles Tool entschieden? Weil also alle Entwickler kennen ja diese Diskussion, Du willst jetzt was bauen. Da ist erst mal die wichtigste Diskussion vorher so, was ist der Stack, was ist die Sprache, was ist das Framework, ja? Gab es da auch ähnliche Diskussionen so in diesem No Code Bereich, wo man sich überlegt, na ja, es gibt da halt zig verschiedene Tools da draußen, ja? Also wenn man einfach nur mal No Code oder Low Code googelt, da wird man ja jetzt zugeschüttet mit irgendwie Plattformen, die einem da was anbieten wollen und verschiedenen Wändern und basierend auf verschiedenen Technologien. Und wie habt ihr euch da überhaupt erst mal durchnavigiert, euch für eines von diesen Zehntausenden zu entscheiden? Und und was waren da ausschlaggebende Faktoren?
Tobias Müller
Was ich mal sagen kann dazu, ist, dass es auf jeden Fall Reibungspunkte auch da schon am Anfang gab. Weil ich als Entwickler hab natürlich meine Tools, die ich schon kennengelernt hab und so weiter. Und dann bleib ich natürlich auch gerne aus Gemütlichkeit bei dem, was ich schon kenne. Till war da schon deutlich offener am Anfang. Er guckt sich gerne aktuelle Berichte an, über was was für neue IDs es gibt, welche neuen Frameworks und so weiter. Er ist da deutlich offener, 'n bisschen weniger voreingenommen, würd ich sagen. Und da haben wir schon am Anfang auch schon angefangen zu diskutieren. Ja, und irgendwie kamen wir dann auf Flatterflow. Ich weiß gar nicht mehr so genau, wie's dazu kam. Weißt Du's noch, Tür?
Till Schneider
Ich glaube, wir hatten einfach verschiedene Test Testberichte gelesen. Ich bin 'n großer Youtube Videoanker und da verschiedenste Vergleiche mir reingezogen. Und ja, Flatterflow ordnet sich da einfach am Ende am am am am Ende zu Code eigentlich ein von den Low Code Tools. Also man kann sehr viel Custom Code mit einbauen und das das Tool eigentlich mehr und mehr erweitern. Und die anderen Low Code No Code Tools sind da eher noch mehr Low Code No Code, sag ich mal. Also wir haben uns dann so für diesen für für für das Komplexeste entschieden, was es gibt, haben dann auch geschaut, wo gibt's eine gute Community? Wo wo kann man Fragen stellen? Wo wird wo werden Features geschipped? Natürlich son bisschen, was ist der was ist grade heiß am Markt auch, aber was existiert auch schon länger? Also wir Flatterflor war damals schon 'n paar Jahre alt und und aufm Markt, heißt, auch da 'n bisschen auf die Langlebigkeit dann geschaut. Dann hat sich das so bisschen rauskristallisiert recht schnell eigentlich.
Garrelt
Aber Tobias, Du kannst aus nicht aus der Ecke Low Code, No Codes. Dein Setup vorher war reiner Code.
Tobias Müller
Also ich für mich selbst hab bisher weniger mit Low Code, No Code gearbeitet, klar. Und so weiter. Schon ein bisschen welche SDKs Frameworks kannst Du nutzen, wirklich 'n schnelles Ergebnis zu erzielen. Aber wirklich eingesetzt hatt ich's bisher dann noch weniger. Deswegen war jetzt halt auch son bisschen die die Diskussion, wo steigen wir ein? Und der große Punkt war, wie Till schon sagte, wie kann er halt auch mitentwickeln? Weil wenn wir jetzt wirklich mit Code anfangen, wird's halt schwierig. Und wir hatten dann auch getestet und angeschaut, inwieweit sind wir eingeschränkt? Wie viel Freiheiten haben wir? Und mit florida Flow war halt auch eben dies diese Möglichkeit, trotzdem noch eigenen Code einzufügen, was für uns auch notwendig war.
Jan
Das ist ja 'n spannender Punkt. Also wenn ihr relativ früh schon erkannt habt, okay, ist es halt notwendig, da auch Custom Logik oder UI Elementter oder so was zu machen. Wie lange ging es denn gut mit nur den Onboard Tools, ja, in eurem Prototyping, Bootstrapping Prozess? Und wann war so das erste Mal, wo ihr gesagt habt, okay, jetzt müssen wir mal hier Ärmel hochkrempeln und mal was Eigenes noch dazu bauen?
Tobias Müller
Eigentlich ging das ziemlich schnell, weil 'n Kerninhalt von unserer App ist halt die Audioaufnahme. Und Floodderflow bietet schon einen einfachen Recorder, aber sehr eingeschränkt. Also großes Kriterium ist bei uns, dass man's halt wirklich das Handy aufn Tisch legen kann und ohne drüber nachzudenken aufnehmen lassen kann. Und da darf halt die App nicht ins Sleep Mode gehen. Die der Screen muss gesperrt werden können. Und da muss man halt die Anforderungen von der jeweiligen Plattform erfüllen können. Dieser einfache Recorder, der hat dafür nicht ausgereicht. Das heißt, wir haben sehr schnell damit angefangen, wirklich den Recorder selbst zu implementieren.
Jan
Und was war das fürn Erlebnis? Also wie wie sehr arbeitet man da mit oder gegen die Plattform, wenn man dann da eigene Komponenten einbauen möchte?
Tobias Müller
Das war schon teilweise recht anstrengend. Man hat schon schmerzhafte Einschränkungen, würd ich sagen. Man hat halt wirklich nur, wie soll ich sagen? Man man kann Custom Actions, Custom Functions, Custom Widgets anlegen, aber man bewegt sich halt dann immer nur in einem Codebereich. Wenn man dann eben Sachen in die App integrieren muss, vielleicht direkt schon beim beim Starten der App mit initialisieren wird, dann wird's eben komplex und manchmal muss man dann auch Workaruns suchen. Wie kriegt man eben das integriert ohne diese Freiheiten? Wobei natürlich, man muss auch noch ergänzen, man könnte durchaus den Code verändern, aber dann wird's noch mal komplizierter.
Garrelt
Mhm.
Jan
Jetzt ist ja so auf Funktionalitätsebene das eine, aber auf Designebene ist ja das andere, ne. Also ich stell mir vor, wenn man da in sonem grafischen Editor arbeitet und sich da seine Elemente zusammenklickt, dann kommt man Till ja vielleicht auch relativ schnell an so dem Punkt so, das ist jetzt nicht so ganz Pixelperfekt, wie ich's eigentlich gerne hätte oder wie's in unserem Designentwurf aussah? Und gab es da auch Punkte, wo man da irgendwann so die die Edge Cases des Editors erreicht hat, sag ich mal und sich dann was anderes einfallen lassen muss?
Till Schneider
Würd ich nicht sagen. Also ich hab am Anfang noch angefangen, alles in Figma zu designen und wie die UI und UX da zu planen. Und dann war es auch son schleichender Prozess, dass dass halt Flatterflow im Grunde genommen so schnell funktioniert, dass ich eigentlich weniger und weniger die Sachen in Figma vorgescatcht hab und angefangen hab, wieder eigentlich direkt in Flatterflow Dinge auszuprobieren. Ist son schmaler Grat. Also manchmal hab ich's damals bereut, die Sachen nicht besser geplant zu haben in Figma. Manchmal war's eine gute Entscheidung, also da son bisschen der Mittelweg. Aber ich kam nie, also doch an ein, zwei Stellen, aber da würd ich sagen, also sehr, es gibt es gab dann gewisse Bereiche, die doch nicht funktioniert haben. Zum Beispiel wollt ich eine eine Liste am unteren Ende des Bildschirms haben und wenn auf die Liste getippt wird, sollte sie hochfahren und dann den ganzen Screen ausfüllen. Also quasi so wie eine Page Navigation, aber halt auf auf eigentlich in einer Seite contained, ging nicht. Solche Sachen wie wie Blur, haben Blurview, also auf iOS kennt man das ja, dass dann der der der Header zum Beispiel so so diesen, ja, ja, diese diesen Blur bekommt. Den konnte man nicht schön umsetzen beziehungsweise dann hat Flatterflow irgendwie im im Preview Mode dann rumgesponnen und wurde langsamer, wurde irgendwie slackgisch. Das gab's bei 'n paar Punkten, wo man wo man so, wenn man Sachen bauen will im Frontend, dass auch Flatter Flow es nicht mehr richtig gut handeln kann und man, ja, man eigentlich dann Abstriche machen muss. Was an sich okay war, glaub ich, weil im Grunde genommen sind halt die die Patterns, die User gewohnt sind, recht gut dargestellt darin. Und wenn man dann eigentlich an diese Grenzen kommt, sollte man sich vielleicht eher überlegen, ob das auch kein kein gewohntes Pattern ist, was man vielleicht auch anders bauen könnte.
Jan
Wie ist es denn in der in der täglichen Arbeit? Weil im Prinzip fahrt ihr ja zweigleisig, also oder auf zwei Ebenen, ne. Die die unterste Ebene ist das Betriebssystem iOS, Android, auf was auch immer ihr so deployed. Das verändert sich ja auch. Da kommen neue Versionen, neue Sales, neue raus. Und dann zwischen euch und dem Betriebssystem ist da diese Plattform, die es ja wahrscheinlich auch jetzt geändert hat so in der Zeit, in der ihr sie benutzt, ja auch mit mit neuen Features, mit neuen Funktionen, neue Widgets vielleicht out of the box bereitstellt oder so was. Was was macht es so mit dem Erlebnis?
Tobias Müller
Das hat aber auf jeden Fall dazu geführt, dass wir öfters mal zu plötzlich Errors hatten, die wir vorher nicht hatten. Und nach dem Debugging hat sich raus rausgestellt, okay, jetzt haben sich Dependencies geändert. Doch das ist son Beispiel gewesen. Entweder hat dann halt Flatterflow Updates gezogen oder ich hab dann irgendwie eine Dependency reingemacht, die halt auch nicht mehr mit 'ner anderen Version kompatibel war. Das war so 'n Problem, über das wir öfters gestolpert sind, was dann auch natürlich 'n bisschen frustrierend war, erst mal überhaupt rauszufinden, was ist denn da jetzt passiert? Auch von Android Seite gab's mal eine Änderung, dass man zusätzlich permissions grad mit dieser Backgroundaufnahme noch hinzufügen musste. Auch das war eine längere Recherche, dann wieder rauszufinden, okay, hier müssen wieder zusätzliche Angaben zu machen. Ja, da haben wir auf jeden Fall oder hab ich auch viel Zeit mit verbringen müssen mit dem Debugging, mit dem Recherchieren.
Jan
Mhm. Und was bringt denn sone Low Code Lösung am Ende alles mit? Also wir haben jetzt viel, ne, über so grafische Editoren für das Interface gesprochen und auf und Komponentenebene, die man selber bauen kann. Aber zu soner richtigen in Anführungszeichen App gehört ja oftmals noch viel mehr dazu, ne. Insbesondere wenn's irgendwie was kommerzielles ist, da braucht man irgendwie Usermanagement, da braucht man 'n 'n Payment oder Billing vielleicht auch, ja. Da braucht man irgendwie eine Art von Storage, die da dranhängen muss. Ihr habt schon über eure Aufnahmen gesprochen. Die werden ja auch irgendwo aufgerufen werden müssen. Ihr macht Transkcription, das wird irgendwo gemacht. Wie viel davon kann ich so alles in meinem Sandkasten machen? Und was muss ich selber extern zur Verfügung stellen? Und wie krieg ich das dann am Ende connected?
Tobias Müller
Ich glaube eben mal allgemein zu dem Low Code No Code Thema. Das ist auch der große Vorteil, weswegen man vielleicht das wählen kann, weil den meisten Low Code No Code Tools eben solche Integration schon mit drin sind. Da braucht man sich eben nicht darüber Gedanken machen, wie mach ich die das Setup? Was muss ich da beachten und so weiter? Und das war eben jetzt auch speziell bei Flood oflow sehr angenehm, weil da zum Beispiel Firebase schon komplett integriert ist mit dem der der und Firestore als Datenbank. Und das hat auch noch weitere Sachen wie zum Beispiel Superbase. Klar, schon eine gewisse Einschränkung. Es ist jetzt nicht alles drin, aber super zum Durchstarten, kein größeres Set-up kann man hat man direkt, was man braucht.
Till Schneider
Und auch weitergehen noch in Richtung eben, wie bekomme ich dir die Sachen denn in die Appstores rein? Wie bekomme ich das auf Testflight oder in den Play Store, Betatrack? Das ist alles eigentlich schon recht gut eingebaut. Das ist dann 'n ziemlich viel händisches Set-up, bis man mal eben den den Apple Developer Profil und den Android Developer Profil sauber aufgesetzt hat und da die recht alles alle Formulare ausgefüllt hat, bis man da überhaupt mal was reinpushen kann. Aber das ist im Grunde genommen alles schon eingebaut und dann auch Revenue Cat zum Beispiel, Integration, die dann die Payments handeln und und eigentlich den die App Store und Play Store Payments dann an einer zentralen Stelle wieder verwalten. Da musste, glaube ich, Tobi dann schon noch 'n bisschen was quasi auf Datenbankseite nachziehen, dass das wieder, dass dass wir auch die Daten sauber behalten und und das sauber abbuchen können sozusagen. Aber tatsächlich ging das jetzt in Flatterflow, dass wir die App dahin gebracht haben, dass sie inklusive in App Payments und Abomodell und so weiter vollkommen in Flatterflow entstanden mit viel Customcode dann geschippt werden kann. Und aktuell ist das immer noch die Version, die im App Store live ist.
Garrelt
Es klingt ja schon nach ziemlich viel Funktionalität. Und als ich mir das mal auf der Seite angeguckt hab, wirkte flutterflow auch sehr mächtig und auch für mich fast schon überladen. Wie ist denn so eure Erfahrung? Wie schnell seid ihr da reingekommen? War viel intuitiv für euch? Musstet ihr diese Software erst mal lernen, zu also überhaupt erst mal was machen zu können oder findet man eigentlich alles sehr schnell, was man braucht?
Tobias Müller
Also wenn man jetzt eben von diesen Integrationen redet, wie man die nutzt, auch UI Elemente, wie man die platziert, einfache Klickfunktionen und so weiter, ist es, denk ich, schon sehr einfach. Sobald's dann aber 'n bisschen Logiken geht, so das typische f F ifs, Schleifen, also ich red jetzt grade eher 'n bisschen über die algorithmische Ebene, da ist es dann vielleicht nicht mehr so intuitiv. Da hilft's dann's durchaus, sich auch 'n paar Tutorials anzuschauen, sich mal 'n bisschen damit zu beschäftigen, grade wenn man vielleicht jetzt nicht so die Codingerfahrung hat.
Till Schneider
Auch vom Frontend her finde ich, es gibt eine gewisse Lernkurve. Also man es ist wirklich 'n Tool, was was in Richtung, man muss schon Experte sein geht. Es hilft, wenn man Verständnis hat von Atomic Design System und weiß, wie man Komponenten richtig anlegt, einfach eine Konsistenz auch zu halten, sonst läuft es schon schnell ausm Ruder. Also ich musste da auch im Frontend dann immer wieder quasi refactern und mir überlegen, okay, wie krieg ich das jetzt einiger bisschen sauberer noch hin, dass ich hier nicht auf jeder Seite eine einzelne Header Komponente hab, sondern eine zentrale und da mach ich das halt mit Variablen. Also man, es wird schon recht schnell komplex. Also man braucht 'n gewisses Grundverständnis dafür. Und ich würde sagen, von den No No No No Go Tools eben hat es eine eine relativ steile Lernkurve.
Jan
Und wie habt ihr euch da geholfen? Also wie wie habt ihr diese Lernkurve gemeistert?
Till Schneider
Also in meinem Fall ist es tatsächlich Youtube Tutorials angucken. Flatterflow hat 'n supereigenen Channel, wo sie gute, auch lange Tutorials haben, wo sie mal eine ganze Applikation bauen. Oder sie haben solche solche Webinare, wo sie zusammen sich zusammenschalten und bisschen über Themen reden. Es war aber tatsächlich 'n extremes Gesuch. Also wir haben dann so zwei, drei Stunden lange Webinare und ich habe einen Fehler. Und der Fehler ist dann bei Minute dreiundfünfzig, wo sie genau darüber reden und erklären, dass das ja grad 'n Fehler ist in ihrer Software. Und man muss gucken, da muss man das so umbauen. Also es war schon oft son bisschen das Suchen nach der Nadel im Heuhaufen.
Tobias Müller
Also man merkt auch schon, es ist eine Community da, vermutlich auch größer wie bei anderen Tools, aber sie ist doch noch am Aufbauen. Also grad wenn's dann speziellere Dinge geht, ist es schon 'n bisschen an Gesuche und auch 'n bisschen mit Glück verbunden, ob denn tatsächlich schon mal jemand diese Frage überhaupt gestellt hat.
Till Schneider
Und und was ich noch ergänzen will, ist, dass wirklich diese diese zusätzliche Ebene, Abstraktionsebene, die halt Flatterflow noch mal auf Flatter draufsetzt, halt auch zu zu Problemen führt. Dann weiß man nie genau Und und auch die Flatterflow Community weiß nicht, ist das jetzt 'n Flatter Error oder entsteht das jetzt durch die Flatterflow Abstraktion noch mal, ist da der Fehler und es sorgt schon zu noch mehr Reibungen.
Garrelt
Aber die Community, mit der da im Austausch war, war schon auch die Flatter Community oder ist diese Flatterflow Community arg getrennt?
Till Schneider
Nur, Flatterflow hat selber eine Community, also auf Community, glaub ich, Punkt Flatterflow Punkt com oder wie auch immer die URL ist, gibt's eine eigene Community, die auch recht stark ist. Da gibt's 'n paar sehr, sehr tolle Members, die auch sofort antworten, die schnell antworten. Und wir haben uns dann, also ich mich fürs Frontend her, meistens darin bewegt. Tobi musste, glaub ich, noch öfters dann auch in auf die Flatterebene wechseln, gewisse Dinge zu verstehen oder zu debuggen.
Garrelt
Ich frag mich schon die ganze Zeit, also Till, Du sagst ja, Du kommst eher aus dem Design Background. Tobias, Du wärst so im Dev Background. Wie gut oder wie habt ihr euch so aufgeteilt aufgabentechnisch? Hat das gut funktioniert? Auch auch eine Frage in meinem Kopf ist, wie hat das mit Version Control funktioniert, weil das manchmal bei so Tools, glaub ich, 'n bisschen komplizierter sein kann. Und ich glaube, in meinem Kopf ist son Tool ja megastark, was so Design- und Entwicklungszusammenarbeit geht. So, was was sind da eure Erfahrungen? Was hat da gut funktioniert, was vielleicht nicht so gut?
Tobias Müller
Also flatterflow bietet schon eine git ähnliches Brunching mit ja, auch eine Versionskontrolle, aber die hat uns eigentlich nicht so wirklich geholfen. Meistens war eher der Prozessor. Till hat was eben UI mäßig umgesetzt, irgendwelche Elemente. Und nach ihm bin ich dann ran und hab irgendwelche Logiken dann dazu noch implementiert. Also es war schon irgendwie 'n getrenntes Arbeiten, da wir aber mehrere Baustellen hatten, hatten wir uns dann halt schon auch irgendwo aufgesplittet. Es war halt, das ist auch ein ein großes Problem, was wir eben auch mit Flatterflow hatten, son Bug könnte man sagen. Sobald man parallel auf einer Seite gearbeitet hat, was man theoretisch machen könnte, hat es oft dazu geführt, dass einfach Änderungen verschwinden sind. Till hat irgendwas gemacht, ich hab dann was eingefügt, dann
Garrelt
geh ich
Tobias Müller
in den Testmode und plötzlich ist es nicht mehr da. Und das war auch schon sehr frustrierend. Deswegen war dann einfach die Konsequenz, okay, wir müssen das halt etappenweise
Garrelt
machen. Also fast mehr Absprache nötig als sonst.
Tobias Müller
Genau, auf jeden Fall.
Garrelt
Ja. Und Till, Du konntest aber ohne viel Entwicklungs, nee, das weiß gar nicht, wie viel Entwicklungserfahrung hattest Du denn vorher? Oder bist Du da einfach wirklich aus dem reinen Designbackground eingestiegen und konntest da Dinge entwickeln?
Till Schneider
Also ich hab einmal eine eine Designlibrary eben mit mit Tellwind HTML, CSS irgendwie geschrieben. Das so Grundverständnisse waren eben da jetzt übers Boxmodel oder wie grundsätzlich die die die Dinge in in Code übersetzt werden. Aber tatsächlich war es dann doch eine recht steile Lernkurve eben noch mal eben viel, viel Tutorials angucken, dass man weiß, wie sich halt der flatterflow wieder verhält. Also ich wär dann wahrscheinlich schneller gewesen, hätt ich's dann in in mit irgendwie schreiben können. Ging an der Stelle aber nicht, weil andere Sachen wieder nicht gegangen wären. Und tatsächlich diese die Grenzen waren dann recht fließend. Also ich konnte dann halt auch über dieses dieses son System, wo man quasi die die Action Blocks dann einfügen kann, die irgendwelche Dinge triggern, wie zum Beispiel haptisches Feedback. Das waren für mich halt dann zwei Klicks, ein neue Note anlegen, sagen hier, haptisches Feedback auf, wenn der Button gedrückt wird. Und dann hat kann ich das supereinfach auch bewerkstelligen. Das wär auf Codebene dann wieder deutlich komplizierter gewesen, beziehungsweise hätt ich wahrscheinlich noch mehr Fehler gemacht. Also da konnte ich mich dann mehr Richtung Tobi auch arbeiten, sag ich mal, von der von der Funktionalität her. Auch Stück für Stück hab ich mich auch sicherer gefühlt, wie wie mach ich eine eine Firebase Datenbankabfrage und so weiter und so fort, weil der Flatterflow einfach das schon relativ verständlich macht für den Nutzer.
Jan
Das wär jetzt meine nächste Frage gewesen. Also selbst wenn Du dann durch diverse Tutorials und und so dich in dem Editor irgendwie wohlfühlst, musst Du ja trotzdem am Ende noch über kurz oder lang andere Services integrieren, sei es euer eigenes Firebase oder vielleicht habt ihr auch noch andere Services angebunden, keine Ahnung. Und wie schafft das denn son Tool an der Stelle, da sone Translation hinzubekommen zwischen, na ja, na ja, Du machst dir zwar eine Datenbankabfrage und da kommt irgend 'n 'n JSON Objekt vielleicht zurück am Ende des Tages. Aber wie sagt man dann, okay, und jetzt von diesem JSON Notizenobjekt hier an dieser Stelle den Titel anzeigen und hier den Inhalt und hier vielleicht, ne, also also wie wie ist dieser Connect so?
Tobias Müller
Ich glaub, da hat sich dann auch schnell gezeigt, wo so Grenzen dann liegen, was was trotzdem aber möglich ist. Einfache Updates, Datenbankabfragen und so weiter. Da kam Till auch super mit klar. Ich glaub, da braucht man nicht wirklich das Coding Verständnis, wenn's aber dann drum geht, wirklich mal Daten zu extrahieren, mehrere multiple Datensätze zu verändern oder auch in der UI ja berechnen und so weiter zu machen. Da musste ich dann schon einschreiten. Das ist nämlich auch flatterflow möglich, aber da gehört dann halt wirklich auch das Verständnis 'n bisschen dazu.
Jan
Und wie habt ihr euer euer Backend dazu gebaut? Weil ich nehm mal an, das macht man ja nicht in flatterflow.
Garrelt
Genau. Also wir hatten ja jetzt erst
Tobias Müller
mal über die Integration von Feuilleton zum Beispiel geredet. Jetzt kommt eben der Punkt unsere Verarbeitung bei Memoro, unsere unsere Prozesse, wie wir das natürlich aufgearbeitet ausgeben, das liegt alles an 'nem Backend. Und auch da bietet eigentlich Flatterflow 'n guten zentralen Punkt, wo man einfach seine Backend Route einfügt, seine Restcalls definiert und die dann auch als Action abfeuern kann. Aber auch da gibt's dann halt wiederum Limitierungen. Wir hatten jetzt Glück, dass wir jetzt da backend rotemäßig nicht nur hohe Komplexität haben, aber wir waren jetzt trotzdem auf ein back End eingegrenzt. Wir konnten so was wie On Preme und so weiter nicht umsetzen damit.
Garrelt
Was ich
Till Schneider
vielleicht noch ergänzen will an der Stelle, im Bezug auf die Datenbankabfragen, ist es son bisschen auch eine Blackbox gewesen. Also wir hatten dann im Endeffekt das Problem, dass wir in den Memory Leak reingelaufen sind und mussten dann ewig lang debuggen, woran liegt es? Wir haben's dann auch so halb rausgefunden, aber dann wieder doch nicht. Also es war irgendwie der Timer, den mussten wir dann Custom nachbauen, damit es besser funktioniert. Also drückst Aufnahme starten und dann zählt 'n Timer hoch und irgendwie der eingebaute Timer von Flatterflow hat diesen Memoryleak wohl verursacht, aber es war dann doch nicht, das war das Hauptproblem, aber da war noch mal 'n tiefer liegendes Problem und wir kamen da partout nicht dahinter. Und das war hat mich dann auch sehr verrückt gemacht. Ich als quasi, ich nutze Memoro am meisten, ich kipp da wirklich alles rein, ich nenn mein ganzes Leben damit auf. Ich find, das sind ganz essenzielle Metadaten. Da kann man ja dann irgendwann eine schöne eine KI mit personalisieren. Hatte dann die Probleme, dass das unglaublich langsam würde, die Oberfläche. Und ich hab jetzt im Verlauf von der Entwicklung von Nimuru, glaube ich, vier- oder fünfmal meinen Account gewechselt, weil einfach das das Ding so voll war, dass es dass es nicht mehr performant lief. Und wir hatten, wir wussten aber, es gibt eigentlich keine Lösung dafür. Wir haben ewig lange Schleifen gedreht in der Community, haben versucht, das rauszufinden, kamen dann halt aber zu der Erkenntnis, hey, wenn wir uns angucken, wie wie viel der Durchschnittsnutzer Memos hat, merkt er das Problem gar nicht. Also machen wir uns jetzt deswegen verrückt oder schieben wir das Problem vor uns her? Und wir waren dann an 'nem gewissen Punkt, war uns klar, okay, wir sollten den den den den Stack wechseln. Wir brauchen die Kontrolle über den ganzen Stack und dann haben wir dieses Thema einfach vertagt. Aber das hat uns sehr, sehr viele Bauchschmerzen bereitet.
Jan
Jetzt sprichst sprichst schon 'n wichtigen Punkt an so, diese Frage nach, ne, wann entwächst man halt so diesem Tool und wann muss man sich den Stack noch mal irgendwie grundlegend Gedanken machen. War das für euch von vornherein klar, dass ihr sagt, na gut, wir starten hier mit diesem No Code No Code Thema, einfach schnell sein zu können? Aber irgendwann werden wir's irgendwie selber bauen müssen, wenn alles gut läuft. Oder war eigentlich die Annahme, na ja, wir bauen das jetzt hier mit 'ner Flatterflow oder irgend 'nem anderen Tool und das kann uns auch bis zu den ersten zehn Millionen Usern tragen?
Tobias Müller
Ich glaub, gestartet sind wir schon 'n bisschen mit der Einstellung, dass wir das jetzt mal unlimitiert nutzen können. Natürlich trotzdem mit der Ungewissheit, welche Anforderungen wir noch haben werden. Wir haben aber auch schnell dann gemerkt, wir haben wirklich 'n Problem mit dem Faktor Zeit. Am Anfang eben diese diese Geschwindigkeiten, mit der uns wir uns bewegen, hat uns unglaublich geboostet. Wir konnten schnell iterieren. Wie Thuill schon sagte, unsere vierhundert Versionen, die wir da hatten, konnten direkt auf Feedback eingehen, konnten Sachen testen. Das hat uns sehr viel Geschwindigkeit gegeben, aber wir haben dann gemerkt, je komplexer das Projekt wurde, je größer hatten wir, je mehr hatten wir Probleme mit mit dem Thema Zeit ja bedingt durch. Zeiten, die teilweise bis zu zehn Minuten gingen, einfach nur, wenn man was in der UI ändert und hat dann eben dieses Hot Reload. Auch da sind die Zeiten bei so, keine Ahnung, eine halbe Minute. Und dann stimmt mal was nicht und man muss noch mal neu laden. Man will's aufm Device mal testen, ob da muss man dann wieder warten. Und Flatterflow, ja, kostet da halt leider viel Zeit. Und dann muss man halt irgendwann überlegen, ab welchem Punkt muss bringt man muss man zu viel Zeit für die Entwicklung aufbringen für diese Wartezeiten? Und wann wäre es es sinnvoller, eben auf 'ner anderen eigenen Stack zu wechseln?
Till Schneider
Wir hatten auch ein Problem. Wir haben ja die App dann direkt sehr mehrsprachig aufgebaut, also gleich vierundzwanzig Sprachen unterstützt von Anfang an. Da geht's eben auch drum, dass ausländische Fachkräfte ihr ihre Arbeit in ihrer Heimatsprache dokumentieren können per Sprache. Wir haben son bisschen son Speech to form System geschaffen dann in der App, da wir eben aus verschiedenen Industrien da Anfragen bekommen haben. Und da war einfach einen einen Bug in Flatterflow, dass ich hier die händisch oder ich ich hab einen Google Translate Button, der war erst mal super. Ich konnte oben die englische Sprache eintragen, dann hat er mir das übersetzt in die anderen dreiundzwanzig Sprachen. Erst mal war war da das Problem, dass Google Translate keinen Kontext mitgegeben bekommen hat. Also der wusste nicht, dass es eine UI Übersetzung hielt. Heißt, ich musste ganz viel dann, ich musste dann andere englische Wörter suchen, damit er besser versteht, dass es dass es sich eben eine App handelt. Das war schon 'n Krampf und dann gab's einfach einen Bug, dass er immer wieder die Übersetzung gelöscht hat. Und dann hatten wir sogar geschippte Versionen, wo dann wo er uns eine Seite, die wir überhaupt nicht angefasst hatten, wieder alle Übersetzungen zerschossen hat. Und da gab's, da haben wir auch eben Tickets für angelegt im in der Flatterfloor Community und und und. Kam aber nix. Also da gibt's, glaub ich, bis heute kein kein Fix dafür. Und wir hatten dann vermehrt genau dieses Problem der Hilflosigkeit eigentlich an der Stelle, wo wir wissen, okay, wenn wir das jetzt, hätten wir den Stack im Griff, könnten wir das lösen, aber wir können's grade nicht lösen. Und das waren dann so zwei, drei Baustellen und wo man dann mehr und mehr Bauchschmerzen bekommt und und dann halt eben die Zeit verliert. Weil auf einmal muss ich alle Seiten noch mal kontrollieren vor jedem Shipment und schauen, hey, ist da sind alle Übersetzungen drin. Also das ist genau der Punkt, Tobi meint so, diese die Zeit, am Anfang ging's unglaublich schnell und dann ging's unglaublich schnell unglaublich langsam.
Tobias Müller
Ja, und da haben wir halt schon früh gemerkt oder zu 'nem gewissen Punkt, wir müssen irgendwann den Absprung planen. Wir haben dann angepeilt, eine stabile Version zu haben, die wir in Ruhe lassen können. Und ab da dann eben zu sagen, okay, jetzt machen wir 'n Cut und jetzt wechseln wir auf unseren eigenen Stack. Also das haben wir schon früh angefangen zu planen.
Garrelt
Ja. Aber
Jan
das heißt ja natürlich oder vermutlich, in dem Fall ist das 'n kompletter, ne, weil son Tool wie Flatterflow sich wahrscheinlich nicht dafür anbietet zu sagen, wir machen hier son son hybrides Ding und ersetzen das so peu à peu vielleicht View by View oder Action by Action oder so was. Sondern Du musst im Prinzip, wie Du schon gesagt hast, ne, sone auch sone stabile Version mal kommen, die in Ruhe lassen und nebenbei dann im Prinzip komplett von vorne noch mal die ganze App bauen. Hat euch flutterfloda in irgend 'ner Art und Weise unterstützt? Also konntet ihr, weiß nicht, das Projekt, was ihr da hattet, exportieren und irgendwie dann im Code weiterbearbeiten? Oder habt ihr komplett auf der grünen Wiese angefangen? Wie wie läuft das? Oder habt ihr überhaupt schon angefangen oder plant ihr's grade nur? Vielleicht, das mal ganz vorne anzufangen.
Tobias Müller
Also also wir sind schon am Umbauen.
Till Schneider
Mhm.
Tobias Müller
Mhm. Also ich will nicht sagen, dass es nicht technisch möglich ist. Es ist durchaus technisch möglich, die Codebasis zu nehmen. Sobald man 'n Abo hat, hat man das Recht und kann man eben die die Codebasis nehmen und kann den Code auch selbst verändern. Aber dann kommt man, würd ich sagen, wirklich in Teufelsküche. Ich würde von abraten, weil dann noch mit wirklich der Flow Oberfläche noch Dinge zu verändern, fangt's an mit Merch Konflikten. Das geht So, nee,
Jan
dann hab ich's nämlich, glaub ich, falsch verstanden. Das ging mir nicht darum, parallel auch an der Codebase rumzuarbeiten, sondern die quasi zu nehmen und dann, also die Codebase einmal zu exportieren, flatterflow sozusagen abzuschalten, aber an der exportierten Codebase sozusagen weiterzuarbeiten einfach nur, ja? Dass man halt diesen Plattformlog in son bisschen umgehen kann.
Tobias Müller
Ja, okay. Ja gut, also prinzipiell gleiche Aussage, man hat den Code, man kann den nutzen, man könnte dann auch Vorurteile davon exportieren. Aber dadurch, dass halt flatterflow den Code selbst generiert und auch sein eigenes SDK dadrin nutzt, ist es, denke ich, nicht praktikabel. Also ich würde wirklich davon abraten, lieber die Logik, die dahintersteckt, nehmen und
Garrelt
aus der Logik die eigene App noch mal schreiben. Das geht ja
Tobias Müller
dann auch super mit KI heute. Da KI heute. Deine, Du schreibst deine Logik runter und sagst mir, erzeug mir daraus den Code. Viel effektiver als jetzt wirklich den Code da rauszuexrahin.
Jan
Aber das ist ja eigentlich 'n superwichtiges Learning, ne. Weil ich glaub, für ganz viele Leute ist so dieser Punkt, na ja, hab ich da 'n Plattformlog in oder kann ich am Ende meine App rausexportieren und mitnehmen?
Garrelt
Ja.
Jan
So, gibt ja gefühlte Sicherheit so eigentlich, ne, wenn Du damit anfängst. Und was Du jetzt ja eigentlich gesagt hast so durch die Blume ist, das hat eigentlich nichts wert, dieses Feature. Also es es gibt dir so sone gefühlte Sicherheit noch mit, aber 'n wirklich großen Nutzen außerhalb das zu retten, was Du hast, hast Du halt eigentlich nicht davon.
Tobias Müller
Ich ich kann mal kein Szenario vorstellen, wo das wirklich der Mehrwert größer wär als die Probleme, die man da durchkriegen würde.
Till Schneider
Ich würd das auch mal unter unter Marketing irgendwie verbuchen, weil wenn man wenn sich 'n Flatter Dev mal diesen Code anguckt, den man exportieren kann aus Flatterflow raus, dann wird der sagen, nee, nee, nee, wir müssen das so oder so komplett neu schreiben. Das entsteht halt aus aus diesem Low Code, No Code Ansatz, dass die halt das irgendwie zusammenschustern. Ich find da eine Parallele ganz spannend mit der Superbase, weil wir uns jetzt im im Sinne des auch damit beschäftigt haben, gehen wir auch aus diesem Log von Firebase raus. Und Superbase wirbt es ja ganz ähnlich an. Man kann da ja loslegen ganz einfach, aber sie sind ja Open Source und so weiter. Man kann es ja dann auch on prem hosten und selber hosten oder was auch immer. Ist halt dann aber doch nicht ganz der Fall, weil dieses ganze Tooling, was die Superbase so einfach macht, wenn man sie über deren Dienste nutzt, dann nicht mehr verfügbar ist. Und bei flut of floast, das würde ich sagen, noch extremer. Man vom Frontend her unglaublich wichtig, weil man konnte eben sehr viel Schleifen drehen. Das eine 'n Frontend nachzubauen geht verhältnismäßig einfach, es zu finden, ist ja das Schwierige. Also wo wo muss das Zeug hin? Wie wir deswegen, wir haben ganz schöne Screenshots von diesen vierhundert Versionsnummern und sehen genau, wie hat sich das verändert über die Zeit. Und dann haben wir eine eine gute Linie gefunden und die Linie jetzt nachzubauen, geht verhältnismäßig schnell dann. Aber ja, von der Codebasis, die mitzunehmen, nein. Und wir hatten ja nun dann auch eine Recherche gemacht und uns eigentlich auch gegen Flatter entschieden an der Stelle und haben auf REAC Native gesetzt.
Jan
Das ist jetzt noch mal 'n spannender Punkt, ne. Ich mein, jetzt habt ihr ja im Prinzip die ganze App schon mal in Flatter gebaut oder zumindest den Teil, den ihr custom gebaut gebaut habt davon, der ist ja vermutlich 'n Datenflatter gebaut in Flatterflow so, ja. Ja. Wieso seid ihr dann davon quasi weg, wo ihr dann Also da könnt ihr ja noch weniger recyceln, sag ich mal.
Till Schneider
Ja. Ja. Also ich finde da da auch lange lange recherchiert, mich reingefuchst irgendwie und die Es ist, glaub ich, relativ egal. Also es gibt in in Europa auch die größere Flatter Community. Ich kenn sehr viele Flatter Devs, die produktive Apps haben in Flatter gebaut. Und ich glaube, von der Größe der Community her, von der Menge der Packages her und dem Support her schenkt sich das wenig. Ich fand ich fand rig Native einfach deutlich interessanter, weil sie's jetzt erst, glaub ich, seit 'nem halben Jahr auch auch geschafft haben, aus einer zentralen Codebasis eine performante Web App hinzubekommen. Und Flatter, da es ja aus vom Canvas Model herkommt und eigentlich nicht aus der Webentwicklungsseite her, wird das wahrscheinlich nie schaffen, dass die wirklich performant wird, die Webseite. Wirklich eine Codebasis haben und damit auf alle Plattformen schippen können. Und da ist Reggnative meiner Ansicht nach jetzt 'n guten Schritt weiter plus Tobi und ich haben mehr Erfahrung von der vom Web Development Seite her. Deswegen fällt uns auch da die die, sag ich mal, die Sprache leichter. Es kommt kommt uns bekannter vor. Plus natürlich von Microsoft, Microsoft nutzt es immer mehr. Jetzt kam erst vor zwei, drei Wochen einen einen Riesenupdate. Wir haben eine neue neue Architektur geschipp, geschipped, weil Meta ist ja von Meta React auch in den in den Quests nutzt, also in ihren VR Headsets, das dort für die Oberfläche nutzt. Heißt, sie mussten extrem jetzt die Performance hochschrauben, dann Drop Frame in VR natürlich zu Motion Sickness führt und geht überhaupt nicht. Heißt, da ist einfach 'n megades Backing dahinter. Und vor allem, das war eigentlich der der entscheidende Punkt, man bekommt alle Plattformen performant bespielt.
Jan
Und als wie realistisch hat sich der Plan herausgestellt zu sagen, ihr habt eine stabile Version in Flatterflow, die ihr nicht mehr anfassen müsst. Und parallel arbeitet ihr quasi an dem an dem. Aber ich mein, das ist sone Situation, die haben wir da draußen, glaub ich, alle schon mal erlebt. Der hat's da also, hey, dieses alte Produkt fassen wir jetzt nicht mehr an. Und wir bauen hier einfach nebenbei das Neue. Ja. Und dann kommt halt doch irgendwie jemand die Ecke und sagt, könnt ihr können wir dich noch mal da eine Kleinigkeit ändern? Und da ist noch 'n Bug, der müsste irgendwie noch mal gemacht werden, weil bis das Neue kommt, dauert's irgendwie noch so lange. Und dann ist ja doch dieses Ziel, auf das man hinarbeitet, doch deutlich beweglicher, als man vorher gedacht hat, sag ich mal.
Tobias Müller
Ja, man muss die Sachen, glaub ich, einfach 'n bisschen runterschlucken und sich drauf fokussieren, dass man ja grade daran arbeitet. Weil dann hört man eben, ja hey, das und das geht nicht. Zu den Leuten, die eingeweiht sind, denen sagt man dann so, ja, das wissen wir. Die neue Version kommt bald. Zu den Externen sagt man, oh, vielen Dank, das nehmen wir uns zu Herzen, da arbeiten wir direkt dran. Also ja, wir arbeiten direkt dran, aber halt an 'ner komplett neuen App.
Till Schneider
Es war wirklich, dass wir's geschafft haben, die App noch an den Stand zu bekommen, an der sie jetzt ist. Und wir haben sie jetzt wirklich seit 'n paar Monaten nicht mehr angefasst. Und der letzte Schritt für uns war die Monetarisierung, dass wir dass wir's monetarisiert haben, dass die Leute da sich 'n Abo ziehen können. Diese letzte Versionsnummer war wirklich, also war wie im waren wir. Das, weil gefühlt ist ist uns mehr auseinandergefallen von jeder neuen Version, als dass es wieder funktioniert hat. Und es war wirklich, würd ich sagen, Glück, dass wir das noch mal rausgeschoben bekommen haben und es einfach auf 'nem guten Stand ist. Weil wie gesagt, Flatterflow auf einmal entschieden hat, hier, wir löschen euch Übersetzungen, wir löschen machen irgendwelche Hintergründe kaputt oder so. Das das war wirklich 'n Kampf dann am Ende gegen gegen gegen das Framework, sag ich mal.
Tobias Müller
Ja, es war es war, glaub ich, so gefühlt 'n bisschen Glück, dass wir genau jetzt zum wichtigen Zeitpunkt den Absprung geplant haben, weil's wirklich schlimm wurde jetzt.
Till Schneider
Ja, ja. Knappe Kiste. Okay.
Jan
Das forciert natürlich son bisschen die spannende Frage. Wenn ihr jetzt Memoro noch mal bauen würdet oder in ein paar Jahren ein anderes Start-up macht oder was auch immer, ein anderes Produkt im im selben Space, würdet ihr das noch mal genauso machen?
Garrelt
Zu Anfang drüber.
Tobias Müller
Ja, wir haben 'n bisschen differenzierte Meinung. Also mal allgemein find ich immer noch, ist valide. Für jemanden, der keine Codingerfahrung hat, eine eigene eine einfache App machen will, keine Ahnung, Du machst Schmuck und willst dann unbedingt auf eine App anbieten. Hey, dann, damit wirst Du glücklich, denk ich, mit flatterflow. Das wirst Du damit gut hinkriegen. Aber so was wie mit Moreo grade heute würd ich nicht mehr mit Flatterflow starten. Und genau, ich glaub, da kann ich Herrn Till übergeben zu seiner Meinung.
Till Schneider
Ich find das eine eine unglaublich spannende Frage, weil ich mach mir da viel Gedanken drüber, was wir denn jetzt eigentlich die letzten eineinhalb Jahre so gemacht haben und dann was wir so gearbeitet haben. Und vor allem haben wir uns, weil wir son kleines Team sind und eigentlich auch nicht das Budget haben, groß zu wachsen in dem Sinne. Und ich halte auch viel von organischem Wachstum, ich halte relativ wenig von riesenschnellen VC und wir sammeln viel Geld und wir machen hier. Das das find ich alles 'n bisschen eigentlich kontraproduktiv. Wir haben uns extrem drauf konzentriert, wie werden wir besser? Wie wie können wir noch schneller bauen? Also Tobi und ich, wie arbeiten wir besser zusammen und wie enhanzen wir uns noch mehr mit KI? Meine persönliche Meinung ist, ich würde nicht mehr mit Flatterflow starten, weil die Lernkurve eben so steil ist, dass meiner Ansicht nach heute, wenn man sich 'n Cursor nimmt oder 'n Windsurf nimmt, wenn man sich diese IDIs hernimmt, die wirklich gepimpt sind mit KI, man eigentlich schneller vom Fleck kommt. Und wir haben jetzt über die letzten drei, vier Monate, glaube ich, fünf, sechs, sieben neue Applikationen geschaffen, die alle in in 'ner guten Alphaphase sind, auch intern. Und wir haben unsere Webseite auch zum Beispiel, die jetzt mit NextJS und so, hab ich komplett mit mit Curcer damals komplett mit KI neu geschrieben und so. Das die liest die Marktdown Dateien aus, baut sich über Marktdown Dateien auf und so weiter und so fort. Man kann die dann recht einfach befüllen. Wir haben sehr viel experimentiert mit verschiedenen Sachen und ich glaube, es macht keinen Sinn mehr, diese Dependency und diese Probleme, die diese Dependencies eigentlich mit sich bringen, einzugehen. Man sollte mehr und mehr sich drauf konzentrieren, den Stack selber im Griff zu haben und grundsätzlich weniger Dependencies, weniger kleine Pakete für für irgendwelche kleinen Helferfunktionen und so weiter nutzen, sondern es die KI schreiben lassen. Und langsam sind die Tools, ich mein, die Zeit arbeitet da ja für uns und die das Tooling wird besser. Ich hab das Gefühl, die KI ist noch nicht so richtig in der Endanwendung angekommen, sondern es es passiert ganz viel im im Tooling, diese Tools zu bauen. Aber es ist 'n ganz neues Medium und wie bei jedem Mediumswechsel, die erste Fernsehsendungen, die liefen, da haben sie halt Radioshows abgefilmt, weil sie wussten ja auch nicht, was sie jetzt erfüllen sollen. Also man nimmt irgendwie so die alten Dinge auch noch mit, auch ausm UI Design und so weiter. Da hinken wir eigentlich hinterher, da gibt's extremes Innovationspotenzial. Meiner Ansicht nach hat hat AI 'n großes UX UI Problem eigentlich, wieder leere leere URL Zeile wie Anfänge des World Wide Webs und wir haben einfach wir haben einen unglaublichen Drang zu bauen. Also Tobi hat dann irgend eine Idee, wo er wo er sich 'n kleines Helfertool bauen will und baut es halt einfach. Und damals haben wir das noch in Bildship zusammengesetzt und und in in Flatterflow tatsächlich haben wir die ersten Prototypen gebaut und jetzt machen wir das einfach in Code. Und es geht meiner Ansicht nach schneller, aber wir wissen ja auch, dass das ist es heißt, es ist langfristiger. Wir haben den Stack im Griff und wir können dieses Tool jetzt mit anderen Tools verbinden. Und jetzt kommen Enterprise Customer und wollen das on prem haben oder unsere Schweizer Kunden wollen das in der Schweiz gehostet haben, die Deutschen in Deutschland und dann ist das möglich. Also wir sind viel, viel flexibler und wir bauen halt gleich was auf auf 'nem guten Fundament. Und langsamer geht es, glaub ich, nicht mehr.
Jan
Aber der Punkt, den Du gemacht hast mit, die die Zeit spielt ja dir in die Karten. Das gilt ja auch für so Tools wie Flatter, Flow oder wahrscheinlich analog auch für vergleichbare Tools. Vielleicht hab mir das eben nur mal so kurz nebenbei aufgemacht. Und da gibt's jetzt auch AI Funktionen, die so, ja, wo Du halt son bisschen schnell was malst und da generiert dir dein dein Widget daraus oder Nicht
Till Schneider
gut. Also leider ist das es ist es wirklich einfach nicht gut. Also Aber
Jan
aber wie Du gesagt hast, das ist ja jetzt das Schlechteste, was es jemals sein wird, so, ja? Also auch da ist ja die Frage, ne, weil Du dieses UI Thema auch so angesprochen hast, also da Also ich versteh deine Abwägung jetzt vollkommen, so. Ich frag mich halt, ob das in fünf Jahren immer noch genauso ist. Weil was ich immer seh, wenn Leute mit AI Code generieren oder UIs generieren oder so, es ist superschnell, solange diese diese menschliche Sprache als und funktioniert, ja? Wenn Du jetzt schreibst, hey, ich brauch 'n Login Screen und ich will hier 'n Userfeld und 'n Passwortfeld und 'n Login Button und 'n Registrieren Button und 'n, weiß ich nicht, Button oder so was, ja? Und dann wird das erstellt und alles cool. Und dann kommt so diese diese kleinen Details, ne. Ich brauch hier mein mein Logo drin und ah, nee, ich brauch's irgendwie anders. Das sitzt noch nicht so ganz richtig, wo man halt eigentlich in sonem grafischen Editor, ne, wenn wenn die Grundlage mal erzeugt, das würde man eigentlich viel lieber mit der mit der Maus mal so hingehen und das son bisschen bewegen oder ziehen oder noch mal noch mal anpassen. Und solang ich aber immer 'n neues prompt irgendwie schreiben muss so, ah, mach's doch noch mal 'n bisschen anders und schiebst's mal mehr dahin oder versuch's mal so und so. Das ist, glaub ich, beim Iterieren noch ziemlich mühselig grade. Was man, glaub ich, eigentlich haben will, ist doch son sone Toolchain, wo der Initialzustand quasi AI generiert wird und ich dann aber trotzdem 'n grafisches Interface hab, die letzten null Komma eins Prozent irgendwie Feintuning noch zu machen, ja. Ich will ja nicht in den Code gehen, nur Margin irgendwie anpassen zu müssen.
Garrelt
Weißt Du,
Jan
was ich mein?
Garrelt
Ich weiß, was Du meinst, aber das Problem der aktuellen No Code No Code No Go Tools ist eben, dass sie diese zusätzliche Abstraktionsebene ein
Till Schneider
aktuellen Tools ist eben, dass sie diese zusätzliche Abstraktionsebene einführen und sie dich nicht an den ganzen Stack ranlassen. Du hast nicht die komplette Kontrolle über deine Tools. Also da braucht's, glaub ich, einfach eine neue Generation von Tools, die Ja, vielleicht. Im Grunde genommen auf voll mit KI gebaut sind und diese Abstraktionsebene einfach weglassen. Die können mir das ja dann anzeigen visuell und ich kann meinen Button verschieben, wobei das auch 'n bisschen 'n Wunschtraum ist, dass man dann die Margin anpasst, weil wie verhält sich das dann in der responsivness? Also das ist dann alles gar nicht so einfach. Son visueller Editor bringt auch Probleme mit sich auf der anderen Seite, aber ich glaube, da braucht's, man braucht die Kontrolle über den Stack. Und diese flatte Flow Ebene bringt halt Performance Issues mit sich, bringt eben mit sich und so weiter und so fort. Also zum jetzigen, ist wär schön, wenn wir das hätten, dass jetzt quasi, aber das das muss dann 'n VS Code bringt dann halt irgendwie eine eine Möglichkeit, dass man da auch visuell 'n bisschen rumschieben kann. Da seh ich das dann eher, dass dass sich eigentlich die Code Editoren in die Richtung bewegen, anstatt dass diese Low Code No Code Tools mehr wie die Code Editoren werden. Mhm. Mhm.
Garrelt
Ich
Tobias Müller
glaub, das ist dann auch son bisschen unsere differenziertere Ansicht, weil ich finde, es gibt trotzdem noch valide Argumente für die Low Code No Code Editoren. Ja, es würd, denk ich, schon einiges sich noch ändern. Aber wie gesagt, hast Du diesen ganz ganz einfache Anforderungen oder hast auch überhaupt keine Ahnung von Design oder so. Oder willst einfach nur schnell mal 'n Prototypenen zusammenklicken, find ich, hattest Du durchaus schon noch seine Existenzberechtigung, ich persönlich.
Jan
Also ich muss auch sagen, in meinem vorherigen Job hatten wir auch No Code No Code Tools benutzt, so interne Sachen einfach zusammenzubauen, ja? Und ich sag mal, für so das interne Dashboard zur Eventplanung, ja, wo jetzt auch Design mal komplett zweitrangig ist und es eigentlich mehr son glorifiziertes ist für so einfache Update Delete Operationen und 'n bisschen Auswertungen oder so. Dafür wird es, glaub ich, auch auf sehr lange Sicht nicht zu schlagen sein. So ja, weil das halt noch Weiten mächtiger ist als son Excel Spreadsheet so und deutlich billiger zu machen als irgend eine Custom Softwarelösung, so. Ja, ich glaube, das wird vielleicht die Nische, die Sie da so lang- und mittelfristig besetzen können. Aber wahrscheinlich hast Du recht, Tobias, ja, es gibt gibt auch genug andere Use Cases, wo's sich vielleicht dann dann auch nicht lohnt.
Till Schneider
Sehe ich genauso. Aber sobald's, glaub ich, Customer Facing wird und man will man will in eine gewisse Skalierung reingehen und man will einfach das beste die beste User Experience bieten, was man ja für interne Tools nicht muss. Da geht's nicht drum. Da weiß jeder irgendwie, was er da jetzt machen soll, das Sommerfest zu planen und dann dann ist das super. Aber ja, vielleicht geht dann da so, trennt sich da die Spreu vom Weizen. Als Tobi und ich uns dann irgendwann, das war recht spät in dem Prozess, mal angeguckt haben, was sind denn so die ganz tollen Flatterflow Apps, die so die live sind, waren wir dann auch sehr ernüchtert, weil also die die das, was die da alles showkasen an an den den erfolgreichen Flatterflow Apps, die sind auch gar nicht so gut und und gar nicht so erfolgreich und und und. Also ich glaub auch, es ist sone Frage von von für wen ist es dann gedacht am Ende.
Garrelt
Ich glaube, ihr habt das schon beantwortet, aber das heißt, am Anfang seid ihr eingestiegen mit, es ist cool für Prototyping und wenn's schnell gehen soll. Aber wenn ich das richtig verstehe, wenn ihr jetzt in dem Hacker fahren seid, würdet ihr auch eher auf cursor a I gehen und das so erstellen. Hab ich das richtig verstanden?
Tobias Müller
Ich glaub, das war dann tatsächlich der Punkt, wo wir anfangen zu diskutieren, wo Till wahrscheinlich ironischerweise Till, der nicht Und dann ist der Herr der Podcast hat
Jan
vorbei nach zwei Tagen diskutiert.
Garrelt
Das ist
Jan
schon fatal.
Till Schneider
Genau. Ich ich
Tobias Müller
glaub, das ist 'n bisschen die Ironie. Till eben, der nicht dann Code schreibt, der wird dann vielleicht eher zu Cursor oder Windsurf tendieren und ich vielleicht eher dann tatsächlich zu Flatterflow, der eigentlich coaten kann. Keine Ahnung, aber ja.
Till Schneider
Ich glaube für für Leute, die, also ich persönlich würde es so machen, dass ich dann mit Cursor oder oder Wünsdorf anfange, aber anderen Menschen würde ich da vielleicht schon noch 'n Low Code No Code Tool empfehlen. An der Stelle vielleicht jetzt nicht Flatterflow, weil's einfach noch komplexer ist. Da gibt's eben andere Low Code No Code Tools, die deutlich simpler daherkommen und für einen Hacker Thorn dann besser geeignet sind. Aber ja, für uns persönlich macht es keinen Sinn mehr, in diese Abstraktion reinzugehen, weil wir jetzt weil wir uns halt auskennen und schneller sind, wenn wir es selber coden. Aber für jemand ohne Erfahrung hat seine Tobi, ich
Jan
stell mal eine These in den Raum, weil ich würde mich, glaub ich, dir auch anschließen und auch eher zu dieser zu sonem grafischen Interface Code, nicht Nicht Code Editor tendieren für son Setting. Aber weil ich bei mir persönlich auch immer so das Risiko seh, wenn ich das jetzt in sonem HTML mache und egal, ob das auto generiert ist und oder weiß nicht, Du nimmst fertiges Template ausm Netz oder so was, keine Ahnung, ne. Man verliert sich dann halt doch so ganz schnell in diesen kleinen Sachen so, oh, hier, das könnt ich schon mal weg abstrahieren und hier könnt ich irgendwie noch mal das 'n bisschen eleganter machen als diese Vorlage oder dieses Generierte. Und das ist natürlich sone Falle, in die man in 'nem No Code, in einer No Code Umgebung halt nicht so reinfällt. Ja, man hat jetzt okay, ich klick jetzt mein Interface zusammen und vielleicht ist der Button noch nicht perfekt, aber für mich, dann mein ich jetzt mich, Jan, ja, der im Prinzip null grafischen Gestaltungsanspruch hat, weil ich zwei linke Hände hab, was so was angeht, da reicht das halt. Dann bin ich halt viel, viel schneller so, mal zusammen zu klicken sozusagen, anstatt irgendwie eine IDI aufzumachen und nach zwei Stunden zu merken, dass diese eine CSS Klasse irgendwie immer noch nicht so will, wie ich will, ja. Und halt wieder nur viel Zeit verschenkt hab. Ach, Ja, ich ich ich glaube,
Tobias Müller
das ist 'n valides Argument, ja. Der da da triggert dann son bisschen der Perfektionismusdrang. Den sieht man halt nicht bei sonem Editor.
Till Schneider
Deswegen, das ist sone spannende Diskussion, weil ich glaub auch nur, dass dass dass das meine Meinung ist, weil ich eben keine Erfahrung hab im Coden. Also Ja, Verständnis ist. Ja und das ist aber eine ganz spannende Herangehensweise. Also ich bin ich bin ich bin fest davon überzeugt, ich kann die ich kann die Dinge anders entwickeln. Ich denk anders über sie nach, weil mir ist es egal, wie gut der Code aussieht, da jetzt eben der Wechsel von Curser to Windsurve, vielleicht für meinen schon for shadowing für meinen Pick des mein mein Pick des Tages, finde ich ganz spannend, weil mich Ich les den Code auch nicht mehr. Wirklich, das interessiert mich nicht, was im Coach steht. Ich les den nicht. Ich ich drück aufn Knopf und dann drück ich zehnmal noch mal aufn Knopf. Und wenn ein Ärger rauskommt,
Jan
dann Zu viel Lehrer aus dem sehen gerade nicht, wie hochgaret die Augenbrauen gezogen hat. Die sind quasi zum zum Hinterkopf.
Till Schneider
Ich weiß, Du weißt die. Aber es hängt halt davon ab, was man baut. Und wir bauen deswegen sehr viel neue Tools, damit wir die die vom vom Funktionsumfang sehr abgespeckt halten können. Und mir geht es nicht die Codequalität. Mir geht's darum, dass ich 'n fertiges Produkt hab, was ich den Leuten schippen kann. Und und was da drunter für 'n Müll ist, da ist ja Code ist ja 'n Code nicht perfekt, ist nie perfekt. Das ist eher Gerade nur eine größere war, sonst läuft der irgendwann ausm Ruder. So, das das das entbrase ich von vornherein, das Chaos. Ich weiß noch nicht, wie Oh, okay. Wo das denn gelang ist? Schwierig. Ich
Jan
ich kann da ganz kurz eine eine Anekdote zu erzählen aus Ja. Aus 'nem Consultingprojekt von jemandem, die mal ein ein Interface, ein Frontend in Auftrag gegeben haben, irgendwo offshoring mäßig. Mhm. Und das haben wir dann zurückbekommen in irgend sonem Javascape Framework und und und bla. Und dann mussten da noch 'n paar Änderungen gemacht werden, weil es halt nicht so ganz dem Design entsprochen hat in allen Details. Und dann sind wir da 'n paar Wochen vergangen und das gab eine neue Auslieferung von dem Interface. Und wenn man da mal den Code reingeholt hat, hat man halt gemerkt so, das hat kein Stein auf dem anderen geblieben. Ja. Und dann wurde da so nachgefragt, so, wieso habt ihr denn das ganz? Also wieso ist denn da so viel geändert und so, ja, weil wir das halt komplett neu gebaut haben so. Also bevor wir uns die Mühe machen, hier den Leuten quasi zu erklären, das ist der Stand, das muss geändert werden, gibst Du halt dieselben Specs Ja. Noch mal fünfzig anderen Offshore Arbeitern so. Und das ist halt einfach noch mal komplett neu iterieren, So.
Till Schneider
Keine Techdap, keine Legacy Code, wie
Jan
Ja, genau, einfach komplett bei null an.
Garrelt
Das ist
Jan
halt viel einfacher für die, wenn Arbeitskraft quasi nix kostet. Und im Prinzip ist ja das mit AI genauso, ja? Wenn Arbeitskraft nix kostet, ist ja jede Iteration umsonst sozusagen. Und kannst das dir dann halt auch erlauben. Aber meine meine abschließende Frage an Gareth wär eigentlich gewesen, nach dieser Diskussion jetzt hier, Gareth, ja. Wenn Du jetzt bei 'nem Hackathon anfangen müsstest, ja, für was würdest Du dich denn entscheiden?
Garrelt
Ja, also ich glaub, mein Fazit ist so, das kommt aufs Team an, weil ich hab schon das Gefühl zum Beispiel, es ist es ist eine arge Zielgruppenfrage, dieses Thema, nutzt man dieses Tool oder nicht? Und mein Gefühl war eigentlich, Till und das wollte ich dich auch noch fragen, dass es für dich gefühlt eigentlich 'n cooles Tool war, weil Du so von Design supergut auch ins Entwickeln einsteigen kannst, so. Und so der Weg darüber in meinem Kopf halt 'n schöner ist und 'n guter, weil man eben so viel schon sieht und an manchen Stellen einsteigen kann, technisch, wenn man will, es aber auch erst mal weglassen kann oder von anderen machen lassen kann. Und deswegen wär, glaub ich, meine Entscheidung so, hab ich jetzt nur Entwickler in meinem Hacker vom Team? Dann wahrscheinlich nicht. Aber hab ich auch Designer, die auch Lust haben, nicht nur zu designen, sondern das auch direkt zu bauen, was ja auch dann den Entwicklern erst mal auch Arbeit abnimmt und eine bessere Zusammenarbeit ermöglicht. Auf jeden Fall, find ich's total sinnvoll. Und ja, wahrscheinlich und mein zweites Learning wär dann, haben wir gewonnen, ist es eine coole App, wollen wir daraus 'n Produkt machen, direkt weg von No Code. Das wär, glaub ich, so mein mein Learning.
Jan
Das ist, glaub ich, 'n sehr schönes Fazit. Das das kann man so stehen lassen als letzten Satz. Aber das ist ja noch nicht ganz der letzte Satz, weil wir haben ja noch unsere. Und Gerald, weil Du grade son schönes Fazit gezogen hast, ja, darfst Du auch anfangen mit deinem. Sehr gerne.
Garrelt
Ich habe einen Artikel mitgebracht, den ein Kollege vor Kurzem geteilt hat mit uns, der heißt von Iven Smith. Und das ist im Prinzip ein sehr ausführlicher Artikel, gibt auch einen Talk dazu, also kann man sich auch anschauen, wen man möchte. Darüber, wie er Kenntnis als Entwickler lebt sozusagen. Also wie kann man, ja, Kenntnis, Freundlichkeit, nee, wie übersetzt man das Deutsch? Ja, ja doch. Freundlichkeit. Wie kann man das als Entwickler in seinem Arbeitsplatz ausleben und fördern und andere Leute da so auch mitreißen? Er beleuchtet das von verschiedenen Sichtweisen und ich find es sehr schön gemacht. Es ist 'n sehr schöner.
Jan
Nice. Wunderbar. Tausend Dank. Dann Tobi, was hast Du im Gepäck?
Tobias Müller
Ich hab 'n kleines Open Source Tool, das heißt Local Sand. Da bin ich drauf gestoßen, das hilft mir hin und wieder, weil ich eben durch die Crossplattformenentwicklung aufn Mac wechseln musste, bin aber eigentlich eher von der Android Linux Site und so weiter gekommen. Ich hab also immer noch 'n Android Phone und da kann ich halt keinen Airdrop nutzen. Und Local Sand ist 'n Open Source Tool, womit man einfach wirklich auf allen Plattformen schnell mal Dateien oder Texte auch verschlüsselt im lokalen Netzwerk senden kann. Also ich hab irgendwie 'n, ich bau grad eine APK, nämlich Local Sand, Shicks aufm Handy. Auch hab ich 'n iPhone hier liegen, kann ich auch darüber schnell was schicken. Also superpraktisch und vor allem die Oberfläche ist super minimalistisch und intuitiv.
Jan
Wenn das Open Source ist, wie krieg ich das auf mein iPhone drauf? Gibt's auch im Store oder muss ich mir das selber bauen und kompilieren oder wie wie wie wie komm ich da dran?
Tobias Müller
Ich vermute, dass es im Store ist. Ich kann's dir grade nicht sagen. Doch, ich ich hab grad geguckt.
Garrelt
Entweder im Store oder Package Manager haben, das gibt's auf verschiedenen Plattformen verschiedene Links.
Till Schneider
Okay. Cool.
Jan
Wunderbar. Till, was hast Du im Gepäck?
Till Schneider
Genau, ich habe Windsurf mitgebracht. Ich eben als als Nichtcoder bin dann von Cursor gewechselt zu Windsurf jetzt vor, also ich benutz beide Tools noch parallel, aber primär jetzt eigentlich Windsurf, ist eben eine neue IDE, die viel ist als Cursor, also die die dreht selber Denkschleifen, schlägt selber die nächsten Schritte vor und ist gefährlich. Also ich würde das wirklich in 'nem neuen Projekt testen, da die wie son, es ist wie 'n breiter Pinsel. Das ist wie man wirft Farbe auf die Leinwand, wo man mit Curseern noch noch zielgerichteter an Probleme rangehen kann, fängt es immer an, gleich die ganze Codebasis in in in Hinblick zu haben. Man muss zum einen keine Dateien mehr referenzieren, sondern der sucht sich die selber raus. Es ist von der von meiner Developer Experience her, wenn man davon sprechen kann, ist es komisch, weil man drückt drauf und dann geht's drei Minuten und man weiß jetzt nicht, was man in den drei Minuten machen soll. Mit mit Cursor kam ich noch schön in einen Tunnel rein und konnte konnte mir überlegen, während der das diesen eine Änderung jetzt macht, okay, wo könnten wir jetzt weiter weitermachen? Also es erfordert 'n bisschen ein Umdenken. Ich find's aber extrem spannend, weil es einfach so der, find ich, die noch mal der nächste Schritt ist in Richtung, KI kann auch in größeren Code Codebasen gut unterwegs sein, kann einem Dinge, kann Probleme finden, die die irgendwo versteckt sind und hat mich jetzt noch mal ermöglicht, Dinge noch schneller zu bauen eigentlich und auch andere Sachen noch besser zu referenzieren. Also fand ich jetzt eine 'n tolles Tool und zeigt son bisschen, find ich, die die die deutet die Zukunft an, wo's hingeht.
Jan
Ich hab bei diesen ganzen AI Tools immer so zwei Fragen. So a, was was ist das Pricing? Und gibt's die Möglichkeit, das auch irgendwie zumindest teilweise lokal bei mir laufend zu haben?
Till Schneider
Tatsächlich weiß ich bei CursOR nicht, ob man seine eigenen API Keys einbinden kann. Ich weiß, dass es also bei kostet ja zwanzig Euro im Monat, wenn man das pro Tier hat. Ich hab da aber ständig auch neue dazukaufen müssen, also ich zahl da eher achtzig Euro im Monat für. Und bei, ja, aber das genau, also das ich hab da überhaupt keinen Schmerz, weil anders könnt ich ja gar nicht arbeiten. Also für mich ist es ja quasi essenziell notwendig, Nein,
Jan
nein, nein, alles cool.
Garrelt
Ich ich
Jan
bin nur drüber gestolpert, weil wir hatten, ich hatte vor Kurzem 'n Gespräch mit Fabi, als wir 'n Gastauftritt im Engineering Kiosk, kleines Shoutout, gehabt haben, da haben wir nämlich auch über Cursor gesprochen. Dann hab ich nämlich Also Fabi hat Cursor gepitcht. Und das war nämlich genau meine Frage, so dieses Pricing Model, wenn ich's für mich so sehe, ne. Null, zwanzig und Teamplan, so ohne Preis sozusagen, was wir da auf der Webseite haben. Und dann stand da so dabei, Du hast irgendwie, weiß ich nicht, zweihundert Autokomplicions im Monat oder keine Ahnung, was Du da für zwanzig Euro kriegst. Und das war für mich als als Entwickler, als Endkunde gefühlt vollkommen intransparent, weil wenn Du mich fragen müsstest, wie viel Autokomplicions brauchst Du denn überhaupt? Also ich Keine Ahnung. Keine Ahnung, so, ja. Vielleicht fünfzig, vielleicht fünftausend? Keine Ahnung.
Till Schneider
Ja. Also ich brauch dann dementsprechend achthundert im Monat oder so circa. Und dann zahl ich meine achtzig dafür.
Jan
Ja, das ist schon schlauer als ich
Till Schneider
Und also Windsurf kostet weniger. Windsurf kostet 'n Zehner im Monat, aber Du hast da wieder, ich bin jetzt, was ist, zwei Wochen Free Trial. Jetzt haben sie mir für Thanksgiving noch mal 'n Monat, glaub ich, oder zwei Monate Free Trial geschenkt, also wie's halt so ist. Sie sie hocken dich, aber sie haben mich schon lange gehook, also an der Stelle hätten sie mich jetzt eigentlich auch schon zur Kasse bitten können. Aber ich bin ja bin ich froh drum. Genau, heißt, es ist 'n bisschen günstiger. Ich bin mir nicht sicher, ob Du deine eigenen API Keys einbauen kannst. Genau, aber fand ich fand ich 'n sehr, sehr schönes Tool. Gleich gleiche Geschichte, Fork von VS Code. Genau.
Jan
Aber auch mit 'nem API Key wär's ja immer noch bei irgendjemand anderem gelaufen. Ich so, weißt Du, ich such noch son Editor, wo ich auch so lokale Modelle irgendwie am Start hab. Ja. Und das gibt's gefühlt
Till Schneider
noch nicht. Noch nicht.
Jan
Wirklich so.
Garrelt
Ja. Cool. Da aber
Tobias Müller
'n Tipp, wenn Du Cursor nutzt, kannst Du zum Beispiel auf auf eine eigene deine eigene GPT Instanz hosten. Das ist ja dann auch DSGVO konform. Das kommt zumindest mal 'n bisschen Richtung Datenschutz und Sicherheit in die Richtung. Und dann kannst Du auch den API key dafür nutzen.
Jan
Tatsächlich, so blöd es jetzt klingt, aber Datenschutz ist nicht mein Main Console. Mein Main Console ist eher so, ich sitz im Zug oder im Flugzeug oder keine Ahnung, Du hast ja aber einfach kein Netz braucht, weißte? Ja. Was die die perfekte Überleitung zu meinem Pick ist, mein Pick ist nämlich Air Fly. Ich weiß nicht, wer von euch Air Fly kennt, das ist son kleines Hardware Ding. Wer öfter mal im Flugzeug sitzt, so wie ich das in letzter Zeit viel getan habe, ärgert sich vielleicht manchmal, dass wenn er irgendwie son Onboard Film gucken will, diese komischen Headsets von irgendwelchen Airlines oder so benutzen muss. Und die coolen Kopfhörer, die man selber dabei
Till Schneider
hat, meistens nicht funktionieren, weil das ja in der Regel alles Bluetooth Sachen sind, die wir so haben.
Jan
Mhm. Und Airfait, in der Regel alles Bluetooth Sachen sind, die wir
Tobias Müller
so haben. Mhm.
Jan
Und Air Fly ist, ich hab jetzt mal kurz, ich hab mal son kleines Dongle, was man im Prinzip an son Audioanschluss hängen kann. Und das macht im Prinzip einen kleinen Bluetooth Anschluss da dran und man kann seine Lieblings Kopfhörer, AirPods, meine Bose Kopfhörer, was auch immer kann ich dann eben benutzen, ohne dass da irgendwie Bluetooth schon dabei sein muss. Und warum grade der Airflight irgendwie so cool ist, Weil er zwei besondere Features hat, die mir immer noch weiterhelfen. Zum einen kann ich mehrere Kopfhörer dadran koppeln. Das heißt, wenn meine Frau, meine Tochter, wie auch immer im Flugzeug mitgucken wollen, können sie im Prinzip auch ihre Kopfhörer dranhängen. Und ich hab wie im Prinzip wie son Splitter dann noch drin. Und man kann dasselbe Gerät nehmen und nicht nur als Transmitter für Bluetooth benutzen, sondern auch als Receiver. Das heißt, wenn ihr am im Urlaub angekommen seid und ihr habt dann quasi einen Mietwagen, der vielleicht auch kein Bluetooth hat und dann steckt ihr das Ding in den Kopfhörer oder in den AUX Eingang von dem Auto, könnt ihr euer Handy quasi so rum daran connecten und habt auf einmal Freisprecheinrichtungen und könnt eure Musik hören und keine Ahnung was. Also es ist einfach 'n superkleines praktisches Gerät. Das ist immer bei mir in der Reisetasche drin und es ist auch in eigentlich jedem Urlaub irgendwie in in Verwendung so. Es ist leider 'n bisschen teuer, ich glaub, es kostet so sechs sechs oder siebzig Euro. Gibt auch eine kleinere Variante davon, die ist 'n bisschen billiger, wenn ich alle Features brauch, aber so oder so. Kann ich nur empfehlen, hat mir schon viel Urlaub gerettet, weil es einem auf einmal ermöglicht, mit Noise Cancelling im Flugzeug zu sitzen und trotzdem 'n coolen Film zu
Garrelt
gucken. Ja. Das ist bei denen sehr spannend. Die haben ein Pro für fünfundfünfzig Euro und ein Duo, was alles kann wie der Pro plus mehr Akkulaufzeit für achtundvierzig Euro.
Jan
Und es gibt noch 'n s e, was irgend 'n Feature nicht hat, was auch noch mal 'n bisschen billiger ist. Aber ja, Ich ich hab den Pro, keine Ahnung, schon vor fünf Jahren oder so gekauft oder was weiß ich, als er rauskam. Bin bis dahin eigentlich sehr, sehr zufrieden. War damals ganz happy, weil das der Erste war, der auch so USB-c charging hatte und Und schon seit Längerem dabei bin, so mein ganzes Travel Equipment auf USB-c umzustellen und nicht mehr mit irgendwelchen komischen Adaptern rumreisen zu müssen, nur noch eine Sorte von Kabel dabei zu haben. Ist auch sehr befreiend. Ja.
Tobias Müller
Das das fühl ich mit den Anschlüssen. Das ist fast schon das größte Argument von allen.
Jan
Ja, also das war auch damals, als ich mein mein iPhone fünfzehn gekauft hab, war so, weißt Du, Kamera, Action Buttons, komplett egal. Hauptsache gib mir halt son USB-c-Anschluss. Endlich einheitliche und
Till Schneider
ich kann Lightning wegschmeißen, ja.
Jan
Ja, genau. Cool. Dann Till, Tobias, danke für die Zeit. Danke, dass ihr da wart. Danke, dass ihr uns viel über Low Code und No Code und Vor- und Nachteile erzählt habt. Ich glaub, das war superspannend auch für alle, die da draußen dazu gehört haben. Danke, Garelt, für die Zeit. Danke an euch da draußen fürs Zuhören. Wenn ihr Fragen, Anmerkungen, Kritik, wie auch immer habt, dann immer gerne an Podcast at Programmier Punkt bar eine E-Mail schicken oder das Kontaktformular auf der Webseite benutzen oder Kommentare hinterlassen auf Youtube, Spotify oder irgendwelchen anderen Plattformen. Lesen immer fleißig mit und wir freuen uns auch immer über Bewertungen über den Podcast bei iTunes zum Beispiel, wenn ihr uns da hört, findet, gefunden habt, wie auch immer. Ansonsten bleibt nicht mehr viel zu sagen, außer dass wir uns hier bald wiederhören. Und tschau tschau. Tschau.

View file

@ -1,27 +0,0 @@
Eine App, die Zeit spart
Konstanzer Gründer bringen Memoro auf den Weg
Damit gewinnen sie ein gut dotiertes Stipendium
VON JÖRG-PETER RAU
joerg-peter.rau@suedkurier.de
Konstanz Die Besprechung dauert eine gute halbe Stunde, und für Smalltalk ist kein Platz. Wo steht das Projekt? Was sind die nächsten Schritte? Und was haben wir beim letzten Mal vereinbart? Es ist eine Situation, wie sie allein in Konstanz wohl hunderte Male jeden Tag vorkommt. Auch beim Besuch des Pflegedienstes muss es konzentriert zugehen. Jede Hilfsleistung, jeder Handgriff ist mit einem Minuten-Budget versehen. Und dann muss das alles penibel dokumentiert werden. Aber in der Zeit des Ausfüllens kann die Pflegekraft genau das nicht machen, was eigentlich ihre wichtigste Aufgabe ist: Pflegen.
Zwei von vielen Beispielen, in denen Künstliche Intelligenz (KI) keine Bedrohung, sondern geradezu eine Verheißung ist. Im ersten Fall erstellt sie ein Besprechungsprotokoll. So knapp, dass es noch gelesen wird aber so ausführlich, dass nichts Wichtiges fehlt. Im zweiten Fall diktiert die Pflegekraft die erbrachten Leistungen ins Handy, und eine App trägt die Informationen automatisch in die richtigen Formularfelder ein. Wenn nötig, übersetzt sie es gleich auch noch ins Deutsche, wenn der App-Nutzer sich mit einer anderen Sprache leichter tut.
Zukunftsmusik? Nein, sagen Tobias Müller und Till Schneider. Denn die App, die sie soeben auf den Markt bringen, kann genau das. Memoro heißt sie und wird so beworben: „Nie mehr ein Gespräch vergessen. Memoro erinnert sich für Dich.“
Einen dritten Anwendungsfall haben die Gründer auch schon vorgesehen: Handwerker, die ebenfalls dazu verpflichtet sind, bestimmte Arbeitsschritte zu dokumentieren, von der Elektroinstallation bis zur Feuerstättenschau.
Was die beiden jungen Konstanzer da an den Start bringen, überzeugt auch andere: Soeben haben sie das mit bis zu 100.000 Euro dotierte Mindelsee-Stipendium der Initiative „Unternehmer für Gründer“ (UfG) gewonnen. Zusammen mit weiteren Förderprogrammen schafft das die Basis dafür, Memoro richtig groß machen zu können.
Bereits beim Gründungs- und Ideenwettbewerb „Hack and Harvest“ 2023 hatten Müller und Schneider ihre Pläne vor- und zur Diskussion gestellt. Siegfried Wagner von UfG, sagen die beiden Gründer, habe dann als Mentor enorm geholfen, aus einer Idee eine funktionierende App zu machen.
Memoro nutzt dabei in erster Linie bereits bestehende KI-Technologien und Software-Lösungen, macht sie aber über eine sehr einfache Benutzeroberfläche zugänglich. „Und mit der Datenschutzgrundverordnung ist es natürlich auch kompatibel“, betont Schneider immerhin geht es ja, je nach Anwendungsfall, zum Beispiel um Patienten- oder Kundendaten.
In den nächsten Monaten wollen die Gründer ihre App weiterentwickeln und vor allem Nutzer gewinnen. Denn neben der Idee muss auch das Geschäftsmodell funktionieren. Tobias Müller sagt, man wolle Abonnements oder auch vorausbezahlte Zeitkontingente anbieten. Memoro könne aber auch in andere Umgebungen integriert werden, sodass neben End- auch alle Arten von Geschäftskunden angesprochen werden können.
Ob bald auf vielen Handys von Handwerkern, Pflege- oder Führungskräften Memoro läuft, ist dennoch nicht gewiss. In jeder Gründung steckt auch das Risiko, dass sie vom Markt nicht angenommen wird. Müller und Schneider wissen dabei, wovon sie reden, denn Memoro ist nicht ihr erstes Start-up. Doch der Gewinn des Mindelsee-Stipendiums hat ihre Zuversicht noch verstärkt, betonen sie.
Die beiden Gründer Schneider ist von Haus aus Mediendesigner, Müller Software-Entwickler machen eine einfache Rechnung auf: Eine Pflegekraft müsse im Schnitt acht Stunden pro Woche für die Dokumentation aufwenden. Memoro könnte das auf die Hälfte reduzieren. Wenn von dieser eingesparten Zeit ein guter Teil bei den Patienten ankommt und ein anderer in Form von Benutzungsgebühren bei Memoro, „dann könnte das ein großer Erfolg werden.“

View file

@ -1,426 +0,0 @@
{
"subscriptions": [
{
"id": "free",
"name": "Mana Tropfen",
"nameEn": "Mana Drop",
"nameIt": "Mana Goccia",
"price": 0,
"priceUnit": "",
"initialMana": 150,
"dailyMana": 5,
"maxMana": 150,
"canGiftMana": false,
"popular": false,
"billingCycle": "monthly",
"available": true
},
{
"id": "Mana_Stream_Small_v1",
"name": "Mana Fluss",
"nameEn": "Mana Stream",
"nameIt": "Mana Corrente",
"price": 5.99,
"priceUnit": "/ Monat",
"initialMana": 600,
"dailyMana": 20,
"maxMana": 1200,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": true
},
{
"id": "Mana_Stream_Small_Yearly_v1",
"name": "Mana Fluss",
"nameEn": "Mana Stream",
"nameIt": "Mana Corrente",
"price": 47.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 3,99€ / Monat, 33% Rabatt)",
"initialMana": 600,
"dailyMana": 20,
"maxMana": 1200,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 3.99,
"available": true
},
{
"id": "Mana_Stream_Medium_v1",
"name": "Mana Strom",
"nameEn": "Mana River",
"nameIt": "Mana Fiume",
"price": 14.99,
"priceUnit": "/ Monat",
"initialMana": 1500,
"dailyMana": 50,
"maxMana": 3000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": true
},
{
"id": "Mana_Stream_Medium_Yearly_v1",
"name": "Mana Strom",
"nameEn": "Mana River",
"nameIt": "Mana Fiume",
"price": 119.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 9,99€ / Monat, 33% Rabatt)",
"initialMana": 1500,
"dailyMana": 50,
"maxMana": 3000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 9.99,
"available": true
},
{
"id": "Mana_Stream_Large_v1",
"name": "Mana See",
"nameEn": "Mana Lake",
"nameIt": "Mana Lago",
"price": 29.99,
"priceUnit": "/ Monat",
"initialMana": 3000,
"dailyMana": 100,
"maxMana": 6000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": true
},
{
"id": "Mana_Stream_Large_Yearly_v1",
"name": "Mana See",
"nameEn": "Mana Lake",
"nameIt": "Mana Lago",
"price": 239.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 19,99€ / Monat, 33% Rabatt)",
"initialMana": 3000,
"dailyMana": 100,
"maxMana": 6000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 19.99,
"available": true
},
{
"id": "Mana_Stream_Giant_v1",
"name": "Mana Meer",
"nameEn": "Mana Ocean",
"nameIt": "Mana Mare",
"price": 49.99,
"priceUnit": "/ Monat",
"initialMana": 5000,
"dailyMana": 200,
"maxMana": 10000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": true
},
{
"id": "Mana_Stream_Giant_Yearly_v1",
"name": "Mana Meer",
"nameEn": "Mana Ocean",
"nameIt": "Mana Mare",
"price": 399.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 33,33€ / Monat, 33% Rabatt)",
"initialMana": 5000,
"dailyMana": 200,
"maxMana": 10000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 33.33,
"available": true
}
],
"legacySubscriptions": [
{
"id": "Mini_1m_v1",
"name": "Mini",
"nameEn": "Mini",
"nameIt": "Mini",
"price": 2.99,
"priceUnit": "/ Monat",
"initialMana": 600,
"dailyMana": 20,
"maxMana": 1200,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "Plus_7E_1m_v1",
"name": "Plus",
"nameEn": "Plus",
"nameIt": "Plus",
"price": 7.99,
"priceUnit": "/ Monat",
"initialMana": 1500,
"dailyMana": 50,
"maxMana": 3000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "Pro_23E_1m_v1",
"name": "Pro",
"nameEn": "Pro",
"nameIt": "Pro",
"price": 23.99,
"priceUnit": "/ Monat",
"initialMana": 3000,
"dailyMana": 100,
"maxMana": 6000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "Ultra_47E_1m_v1",
"name": "Ultra",
"nameEn": "Ultra",
"nameIt": "Ultra",
"price": 47.99,
"priceUnit": "/ Monat",
"initialMana": 5000,
"dailyMana": 200,
"maxMana": 10000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "Mini_1y_v1",
"name": "Mini",
"nameEn": "Mini",
"nameIt": "Mini",
"price": 29.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 2,50€ / Monat)",
"initialMana": 600,
"dailyMana": 20,
"maxMana": 1200,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 2.50,
"available": false,
"isLegacy": true
},
{
"id": "Plus_70E_1y_v1",
"name": "Plus",
"nameEn": "Plus",
"nameIt": "Plus",
"price": 79.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 6,67€ / Monat)",
"initialMana": 1500,
"dailyMana": 50,
"maxMana": 3000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 6.67,
"available": false,
"isLegacy": true
},
{
"id": "Pro_230E_1y_v1",
"name": "Pro",
"nameEn": "Pro",
"nameIt": "Pro",
"price": 239.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 20,00€ / Monat)",
"initialMana": 3000,
"dailyMana": 100,
"maxMana": 6000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 20.00,
"available": false,
"isLegacy": true
},
{
"id": "Ultra_470E_1y_v1",
"name": "Ultra",
"nameEn": "Ultra",
"nameIt": "Ultra",
"price": 479.99,
"priceUnit": "/ Jahr",
"priceBreakdown": "(entspricht 40,00€ / Monat)",
"initialMana": 5000,
"dailyMana": 200,
"maxMana": 10000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 40.00,
"available": false,
"isLegacy": true
},
{
"id": "rc_plus_monthly_8e_play:plus-monthly-8e-autorenewing",
"name": "Plus (Android)",
"nameEn": "Plus (Android)",
"nameIt": "Plus (Android)",
"price": 7.99,
"priceUnit": "/ month",
"initialMana": 1500,
"dailyMana": 50,
"maxMana": 3000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "rc_pro_monthly_23e_play:rc-pro-monthly-8e-play-renewel",
"name": "Pro (Android)",
"nameEn": "Pro (Android)",
"nameIt": "Pro (Android)",
"price": 23.99,
"priceUnit": "/ month",
"initialMana": 3000,
"dailyMana": 100,
"maxMana": 6000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "rc_ultra_monthly_play:rc-ultra-monthly-play",
"name": "Ultra (Android)",
"nameEn": "Ultra (Android)",
"nameIt": "Ultra (Android)",
"price": 47.99,
"priceUnit": "/ month",
"initialMana": 5000,
"dailyMana": 200,
"maxMana": 10000,
"canGiftMana": true,
"popular": false,
"billingCycle": "monthly",
"available": false,
"isLegacy": true
},
{
"id": "rc_plus_yearly_80e_play:rc-plus-yearly-80e-play-renewel",
"name": "Plus (Android)",
"nameEn": "Plus (Android)",
"nameIt": "Plus (Android)",
"price": 79.99,
"priceUnit": "/ year",
"priceBreakdown": "(equals 6.67€ / month)",
"initialMana": 1500,
"dailyMana": 50,
"maxMana": 3000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 6.67,
"available": false,
"isLegacy": true
},
{
"id": "rc_pro_yearly_229e_play:rc-pro-yearly-229e-play-renewel",
"name": "Pro (Android)",
"nameEn": "Pro (Android)",
"nameIt": "Pro (Android)",
"price": 239.99,
"priceUnit": "/ year",
"priceBreakdown": "(equals 20.00€ / month)",
"initialMana": 3000,
"dailyMana": 100,
"maxMana": 6000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 20.00,
"available": false,
"isLegacy": true
},
{
"id": "rc_ultra_yearly_play:rc-ultra-yearly-play",
"name": "Ultra (Android)",
"nameEn": "Ultra (Android)",
"nameIt": "Ultra (Android)",
"price": 479.99,
"priceUnit": "/ year",
"priceBreakdown": "(equals 40.00€ / month)",
"initialMana": 5000,
"dailyMana": 200,
"maxMana": 10000,
"canGiftMana": true,
"popular": false,
"billingCycle": "yearly",
"monthlyEquivalent": 40.00,
"available": false,
"isLegacy": true
}
],
"packages": [
{
"id": "Mana_Potion_Small_v1",
"name": "Kleiner Mana Trank",
"nameEn": "Small Mana Potion",
"nameIt": "Piccola Pozione di Mana",
"manaAmount": 350,
"price": 4.99,
"popular": false
},
{
"id": "Mana_Potion_Medium_v1",
"name": "Mittlerer Mana Trank",
"nameEn": "Medium Mana Potion",
"nameIt": "Media Pozione di Mana",
"manaAmount": 700,
"price": 9.99,
"popular": false
},
{
"id": "Mana_Potion_Large_v1",
"name": "Großer Mana Trank",
"nameEn": "Large Mana Potion",
"nameIt": "Grande Pozione di Mana",
"manaAmount": 1400,
"price": 19.99,
"popular": false
},
{
"id": "Mana_Potion_Giant_v2",
"name": "Riesiger Mana Trank",
"nameEn": "Giant Mana Potion",
"nameIt": "Pozione di Mana Gigante",
"manaAmount": 2800,
"price": 39.99,
"popular": false
}
]
}

View file

@ -1,109 +0,0 @@
# Sort Order Implementation für Blueprint Prompts
## Übersicht
Diese Dokumentation beschreibt die Implementierung der `sort_order` Funktionalität für die Reihenfolge von Memories, die durch Blueprints generiert werden.
## Problem
Die Memories wurden in zufälliger oder unerwünschter Reihenfolge angezeigt. Die Kurzzusammenfassung erschien als letztes, obwohl sie als Übersicht an erster Stelle stehen sollte.
## Lösung
Hinzufügen einer `sort_order` Spalte zur `prompt_blueprints` Tabelle, die explizit die Anzeigereihenfolge der generierten Memories steuert.
## Technische Details
### Neue Spalte
```sql
ALTER TABLE prompt_blueprints
ADD COLUMN sort_order INTEGER DEFAULT 999;
```
- **Typ**: INTEGER
- **Default**: 999 (für nicht explizit sortierte Einträge)
- **Logik**: Niedrigere Zahlen werden zuerst angezeigt
### Standard Blueprint Sortierung
| Position | Memory Typ | sort_order | Begründung |
|----------|------------|------------|------------|
| 1 | Kurzzusammenfassung | 1 | Schneller Überblick für den Nutzer |
| 2 | Aufgaben | 2 | Konkrete Action Items |
| 3 | Ausführliche Zusammenfassung | 3 | Detaillierte Informationen bei Bedarf |
## Backend Implementierung
### Query für Memory-Generierung
```sql
SELECT pb.prompt_id, p.prompt_text, p.memory_title
FROM prompt_blueprints pb
JOIN prompts p ON pb.prompt_id = p.id
WHERE pb.blueprint_id = $1
ORDER BY pb.sort_order ASC;
```
### Edge Function Anpassung
Die Edge Functions müssen die Memories in der durch `sort_order` definierten Reihenfolge zurückgeben:
```javascript
// In der blueprint Edge Function
const promptsQuery = await supabaseClient
.from('prompt_blueprints')
.select(`
prompt_id,
sort_order,
prompts!inner(
id,
prompt_text,
memory_title
)
`)
.eq('blueprint_id', blueprintId)
.order('sort_order', { ascending: true });
```
## Frontend Implementierung
### Memory Display Component
```typescript
// Die Memories sollten bereits in der richtigen Reihenfolge vom Backend kommen
// Falls nicht, kann im Frontend nachsortiert werden:
const sortedMemories = memories.sort((a, b) => {
// Nutze sort_order falls vorhanden
if (a.sort_order && b.sort_order) {
return a.sort_order - b.sort_order;
}
// Fallback auf created_at
return new Date(a.created_at) - new Date(b.created_at);
});
```
## Migration für bestehende Daten
Alle bestehenden Blueprint-Prompt Verknüpfungen erhalten automatisch `sort_order = 999`. Die wichtigsten Blueprints wurden explizit gesetzt:
- **Standard-Analyse**: Sortierung 1-2-3
- **Meeting-Analyse**: Sortierung 1-2-3
- **Feedback**: Sortierung 1-2-3
## Vorteile dieser Lösung
1. **Explizite Kontrolle**: Vollständige Kontrolle über die Anzeigereihenfolge
2. **Flexibilität**: Jeder Blueprint kann seine eigene Sortierung haben
3. **Erweiterbarkeit**: Neue Prompts können einfach eingeordnet werden
4. **Performance**: Index auf (blueprint_id, sort_order) für schnelle Queries
5. **Rückwärtskompatibilität**: Default-Wert 999 für nicht explizit sortierte Einträge
## Testing
Nach der Migration sollte getestet werden:
1. ✅ Neue Memo-Aufnahme mit Standard Blueprint erstellen
2. ✅ Überprüfen ob Kurzzusammenfassung oben erscheint
3. ✅ Überprüfen ob Reihenfolge konsistent bleibt
4. ✅ Andere Blueprints testen (Meeting, Feedback)
## Zukünftige Erweiterungen
- UI für Blueprint-Editor könnte Drag & Drop für Sortierung bieten
- Nutzer-spezifische Sortierung pro Blueprint möglich
- Conditional Sorting basierend auf Memo-Typ oder -Länge

View file

@ -1,20 +0,0 @@
# Aufgaben & Termine Prompt
## Prompt ID
`7a6cac9a-5a34-4fe5-a8f6-23f8165b0e48`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Bitte lies das folgende Transkript durch und extrahiere: 1) AUFGABEN: Alle erwähnten Aufgaben, Aktionspunkte und nächsten Schritte. 2) TERMINE: Alle genannten Termine, Meetings, Deadlines und zeitlichen Vereinbarungen. Ordne beide Kategorien nach Dringlichkeit (hoch/mittel/niedrig). Erfasse für Aufgaben: Beschreibung, verantwortliche Person (falls genannt), Deadline, Kontext und erwartete Ergebnisse. Erfasse für Termine: Datum/Uhrzeit, Art des Termins, Teilnehmer (falls genannt), Ort/Format (falls erwähnt) und Zweck. Markiere unklare Punkte mit [UNKLAR]. Verwende eine strukturierte Listenform ohne zusätzliche Kommentare. Hier das Transkript:", "en": "Please read through the following transcript and extract: 1) TASKS: All mentioned tasks, action items, and next steps. 2) APPOINTMENTS: All mentioned appointments, meetings, deadlines and time agreements. Order both categories by urgency (high/medium/low). For tasks capture: description, responsible person (if mentioned), deadline, context and expected results. For appointments capture: date/time, type of appointment, participants (if mentioned), location/format (if mentioned) and purpose. Mark unclear points with [UNCLEAR]. Use a structured list format without additional comments. Here is the transcript:", "it": "Per favore leggi la seguente trascrizione ed estrai: 1) COMPITI: Tutti i compiti menzionati, punti d'azione e prossimi passi. 2) APPUNTAMENTI: Tutti gli appuntamenti menzionati, riunioni, scadenze e accordi temporali. Ordina entrambe le categorie per urgenza (alta/media/bassa). Per i compiti cattura: descrizione, persona responsabile (se menzionata), scadenza, contesto e risultati attesi. Per gli appuntamenti cattura: data/ora, tipo di appuntamento, partecipanti (se menzionati), luogo/formato (se menzionato) e scopo. Contrassegna i punti poco chiari con [NON CHIARO]. Usa un formato di lista strutturato senza commenti aggiuntivi. Ecco la trascrizione:", "fr": "Veuillez lire la transcription suivante et extraire : 1) TÂCHES : Toutes les tâches mentionnées, points d'action et prochaines étapes. 2) RENDEZ-VOUS : Tous les rendez-vous mentionnés, réunions, échéances et accords temporels. Classez les deux catégories par urgence (élevée/moyenne/faible). Pour les tâches, capturez : description, personne responsable (si mentionnée), échéance, contexte et résultats attendus. Pour les rendez-vous, capturez : date/heure, type de rendez-vous, participants (si mentionnés), lieu/format (si mentionné) et objectif. Marquez les points peu clairs avec [PEU CLAIR]. Utilisez un format de liste structuré sans commentaires supplémentaires. Voici la transcription :", "es": "Por favor, lee la siguiente transcripción y extrae: 1) TAREAS: Todas las tareas mencionadas, puntos de acción y próximos pasos. 2) CITAS: Todas las citas mencionadas, reuniones, plazos y acuerdos temporales. Ordena ambas categorías por urgencia (alta/media/baja). Para las tareas captura: descripción, persona responsable (si se menciona), fecha límite, contexto y resultados esperados. Para las citas captura: fecha/hora, tipo de cita, participantes (si se mencionan), lugar/formato (si se menciona) y propósito. Marca los puntos poco claros con [POCO CLARO]. Usa un formato de lista estructurado sin comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Aufgaben & Termine", "en": "Tasks & Appointments", "it": "Compiti e Appuntamenti", "fr": "Tâches et Rendez-vous", "es": "Tareas y Citas"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Extrahiert alle im Gespräch erwähnten Aufgaben, Aktionspunkte, nächsten Schritte sowie Termine, Meetings und zeitliche Vereinbarungen. Erfasst Details wie Kontext, Verantwortlichkeiten, Deadlines und Teilnehmer.", "en": "Extracts all tasks, action items, next steps as well as appointments, meetings and time agreements mentioned in the conversation. Captures details such as context, responsibilities, deadlines and participants.", "it": "Estrae tutti i compiti, punti d'azione, prossimi passi così come appuntamenti, riunioni e accordi temporali menzionati nella conversazione. Cattura dettagli come contesto, responsabilità, scadenze e partecipanti.", "fr": "Extrait toutes les tâches, points d'action, prochaines étapes ainsi que les rendez-vous, réunions et accords temporels mentionnés dans la conversation. Capture des détails tels que le contexte, les responsabilités, les échéances et les participants.", "es": "Extrae todas las tareas, puntos de acción, próximos pasos, así como citas, reuniones y acuerdos temporales mencionados en la conversación. Captura detalles como contexto, responsabilidades, plazos y participantes."}
```

View file

@ -1,19 +0,0 @@
# Ausführliche Zusammenfassung Prompt
## Prompt ID
`4370cb68-d676-4b93-8afd-2fb7c4ad78c4`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Erstelle eine ausführliche Zusammenfassung des folgenden Transkripts (ca. 30-50% der Originallänge). Strukturiere die Zusammenfassung wie folgt: 1. Überblick (Kernthema in 2-3 Sätzen), 2. Hauptthemen (die wichtigsten diskutierten Punkte), 3. Details (vorgebrachte Argumente und relevante Beispiele), 4. Fazit (zentrale Erkenntnisse). Verzichte auf zusätzliche Kommentare und fokussiere dich auf die reine Inhaltswiedergabe. Hier das Transkript:", "en": "Create a detailed summary of the following transcript (approx. 30-50% of the original length). Structure the summary as follows: 1. Overview (core topic in 2-3 sentences), 2. Main topics (the most important points discussed), 3. Details (arguments presented and relevant examples), 4. Conclusion (key insights). Avoid additional comments and focus on pure content reproduction. Here is the transcript:", "it": "Crea un riassunto dettagliato della seguente trascrizione (circa 30-50% della lunghezza originale). Struttura il riassunto come segue: 1. Panoramica (tema centrale in 2-3 frasi), 2. Temi principali (i punti più importanti discussi), 3. Dettagli (argomenti presentati ed esempi rilevanti), 4. Conclusione (intuizioni chiave). Evita commenti aggiuntivi e concentrati sulla pura riproduzione del contenuto. Ecco la trascrizione:", "fr": "Créez un résumé détaillé de la transcription suivante (environ 30-50% de la longueur originale). Structurez le résumé comme suit : 1. Aperçu (thème central en 2-3 phrases), 2. Thèmes principaux (les points les plus importants discutés), 3. Détails (arguments présentés et exemples pertinents), 4. Conclusion (idées clés). Évitez les commentaires supplémentaires et concentrez-vous sur la pure reproduction du contenu. Voici la transcription :", "es": "Crea un resumen detallado de la siguiente transcripción (aprox. 30-50% de la longitud original). Estructura el resumen de la siguiente manera: 1. Visión general (tema central en 2-3 oraciones), 2. Temas principales (los puntos más importantes discutidos), 3. Detalles (argumentos presentados y ejemplos relevantes), 4. Conclusión (ideas clave). Evita comentarios adicionales y concéntrate en la pura reproducción del contenido. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Ausführliche Zusammenfassung", "en": "Detailed Summary", "it": "Riassunto Dettagliato", "fr": "Résumé Détaillé", "es": "Resumen Detallado"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Gibt eine detailliertere Wiedergabe der Inhalte, inklusive wichtiger Argumente, Beispiele und Kontextinformationen. Geeignet für ein tiefergehendes Verständnis ohne das gesamte Transkript lesen zu müssen.", "en": "Provides a more detailed reproduction of the content, including important arguments, examples and contextual information. Suitable for a deeper understanding without having to read the entire transcript.", "it": "Fornisce una riproduzione più dettagliata del contenuto, inclusi argomenti importanti, esempi e informazioni contestuali. Adatto per una comprensione più profonda senza dover leggere l'intera trascrizione.", "fr": "Fournit une reproduction plus détaillée du contenu, incluant des arguments importants, des exemples et des informations contextuelles. Convient pour une compréhension approfondie sans avoir à lire l'intégralité de la transcription.", "es": "Proporciona una reproducción más detallada del contenido, incluyendo argumentos importantes, ejemplos e información contextual. Adecuado para una comprensión más profunda sin tener que leer toda la transcripción."}
```

View file

@ -1,19 +0,0 @@
# Beantwortete Fragen & Antworten Prompt
## Prompt ID
`47ce3340-e8c6-437c-928d-854c55589491`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Identifiziere alle Fragen aus dem folgenden Transkript, die gestellt und beantwortet wurden. Verwende folgendes nummeriertes Format: 1. FRAGE: [Originalfrage mit relevantem Kontext] ANTWORT: [Vollständige Antwort] STATUS: [Direkt beantwortet/Indirekt beantwortet/Teilweise beantwortet]. Gib bei jeder Frage-Antwort-Kombination den relevanten Kontext mit an. Verzichte auf zusätzliche Kommentare. Hier das Transkript:", "en": "Identify all questions from the following transcript that were asked and answered. Use the following numbered format: 1. QUESTION: [Original question with relevant context] ANSWER: [Complete answer] STATUS: [Directly answered/Indirectly answered/Partially answered]. Include relevant context for each question-answer combination. Avoid additional comments. Here is the transcript:", "it": "Identifica tutte le domande dalla seguente trascrizione che sono state poste e hanno ricevuto risposta. Usa il seguente formato numerato: 1. DOMANDA: [Domanda originale con contesto rilevante] RISPOSTA: [Risposta completa] STATO: [Risposta diretta/Risposta indiretta/Risposta parziale]. Includi il contesto rilevante per ogni combinazione domanda-risposta. Evita commenti aggiuntivi. Ecco la trascrizione:", "fr": "Identifiez toutes les questions de la transcription suivante qui ont été posées et ont reçu une réponse. Utilisez le format numéroté suivant : 1. QUESTION : [Question originale avec contexte pertinent] RÉPONSE : [Réponse complète] STATUT : [Réponse directe/Réponse indirecte/Réponse partielle]. Incluez le contexte pertinent pour chaque combinaison question-réponse. Évitez les commentaires supplémentaires. Voici la transcription :", "es": "Identifica todas las preguntas de la siguiente transcripción que fueron formuladas y respondidas. Usa el siguiente formato numerado: 1. PREGUNTA: [Pregunta original con contexto relevante] RESPUESTA: [Respuesta completa] ESTADO: [Respuesta directa/Respuesta indirecta/Respuesta parcial]. Incluye el contexto relevante para cada combinación pregunta-respuesta. Evita comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Beantwortete Fragen & Antworten", "en": "Answered Questions & Answers", "it": "Domande e Risposte", "fr": "Questions et Réponses", "es": "Preguntas y Respuestas"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Extrahiert Fragen, die im Transkript gestellt und beantwortet wurden, und listet sie zusammen mit ihren jeweiligen Antworten auf.", "en": "Extracts questions that were asked and answered in the transcript, and lists them together with their respective answers.", "it": "Estrae le domande che sono state poste e hanno ricevuto risposta nella trascrizione, e le elenca insieme alle rispettive risposte.", "fr": "Extrait les questions qui ont été posées et ont reçu une réponse dans la transcription, et les liste avec leurs réponses respectives.", "es": "Extrae las preguntas que fueron formuladas y respondidas en la transcripción, y las enumera junto con sus respectivas respuestas."}
```

View file

@ -1,19 +0,0 @@
# Blogbeitrag Prompt
## Prompt ID
`2c6a6e47-1d0c-441f-9449-b5d908bffba2`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Verwandle das folgende Transkript in einen gut strukturierten Blogbeitrag (800-1200 Wörter) für eine professionelle Zielgruppe. Der Beitrag sollte die Hauptthemen und wichtigsten Erkenntnisse detailliert behandeln. Formuliere eine SEO-optimierte Hauptüberschrift, verwende aussagekräftige Zwischenüberschriften zur Gliederung, schreibe eine einleitende Zusammenfassung (2-3 Sätze) sowie einen Schlussteil mit konkretem Handlungsaufruf. Integriere relevante Keywords natürlich im Text. Der Stil sollte informativ, lesefreundlich und ohne zusätzliche Kommentare sein. Hier das Transkript:", "en": "Transform the following transcript into a well-structured blog post (800-1200 words) for a professional audience. The post should cover the main topics and key insights in detail. Formulate an SEO-optimized main heading, use meaningful subheadings for structure, write an introductory summary (2-3 sentences) as well as a conclusion with a concrete call to action. Integrate relevant keywords naturally in the text. The style should be informative, reader-friendly and without additional comments. Here is the transcript:", "it": "Trasforma la seguente trascrizione in un post di blog ben strutturato (800-1200 parole) per un pubblico professionale. Il post dovrebbe coprire in dettaglio i temi principali e le intuizioni chiave. Formula un titolo principale ottimizzato per SEO, usa sottotitoli significativi per la struttura, scrivi un riassunto introduttivo (2-3 frasi) e una conclusione con un chiaro invito all'azione. Integra naturalmente le parole chiave rilevanti nel testo. Lo stile dovrebbe essere informativo, di facile lettura e senza commenti aggiuntivi. Ecco la trascrizione:", "fr": "Transformez la transcription suivante en un article de blog bien structuré (800-1200 mots) pour un public professionnel. L'article devrait couvrir en détail les thèmes principaux et les idées clés. Formulez un titre principal optimisé pour le SEO, utilisez des sous-titres significatifs pour la structure, écrivez un résumé introductif (2-3 phrases) ainsi qu'une conclusion avec un appel à l'action concret. Intégrez naturellement les mots-clés pertinents dans le texte. Le style doit être informatif, facile à lire et sans commentaires supplémentaires. Voici la transcription :", "es": "Transforma la siguiente transcripción en una publicación de blog bien estructurada (800-1200 palabras) para una audiencia profesional. La publicación debe cubrir en detalle los temas principales y las ideas clave. Formula un título principal optimizado para SEO, usa subtítulos significativos para la estructura, escribe un resumen introductorio (2-3 oraciones) así como una conclusión con una llamada a la acción concreta. Integra naturalmente las palabras clave relevantes en el texto. El estilo debe ser informativo, fácil de leer y sin comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Blogbeitrag", "en": "Blog Post", "it": "Post del Blog", "fr": "Article de Blog", "es": "Publicación de Blog"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Erstellt einen ausführlichen Blogartikel basierend auf den Inhalten des Transkripts. Der Artikel soll gut strukturiert sein, eine einleitende Überschrift, Zwischenüberschriften und einen abschliessenden Absatz enthalten.", "en": "Creates a detailed blog article based on the contents of the transcript. The article should be well structured, with an introductory heading, subheadings and a concluding paragraph.", "it": "Crea un articolo di blog dettagliato basato sui contenuti della trascrizione. L'articolo dovrebbe essere ben strutturato, con un titolo introduttivo, sottotitoli e un paragrafo conclusivo.", "fr": "Crée un article de blog détaillé basé sur le contenu de la transcription. L'article doit être bien structuré, avec un titre introductif, des sous-titres et un paragraphe de conclusion.", "es": "Crea un artículo de blog detallado basado en el contenido de la transcripción. El artículo debe estar bien estructurado, con un título introductorio, subtítulos y un párrafo de conclusión."}
```

View file

@ -1,149 +0,0 @@
# Edge Functions Update für sort_order
## Betroffene Edge Functions:
Die folgenden Edge Functions müssen angepasst werden, um die `sort_order` aus der `prompt_blueprints` Tabelle an die neu erstellten Memories zu übergeben:
### 1. `/supabase/functions/blueprint/index.ts`
**Aktuelle Implementation (ca. Zeile 410):**
```typescript
const { data: newMemory, error: newMemoryError } = await memoro_sb.from('memories').insert({
memo_id: memo_id,
title: memoryTitle || 'Blueprint-Antwort',
content: answer,
metadata: { ... }
}).select().single();
```
**Neue Implementation:**
```typescript
// Zuerst die sort_order aus prompt_blueprints holen
const { data: promptBlueprint } = await memoro_sb
.from('prompt_blueprints')
.select('sort_order')
.eq('blueprint_id', blueprint_id)
.eq('prompt_id', prompt.id)
.single();
const { data: newMemory, error: newMemoryError } = await memoro_sb.from('memories').insert({
memo_id: memo_id,
title: memoryTitle || 'Blueprint-Antwort',
content: answer,
sort_order: promptBlueprint?.sort_order || 999, // Neue Zeile!
metadata: { ... }
}).select().single();
```
### 2. `/supabase/functions/auto-blueprint/index.ts`
**Ähnliche Anpassung bei Zeile 479:**
```typescript
const { data: newMemory, error: newMemoryError } = await memoro_sb.from('memories').insert({
memo_id: memo_id,
title: memoryTitle || 'Auto-Blueprint-Antwort',
content: answer,
sort_order: promptBlueprint?.sort_order || 999, // Neue Zeile!
metadata: { ... }
}).select().single();
```
### 3. Andere Edge Functions (ohne Blueprint)
Für Edge Functions die Memories ohne Blueprint erstellen, sollte ein sinnvoller Default-Wert gesetzt werden:
#### `/supabase/functions/create-memory/index.ts`
```typescript
const { data: newMemory, error: newMemoryError } = await memoro_sb.from('memories').insert({
memo_id: memo_id,
title: memoryTitle || 'Memory',
content: answer,
sort_order: 100, // Default für manuelle Memories
metadata: { ... }
}).select().single();
```
#### `/supabase/functions/question-memo/index.ts`
```typescript
const { data: newMemory, error: newMemoryError } = await memoro_sb.from('memories').insert({
memo_id: memo_id,
title: `Frage: ${question.substring(0, 50)}...`,
content: answer,
sort_order: 200, // Fragen erscheinen nach Blueprint-Memories
metadata: { ... }
}).select().single();
```
#### `/supabase/functions/combine-memos/index.ts`
```typescript
const { error: memoryError } = await memoro_sb
.from('memories')
.insert({
...memoryData,
sort_order: 300 // Kombinierte Memos erscheinen am Ende
});
```
#### `/supabase/functions/translate/index.ts`
```typescript
const { error: memoryCreateError } = await memoro_sb.from('memories').insert({
memo_id: newMemoId,
title: translatedTitle,
content: translatedContent,
sort_order: memory.sort_order || 999, // Behalte sort_order vom Original
metadata: { ... }
});
```
## Optimierte Blueprint Function Implementation
Für bessere Performance sollten die Prompts mit ihrer sort_order gleich am Anfang geladen werden:
```typescript
// In blueprint/index.ts und auto-blueprint/index.ts
// Lade alle Prompts mit sort_order für diesen Blueprint
const { data: blueprintPrompts, error: blueprintPromptsError } = await memoro_sb
.from('prompt_blueprints')
.select(`
prompt_id,
sort_order,
prompts!inner(
id,
prompt_text,
memory_title
)
`)
.eq('blueprint_id', blueprint_id)
.order('sort_order', { ascending: true });
// Dann beim Erstellen der Memories:
for (const blueprintPrompt of blueprintPrompts) {
// ... AI Processing ...
const { data: newMemory, error: newMemoryError } = await memoro_sb.from('memories').insert({
memo_id: memo_id,
title: memoryTitle,
content: answer,
sort_order: blueprintPrompt.sort_order,
metadata: { ... }
}).select().single();
}
```
## Sort Order Konvention:
| Bereich | sort_order | Verwendung |
|---------|------------|------------|
| 1-99 | Blueprint Memories | Hauptanalysen (Zusammenfassung, Aufgaben, etc.) |
| 100-199 | Manuelle Memories | Vom Nutzer erstellte Memories |
| 200-299 | Frage-Antwort | Memories aus Fragen an Memos |
| 300-399 | Kombinierte Memos | Aus mehreren Memos generiert |
| 999 | Default | Fallback für nicht kategorisierte Memories |
## Testing nach Implementation:
1. Neue Aufnahme mit Standard Blueprint erstellen
2. Prüfen ob Memories in richtiger Reihenfolge erscheinen
3. Manuelle Memory hinzufügen - sollte nach Blueprint-Memories erscheinen
4. Frage an Memo stellen - sollte nach manuellen Memories erscheinen

View file

@ -1,133 +0,0 @@
# Frontend Patches für Memory Sorting
## Dateien die angepasst werden müssen:
### 1. `/features/memos/hooks/useMemoState.ts`
**Aktuelle Zeile 281:**
```typescript
.order('created_at', { ascending: false })
```
**Ändern zu:**
```typescript
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false })
```
### 2. `/features/memos/hooks/useOptimizedMemoData.ts`
**Aktuelle Zeile 95:**
```typescript
.order('created_at', { ascending: false })
```
**Ändern zu:**
```typescript
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false })
```
### 3. `/app/(protected)/(memo)/actions/memoActions.ts`
**Aktuelle Zeile 468:**
```typescript
.eq('memo_id', memoId)
.order('created_at', { ascending: false });
```
**Ändern zu:**
```typescript
.eq('memo_id', memoId)
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false });
```
### 4. `/app/(protected)/memories.tsx`
**Aktuelle Zeile 157:**
```typescript
.order('created_at', { ascending: false });
```
**Ändern zu:**
```typescript
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false });
```
### 5. `/app/(protected)/(tabs)/index.tsx`
**Aktuelle Zeile 999:**
```typescript
.eq('memo_id', memo.id)
.order('created_at', { ascending: false });
```
**Ändern zu:**
```typescript
.eq('memo_id', memo.id)
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false });
```
### 6. `/app/(protected)/(tabs)/memos.tsx`
**Aktuelle Zeile 603:**
```typescript
.eq('memo_id', memo.id)
.order('created_at', { ascending: false });
```
**Ändern zu:**
```typescript
.eq('memo_id', memo.id)
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false });
```
### 7. `/components/molecules/MemoList.tsx`
**Aktuelle Zeile 550:**
```typescript
.eq('memo_id', memo.id)
.order('created_at', { ascending: false });
```
**Ändern zu:**
```typescript
.eq('memo_id', memo.id)
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false });
```
### 8. `/components/molecules/MemoPreview.tsx`
**Aktuelle Zeile 306:**
```typescript
.eq('memo_id', memo.id)
.order('created_at', { ascending: false });
```
**Ändern zu:**
```typescript
.eq('memo_id', memo.id)
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false });
```
## Erklärung der Änderung:
Die doppelte Sortierung funktioniert so:
1. **Primär nach `sort_order` (aufsteigend)**: Memories mit niedrigerer sort_order erscheinen zuerst
2. **Sekundär nach `created_at` (absteigend)**: Falls mehrere Memories die gleiche sort_order haben (z.B. 999 für alte/manuelle Memories), werden sie nach Erstellungsdatum sortiert
## Testing:
Nach den Änderungen sollte getestet werden:
1. ✅ Neue Memo-Aufnahme mit Standard Blueprint
2. ✅ Überprüfen ob Reihenfolge stimmt:
- Kurzzusammenfassung (sort_order: 1) - OBEN
- Aufgaben (sort_order: 2) - MITTE
- Ausführliche Zusammenfassung (sort_order: 3) - UNTEN
3. ✅ Alte Memories ohne sort_order sollten weiterhin funktionieren

View file

@ -1,19 +0,0 @@
# Gesammelte Ideen & Vorschläge Prompt
## Prompt ID
`8cdc89a5-2f76-4d50-a93d-0c177c3e73ab`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Sammle alle im folgenden Transkript geäusserten neuen Ideen, kreativen Vorschläge oder Brainstorming-Punkte. Gruppiere sie nach Themenbereichen und markiere besonders innovative Ideen mit ⭐. Kategorisiere jede Idee nach Umsetzbarkeit: [KURZFRISTIG] (innerhalb 1 Monat), [MITTELFRISTIG] (1-6 Monate), [LANGFRISTIG] (über 6 Monate). Liste sie übersichtlich ohne zusätzliche Kommentare auf. Hier das Transkript:", "en": "Collect all new ideas, creative suggestions, or brainstorming points expressed in the following transcript. Group them by topic areas and mark particularly innovative ideas with ⭐. Categorize each idea by feasibility: [SHORT-TERM] (within 1 month), [MEDIUM-TERM] (1-6 months), [LONG-TERM] (over 6 months). List them clearly without additional comments. Here is the transcript:", "it": "Raccogli tutte le nuove idee, suggerimenti creativi o punti di brainstorming espressi nella seguente trascrizione. Raggruppali per aree tematiche e contrassegna le idee particolarmente innovative con ⭐. Categorizza ogni idea per fattibilità: [BREVE TERMINE] (entro 1 mese), [MEDIO TERMINE] (1-6 mesi), [LUNGO TERMINE] (oltre 6 mesi). Elencali chiaramente senza commenti aggiuntivi. Ecco la trascrizione:", "fr": "Collectez toutes les nouvelles idées, suggestions créatives ou points de brainstorming exprimés dans la transcription suivante. Regroupez-les par domaines thématiques et marquez les idées particulièrement innovantes avec ⭐. Catégorisez chaque idée selon sa faisabilité : [COURT TERME] (dans 1 mois), [MOYEN TERME] (1-6 mois), [LONG TERME] (plus de 6 mois). Listez-les clairement sans commentaires supplémentaires. Voici la transcription :", "es": "Recopila todas las nuevas ideas, sugerencias creativas o puntos de lluvia de ideas expresados en la siguiente transcripción. Agrúpalas por áreas temáticas y marca las ideas particularmente innovadoras con ⭐. Categoriza cada idea por viabilidad: [CORTO PLAZO] (dentro de 1 mes), [MEDIO PLAZO] (1-6 meses), [LARGO PLAZO] (más de 6 meses). Listálas claramente sin comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Gesammelte Ideen & Vorschläge", "en": "Collected Ideas & Suggestions", "it": "Idee e Suggerimenti Raccolti", "fr": "Idées et Suggestions Collectées", "es": "Ideas y Sugerencias Recopiladas"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Listet alle im Gespräch geäusserten neuen Ideen, Brainstorming-Punkte oder konkreten Vorschläge auf.", "en": "Lists all new ideas, brainstorming points or concrete suggestions expressed in the conversation.", "it": "Elenca tutte le nuove idee, punti di brainstorming o suggerimenti concreti espressi nella conversazione.", "fr": "Liste toutes les nouvelles idées, points de brainstorming ou suggestions concrètes exprimés dans la conversation.", "es": "Enumera todas las nuevas ideas, puntos de lluvia de ideas o sugerencias concretas expresadas en la conversación."}
```

View file

@ -1,19 +0,0 @@
# Kurzzusammenfassung Prompt
## Prompt ID
`c4009bef-4504-4af7-86f5-f896a2412a0a`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Erstelle ein TLDR (Too Long; Didn't Read) des folgenden Transkripts. Fasse die maximal 3 wichtigsten handlungsrelevanten Kernaussagen in insgesamt 3-5 Sätzen zusammen. Fokussiere ausschließlich auf die absoluten Hauptpunkte, die für Entscheidungen oder nächste Schritte relevant sind. Verzichte auf zusätzliche Kommentare. Hier das Transkript:", "en": "Create a TLDR (Too Long; Didn't Read) of the following transcript. Summarize the maximum 3 most important action-relevant key messages in a total of 3-5 sentences. Focus exclusively on the absolute main points that are relevant for decisions or next steps. Avoid additional comments. Here is the transcript:", "it": "Crea un TLDR (Troppo Lungo; Non L'ho Letto) della seguente trascrizione. Riassumi i massimo 3 messaggi chiave più importanti rilevanti per l'azione in totale 3-5 frasi. Concentrati esclusivamente sui punti principali assoluti che sono rilevanti per decisioni o passi successivi. Evita commenti aggiuntivi. Ecco la trascrizione:", "fr": "Créez un TLDR (Trop Long ; Pas Lu) de la transcription suivante. Résumez les 3 messages clés maximum les plus importants et pertinents pour l'action en 3-5 phrases au total. Concentrez-vous exclusivement sur les points principaux absolus qui sont pertinents pour les décisions ou les prochaines étapes. Évitez les commentaires supplémentaires. Voici la transcription :", "es": "Crea un TLDR (Demasiado Largo; No Lo Leí) de la siguiente transcripción. Resume los máximo 3 mensajes clave más importantes y relevantes para la acción en un total de 3-5 oraciones. Concéntrate exclusivamente en los puntos principales absolutos que son relevantes para decisiones o próximos pasos. Evita comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Kurzzusammenfassung", "en": "Executive Summary", "it": "Riassunto Esecutivo", "fr": "Résumé Exécutif", "es": "Resumen Ejecutivo"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Erstellt eine knappe Übersicht der wichtigsten Inhalte und Kernaussagen des Gesprächs oder Vortrags. Ideal für einen schnellen Überblick.", "en": "Creates a brief overview of the most important content and key statements of the conversation or presentation. Ideal for a quick overview.", "it": "Crea una panoramica concisa dei contenuti più importanti e delle dichiarazioni chiave della conversazione o presentazione. Ideale per una rapida panoramica.", "fr": "Crée un aperçu concis du contenu le plus important et des déclarations clés de la conversation ou de la présentation. Idéal pour un aperçu rapide.", "es": "Crea una visión general concisa del contenido más importante y las declaraciones clave de la conversación o presentación. Ideal para una visión rápida."}
```

View file

@ -1,19 +0,0 @@
# Offene Fragen Prompt
## Prompt ID
`c576e875-5a52-4f6a-abb7-0c62c945af78`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Liste alle wichtigen Fragen auf, die im folgenden Transkript gestellt wurden und unbeantwortet blieben. Sortiere sie nach Wichtigkeit/Dringlichkeit (HOCH/MITTEL/NIEDRIG). Gib für jede Frage kurzen Kontext, warum sie offen blieb und schlage konkrete nächste Schritte zur Klärung vor. Verwende folgendes Format: [PRIORITÄT] Frage: ... | Kontext: ... | Nächster Schritt: ... Verzichte auf zusätzliche Kommentare. Hier das Transkript:", "en": "List all important questions that were asked in the following transcript and remained unanswered. Sort them by importance/urgency (HIGH/MEDIUM/LOW). For each question, provide brief context on why it remained open and suggest concrete next steps for clarification. Use the following format: [PRIORITY] Question: ... | Context: ... | Next step: ... Avoid additional comments. Here is the transcript:", "it": "Elenca tutte le domande importanti che sono state poste nella seguente trascrizione e sono rimaste senza risposta. Ordinale per importanza/urgenza (ALTA/MEDIA/BASSA). Per ogni domanda, fornisci un breve contesto sul perché è rimasta aperta e suggerisci passi successivi concreti per il chiarimento. Usa il seguente formato: [PRIORITÀ] Domanda: ... | Contesto: ... | Prossimo passo: ... Evita commenti aggiuntivi. Ecco la trascrizione:", "fr": "Listez toutes les questions importantes qui ont été posées dans la transcription suivante et sont restées sans réponse. Triez-les par importance/urgence (ÉLEVÉE/MOYENNE/FAIBLE). Pour chaque question, fournissez un bref contexte expliquant pourquoi elle est restée ouverte et suggérez des prochaines étapes concrètes pour clarification. Utilisez le format suivant : [PRIORITÉ] Question : ... | Contexte : ... | Prochaine étape : ... Évitez les commentaires supplémentaires. Voici la transcription :", "es": "Enumera todas las preguntas importantes que se hicieron en la siguiente transcripción y quedaron sin respuesta. Ordénalas por importancia/urgencia (ALTA/MEDIA/BAJA). Para cada pregunta, proporciona un breve contexto sobre por qué quedó abierta y sugiere próximos pasos concretos para la aclaración. Usa el siguiente formato: [PRIORIDAD] Pregunta: ... | Contexto: ... | Próximo paso: ... Evita comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Offene Fragen", "en": "Open Questions", "it": "Domande Aperte", "fr": "Questions Ouvertes", "es": "Preguntas Abiertas"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Identifiziert alle Fragen, die während des Gesprächs aufgeworfen, aber nicht oder nicht vollständig beantwortet wurden.", "en": "Identifies all questions that were raised during the conversation but were not or not fully answered.", "it": "Identifica tutte le domande che sono state sollevate durante la conversazione ma non hanno ricevuto risposta o non sono state completamente risposte.", "fr": "Identifie toutes les questions qui ont été soulevées pendant la conversation mais n'ont pas reçu de réponse ou n'ont pas été complètement répondues.", "es": "Identifica todas las preguntas que surgieron durante la conversación pero no fueron respondidas o no fueron completamente respondidas."}
```

View file

@ -1,19 +0,0 @@
# Social Media Posts Prompt
## Prompt ID
`b2e39e0a-ec1f-4d0e-813d-f1a08493332b`
## Prompt Text (direkt für Supabase kopierbar)
```json
{"de": "Erstelle basierend auf dem folgenden Transkript Social Media Posts für verschiedene Plattformen. Formatiere jeden Post plattformgerecht: 1) X/TWITTER: Max. 280 Zeichen, prägnant, 2-3 Hashtags. 2) LINKEDIN: Professionell, 150-300 Wörter, 5-8 Hashtags. 3) INSTAGRAM: Visuell ansprechend beschrieben, Emoji-Nutzung, 10-15 Hashtags. 4) FACEBOOK: Storytelling-Ansatz, 100-200 Wörter, 3-5 Hashtags. 5) TIKTOK: Video-Idee mit Hook, Ablauf und Trending-Hashtags. Verzichte auf zusätzliche Kommentare. Hier das Transkript:", "en": "Create social media posts for different platforms based on the following transcript. Format each post platform-specifically: 1) X/TWITTER: Max. 280 characters, concise, 2-3 hashtags. 2) LINKEDIN: Professional, 150-300 words, 5-8 hashtags. 3) INSTAGRAM: Visually described, emoji usage, 10-15 hashtags. 4) FACEBOOK: Storytelling approach, 100-200 words, 3-5 hashtags. 5) TIKTOK: Video idea with hook, sequence and trending hashtags. Avoid additional comments. Here is the transcript:", "it": "Crea post per social media per diverse piattaforme basati sulla seguente trascrizione. Formatta ogni post specificamente per la piattaforma: 1) X/TWITTER: Max. 280 caratteri, conciso, 2-3 hashtag. 2) LINKEDIN: Professionale, 150-300 parole, 5-8 hashtag. 3) INSTAGRAM: Descritto visivamente, uso di emoji, 10-15 hashtag. 4) FACEBOOK: Approccio narrativo, 100-200 parole, 3-5 hashtag. 5) TIKTOK: Idea video con hook, sequenza e hashtag di tendenza. Evita commenti aggiuntivi. Ecco la trascrizione:", "fr": "Créez des publications pour les réseaux sociaux pour différentes plateformes basées sur la transcription suivante. Formatez chaque publication spécifiquement pour la plateforme : 1) X/TWITTER : Max. 280 caractères, concis, 2-3 hashtags. 2) LINKEDIN : Professionnel, 150-300 mots, 5-8 hashtags. 3) INSTAGRAM : Description visuelle, utilisation d'emojis, 10-15 hashtags. 4) FACEBOOK : Approche narrative, 100-200 mots, 3-5 hashtags. 5) TIKTOK : Idée vidéo avec accroche, séquence et hashtags tendance. Évitez les commentaires supplémentaires. Voici la transcription :", "es": "Crea publicaciones para redes sociales para diferentes plataformas basadas en la siguiente transcripción. Formatea cada publicación específicamente para la plataforma: 1) X/TWITTER: Máx. 280 caracteres, conciso, 2-3 hashtags. 2) LINKEDIN: Profesional, 150-300 palabras, 5-8 hashtags. 3) INSTAGRAM: Descripción visual, uso de emojis, 10-15 hashtags. 4) FACEBOOK: Enfoque narrativo, 100-200 palabras, 3-5 hashtags. 5) TIKTOK: Idea de video con gancho, secuencia y hashtags de tendencia. Evita comentarios adicionales. Aquí está la transcripción:"}
```
## Memory Title (direkt für Supabase kopierbar)
```json
{"de": "Social Media Posts", "en": "Social Media Posts", "it": "Post per Social Media", "fr": "Publications Réseaux Sociaux", "es": "Publicaciones en Redes Sociales"}
```
## Description (direkt für Supabase kopierbar)
```json
{"de": "Generiert optimierte Social Media Posts für X/Twitter, LinkedIn, Instagram, Facebook und TikTok. Jeder Post ist plattformspezifisch formatiert mit passender Länge, Tonalität und Hashtag-Anzahl. Enthält konkrete Textvorschläge und bei TikTok Video-Konzepte.", "en": "Generates optimized social media posts for X/Twitter, LinkedIn, Instagram, Facebook and TikTok. Each post is platform-specifically formatted with appropriate length, tone and hashtag count. Includes concrete text suggestions and video concepts for TikTok.", "it": "Genera post ottimizzati per social media per X/Twitter, LinkedIn, Instagram, Facebook e TikTok. Ogni post è formattato specificamente per la piattaforma con lunghezza, tono e numero di hashtag appropriati. Include suggerimenti di testo concreti e concetti video per TikTok.", "fr": "Génère des publications optimisées pour les réseaux sociaux pour X/Twitter, LinkedIn, Instagram, Facebook et TikTok. Chaque publication est formatée spécifiquement pour la plateforme avec une longueur, un ton et un nombre de hashtags appropriés. Inclut des suggestions de texte concrètes et des concepts vidéo pour TikTok.", "es": "Genera publicaciones optimizadas para redes sociales para X/Twitter, LinkedIn, Instagram, Facebook y TikTok. Cada publicación está formateada específicamente para la plataforma con longitud, tono y cantidad de hashtags apropiados. Incluye sugerencias de texto concretas y conceptos de video para TikTok."}
```

View file

@ -1,206 +0,0 @@
test
Dennis Bauer
Direkter Kontakt1.
"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford
Vari-tech GmbH
ZHAW School of Management and Law
Stockach, Baden-Württemberg, Deutschland Kontaktinfo
500+ Kontakte
Tobias Müller, Alex Vasileva und 19 weitere gemeinsame KontakteTobias Müller, Alex Vasileva und 19 weitere gemeinsame Kontakte
Nachricht
Mehr
HighlightsHighlights
Till Schneider
Dennis Bauer hat sich in den vergangenen 90 Tagen Ihr Profil angesehen
Dennis Bauer hat sich in den vergangenen 90 Tagen Ihr Profil angesehen
Kontaktieren Sie Dennis Bauer, um die Anforderungen zu verstehen.Kontaktieren Sie Dennis Bauer, um die Anforderungen zu verstehen.
Kostenlose Insights von LinkedIn Sales Navigator
Kostenlose Insights von LinkedIn Sales Navigator
Schalten Sie mehr Einblicke in Leads frei
Bessere Kontaktaufnahme mit vertriebsrelevanten Insights
Sales Navigator für 0 CHF erneut testen
1 Probemonat mit Support rund um die Uhr. Sie können jederzeit kündigen. Sie erhalten 7 Tage vor Ablauf der Probeversion eine entsprechende Erinnerung.
Alle 2 Highlights anzeigen
InfoInfo
Machen ist wie wollen, nur viel krasser! Machen ist wie wollen, nur viel krasser!
ServiceleistungenServiceleistungen
Projektmanagement • IT-Beratung • Filing • Dateiverwaltung • Dateneingabe • Technischer Support • InformationsmanagementProjektmanagement • IT-Beratung • Filing • Dateiverwaltung • Dateneingabe • Technischer Support • Informationsmanagement
Serviceleistungen anfordern
Alle Serviceleistungen anzeigen
AktivitätenAktivitäten
564 Follower:innen564 Follower:innen
Beiträge
Kommentare
9 „Beiträge“-Beiträge wurden geladen
Link zur Grafik von Dennis Bauer anzeigen
Dennis BauerDennis Bauer
• 1.1.
"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford
10 Monate • vor 10 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Gestern verfolgten wir aufmerksam den AV Vortrag und die Herangehensweise unserer Kunden.
Es ist immer interessant ein Thema auch von der gegenüberliegenden Seiten zu betrachten.
… mehr
Vari-tech GmbHVari-tech GmbH
180 Follower:innen180 Follower:innen
Tag 3 der archivistica 2024
Auch finden wieder interessante Vorträge statt, wie zum Beispiel "Umgang mit emotional belastenden Beständen und diskriminierender Sprache". Ein Thema das durchaus auch uns Dienstleister betrifft.
Unterstützung bringt unser Recording Secretary"
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
like
3
Link zur Grafik von Dennis Bauer anzeigen
Dennis BauerDennis Bauer
• 1.1.
"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford
10 Monate • vor 10 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
2ter Tag auf der archivistica 2024 in Suhl.
Memoro kommt auch unter Archivaren gut an.
1 Repost
Link zur Grafik von Dennis Bauer anzeigen
Dennis BauerDennis Bauer
• 1.1.
"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford"Wer immer tut was er schon kann bleibt immer das was er schon ist." Zitat Henry Ford
10 Monate • vor 10 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Vari-tech GmbHVari-tech GmbH
180 Follower:innen180 Follower:innen
91.Archivtag in Suhl
Treffen Sie uns auf dem Stand 31.
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
likecelebrate
4
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von Vari-tech GmbH
Geschäftsführender Gesellschafter
Geschäftsführender Gesellschafter
Vari-tech GmbH · SelbstständigVari-tech GmbH · Selbstständig
Juli 2021Heute · 4 Jahre 2 MonateJuli 2021Heute · 4 Jahre 2 Monate
Digitalisierung, Beratung, Konzepterarbeitung, Scanning, Datenaufbereitung, Datenkonvertierung und ErschließungsarbeitenDigitalisierung, Beratung, Konzepterarbeitung, Scanning, Datenaufbereitung, Datenkonvertierung und Erschließungsarbeiten
Workflow-Entwickler
Workflow-Entwickler
Workflow-Entwickler
K&B GbR · SelbstständigK&B GbR · Selbstständig
Okt. 2019Heute · 5 Jahre 11 MonateOkt. 2019Heute · 5 Jahre 11 Monate
Radolfzell am Bodensee, Baden-Württemberg, DeutschlandRadolfzell am Bodensee, Baden-Württemberg, Deutschland
Logo von Zahnmedizin Zentrum Dr. Basset
Manager COO
Manager COO
Zahnmedizin Zentrum Dr. BassetZahnmedizin Zentrum Dr. Basset
Jan. 2019Dez. 2021 · 3 JahreJan. 2019Dez. 2021 · 3 Jahre
Radolfzell am BodenseeRadolfzell am Bodensee
Logo von GBL Gubler AG
GBL Gubler AG
GBL Gubler AG
17 Jahre 9 Monate17 Jahre 9 Monate
Produktionsleiter und Mitglied der Geschäftsleitung
Produktionsleiter und Mitglied der Geschäftsleitung
Apr. 2011Sept. 2018 · 7 Jahre 6 MonateApr. 2011Sept. 2018 · 7 Jahre 6 Monate
Frauenfeld, Kanton Thurgau, SchweizFrauenfeld, Kanton Thurgau, Schweiz
wöchentliche Prozess- und Effizienzanalyse
Einbinden von Menschen mit Beeinträchtigung und sensibilisieren aller Mitarbeiter
Berater im Bereich der digitalen Archivierung, Prozessanalyse und neue Produkte.
Aufbau und Betrieb von abgesetzten Systemen vor Ort beim Kunden.
Business Analyst in mehreren Projekten Bsp. "Vecteur" des Schweizerischen Bundesarchiv.
Aufbau der Softwareentwicklungsabteilung in der neben kleineren Programmen auch ein neues WorkflowManagementSystem für Audio Visuelle Daten entickelt wurde.
Ausbau der Produktion in 4 Fachbereiche
Einführung eines WorkflowManagementSystems für Bilddatenwöchentliche Prozess- und Effizienzanalyse Einbinden von Menschen mit Beeinträchtigung und sensibilisieren aller Mitarbeiter Berater im Bereich der digitalen Archivierung, Prozessanalyse und neue Produkte. Aufbau und Betrieb von abgesetzten Systemen vor Ort beim Kunden. Business Analyst in mehreren Projekten Bsp. "Vecteur" des Schweizerischen Bundesarchiv. Aufbau der Softwareentwicklungsabteilung in der neben kleineren Programmen auch ein neues WorkflowManagementSystem für Audio Visuelle Daten entickelt wurde. Ausbau der Produktion in 4 Fachbereiche Einführung eines WorkflowManagementSystems für Bilddaten… mehr anzeigen
Produktionsleiter
Produktionsleiter
2008März 2011 · 3 Jahre 3 Monate2008März 2011 · 3 Jahre 3 Monate
85528552
wöchentliche Prozess- und Effizienzanalyse
permanente Prozess- und Workflowoptimierung
stetige Ausbildung der Mitarbeiter (Operator, Team- und Projektleiter)
Unterstützung bei Entwicklung des Laserbelichters Eternity E105 zur Produktionsreife am Standort Felben-Wellhausen. Softwareentwicklung in Zusammenarbeit mit dem IML der Univesität Basel.
Unterstützung in mehreren Teilbereichen bei der Entwicklung des Laserbelichters Archivlaser am Frauenhofer Institut in Freiburg und an der Universität Stuttgart zur Produktreife.
Mitarbeit in div. Arbeitsgruppen der e-CH und VSAwöchentliche Prozess- und Effizienzanalyse permanente Prozess- und Workflowoptimierung stetige Ausbildung der Mitarbeiter (Operator, Team- und Projektleiter) Unterstützung bei Entwicklung des Laserbelichters Eternity E105 zur Produktionsreife am Standort Felben-Wellhausen. Softwareentwicklung in Zusammenarbeit mit dem IML der Univesität Basel. Unterstützung in mehreren Teilbereichen bei der Entwicklung des Laserbelichters Archivlaser am Frauenhofer Institut in Freiburg und an der Universität Stuttgart zur Produktreife. Mitarbeit in div. Arbeitsgruppen der e-CH und VSA… mehr anzeigen
Projektleiter
Projektleiter
20012008 · 7 Jahre20012008 · 7 Jahre
85608560
Ausbelichtung und Finishing von Grossformatfotos.
Ausbau und Prozessoptimierung der Digitalisierungsabteilung
Aufbau Fachbereich Metadaten StandardAusbelichtung und Finishing von Grossformatfotos. Ausbau und Prozessoptimierung der Digitalisierungsabteilung Aufbau Fachbereich Metadaten Standard… mehr anzeigen
Logo von ammdoppleb
Monteur
Monteur
ammdopplebammdoppleb
19952001 · 6 Jahre19952001 · 6 Jahre
7826978269
Vom einfachen kleinen bis hin zum individuellen mehrstöckigen Messstand produzierte, fuhr und baute ich die Stände allein und in kleinen Teams in weiten Teilen Europas auf.
Aufgaben: Schreiner, Lackierer, Folien- und PrintverarbeitungVom einfachen kleinen bis hin zum individuellen mehrstöckigen Messstand produzierte, fuhr und baute ich die Stände allein und in kleinen Teams in weiten Teilen Europas auf. Aufgaben: Schreiner, Lackierer, Folien- und Printverarbeitung… mehr anzeigen
AusbildungAusbildung
ZHAW School of Management and Law
ZHAW School of Management and Law
ZHAW School of Management and Law
HERMES 5 FoundationHERMES 5 Foundation
2017201720172017
Berufsschulzentrum Radolfzell
Berufsschulzentrum Radolfzell
Berufsschulzentrum Radolfzell
1992199519921995
Ausbildung zum Bau- und MöbeltischlerAusbildung zum Bau- und Möbeltischler
KenntnisseKenntnisse
Microsoft Office
Microsoft Office
Johannes Paul Kauert (John)s Profilfoto
verifiziert von Johannes Paul Kauert (John) (gemeinsamer Kontakt)verifiziert von Johannes Paul Kauert (John) (gemeinsamer Kontakt)
Johannes Paul Kauert (John)s Profilfoto
In den vergangenen 6 Monaten von 1 Person bestätigtIn den vergangenen 6 Monaten von 1 Person bestätigt
3 Kenntnisbestätigungen
3 Kenntnisbestätigungen
Bestätigen
Management
Management
Johannes Paul Kauert (John)s Profilfoto
verifiziert von Johannes Paul Kauert (John) (gemeinsamer Kontakt)verifiziert von Johannes Paul Kauert (John) (gemeinsamer Kontakt)
Johannes Paul Kauert (John)s Profilfoto
In den vergangenen 6 Monaten von 1 Person bestätigtIn den vergangenen 6 Monaten von 1 Person bestätigt
3 Kenntnisbestätigungen
3 Kenntnisbestätigungen
Bestätigen
Alle 14 Kenntnisse anzeigen
OrganisationenOrganisationen
VSA-AAS
VSA-AAS
Jan. 2014HeuteJan. 2014Heute
Der Verein Schweizerischer Archivarinnen und Archivare (VSA) repräsentiert Archivarinnen und Archivare, Records Manager und Informationsfachleute in der Schweiz. Als nationaler Berufsverband unterstützt er die Zusammenarbeit der professionellen Archive und das Ziel, den Zugang zum Archivgut benutzungsfreundlich zu gestalten.Der Verein Schweizerischer Archivarinnen und Archivare (VSA) repräsentiert Archivarinnen und Archivare, Records Manager und Informationsfachleute in der Schweiz. Als nationaler Berufsverband unterstützt er die Zusammenarbeit der professionellen Archive und das Ziel, den Zugang zum Archivgut benutzungsfreundlich zu gestalten.… mehr anzeigen
Kost ceco
Kost ceco
Mitglied in der Arbeitsgruppe "digitale Archivierung" · März 2010HeuteMitglied in der Arbeitsgruppe "digitale Archivierung" · März 2010Heute
eCH-Fachgruppe Digitale Archivierung
Die eCH-Fachgruppe Digitale Archivierung ist das Standardisierungsgremium, das sämtliche Akteure der digitalen Archivierung (staatliche Archive, öffentliche Verwaltungen, Software- und Dienstleistungsanbieter) vereinigt. Sie bietet somit einen Rahmen für breit abgestützte Standardisierungsvorhaben.
Mehr Informationen zur eCH-Fachgruppe Digitale Archivierung finden Sie auf der eCH-Website sowie auf dem eCH-Share (für registrierte User). Untenstehend dokumentiert sind die bisherigen Sitzungen der Fachgruppe.

View file

@ -1,285 +0,0 @@
Dirk Zimanky
Direkter Kontakt1.
Gründer von adCura - Wir beratenEigentümer, Investoren und Verwaltungsräte. Experte in Electronic Manufacturing Services (EMS)
edisconet
University of Konstanz
Zürich Metropolitan Area Kontaktinfo
1.195 Follower:innen
500+ Kontakte
Alex Vasileva, Tobias Müller und 19 weitere gemeinsame KontakteAlex Vasileva, Tobias Müller und 19 weitere gemeinsame Kontakte
Nachricht
Ihre Serviceleistungen anzeigen
Mehr
Profil mit Premium verbessert
HighlightsHighlights
Unternehmenslogo
Dirk Zimanky folgt Ihrem Unternehmen auf LinkedIn
Dirk Zimanky folgt Ihrem Unternehmen auf LinkedIn
Dirk Zimanky kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.Dirk Zimanky kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.
Kostenlose Insights von LinkedIn Sales Navigator
Kostenlose Insights von LinkedIn Sales Navigator
Schalten Sie mehr Einblicke in Leads frei
Bessere Kontaktaufnahme mit vertriebsrelevanten Insights
Sales Navigator für 0 CHF erneut testen
1 Probemonat mit Support rund um die Uhr. Sie können jederzeit kündigen. Sie erhalten 7 Tage vor Ablauf der Probeversion eine entsprechende Erinnerung.
InfoInfo
Ich arbeite jeden Tag daran, Menschen und Unternehmen erfolgreich zu machen, die positiv auf unsere Umwelt und das sozioökonomische Leben einwirken und damit die Welt zu einem besseren Ort machen.
Mit mehr als 30 Jahren Erfahrung in den Bereichen Business, Technologie und Marktentwicklung, insbesondere auf dem globalen Markt für Elektronik, habe ich folgende Kernkompetenzen:
- Starke Expertise in den Bereichen Business, Technologie und Marktentwicklung, insbesondere auf dem globalen Markt für Elektronik.
- Menschen zu herausfordernden Zielen motivieren und sich selbst tragende Leistungseinheiten bilden.
- Aufbau langjähriger strategischer Partnerschaften und dauerhafter persönlicher Geschäfts-Beziehungen.
- Breites internationales Kontakt Netzwerk.
- Fähigkeit von Organisationsstrukturen auf lokaler und internationaler Ebene zu bewerten und zu optimieren.
- Sicher in multikulturellen und internationalen Vertragsverhandlungen.
- Vertraut mit der Führung und Geschäftsdynamik in Asien, Europa und den USA.
- Fusionen und Übernahmen auf Käufer- & Verkäuferseite.
Meine Werte sind ethisches Verhalten, Vertrauen, ehrlicher Respekt, Zusammenarbeit, greifbare Innovation und Wertschätzung des Lebens.Ich arbeite jeden Tag daran, Menschen und Unternehmen erfolgreich zu machen, die positiv auf unsere Umwelt und das sozioökonomische Leben einwirken und damit die Welt zu einem besseren Ort machen. Mit mehr als 30 Jahren Erfahrung in den Bereichen Business, Technologie und Marktentwicklung, insbesondere auf dem globalen Markt für Elektronik, habe ich folgende Kernkompetenzen: - Starke Expertise in den Bereichen Business, Technologie und Marktentwicklung, insbesondere auf dem globalen Markt für Elektronik. - Menschen zu herausfordernden Zielen motivieren und sich selbst tragende Leistungseinheiten bilden. - Aufbau langjähriger strategischer Partnerschaften und dauerhafter persönlicher Geschäfts-Beziehungen. - Breites internationales Kontakt Netzwerk. - Fähigkeit von Organisationsstrukturen auf lokaler und internationaler Ebene zu bewerten und zu optimieren. - Sicher in multikulturellen und internationalen Vertragsverhandlungen. - Vertraut mit der Führung und Geschäftsdynamik in Asien, Europa und den USA. - Fusionen und Übernahmen auf Käufer- & Verkäuferseite. Meine Werte sind ethisches Verhalten, Vertrauen, ehrlicher Respekt, Zusammenarbeit, greifbare Innovation und Wertschätzung des Lebens.… mehr anzeigen
ServiceleistungenServiceleistungen
Advising founders, owners, investors & boards through:
ACTIVE INVOLVEMENT
for founders and key members through business challenges, board work, business idea feasibility & strategy formulation.
PROFITABLE GROWTH
by implementing new dynamics, including strategy review, interim management, cultural & change management.
MAXIMIZING THE VALUE
of investments through active board work, investment opportunities, add-on acquisitions, divestitures & more.Advising founders, owners, investors & boards through: ACTIVE INVOLVEMENT for founders and key members through business challenges, board work, business idea feasibility & strategy formulation. PROFITABLE GROWTH by implementing new dynamics, including strategy review, interim management, cultural & change management. MAXIMIZING THE VALUE of investments through active board work, investment opportunities, add-on acquisitions, divestitures & more.… mehr anzeigen
Veränderungsmanagement • Unternehmensberatung • Preispolitik • Strategische Planung • Führungskräftecoaching • VerhandlungsführungVeränderungsmanagement • Unternehmensberatung • Preispolitik • Strategische Planung • Führungskräftecoaching • Verhandlungsführung
Alle Serviceleistungen anzeigen
Im FokusIm Fokus
Bild
Bild
Bild für What Others Say
What Others SayWhat Others Say
https://adcura.comhttps://adcura.com
Bild
Bild
Bild für What Others Say
What Others SayWhat Others Say
https://adcura.comhttps://adcura.com
Bild
Bild
Bild für The 4 D's of Organizational Transformation
The 4 D's of Organizational TransformationThe 4 D's of Organizational Transformation
“Im convinced that about half of what separates successful entrepreneurs from the non-successful ones is pure perseverance.” Steve Jobs, Co-Founder of Apple.“Im convinced that about half of what separates successful entrepreneurs from the non-successful ones is pure perseverance.” Steve Jobs, Co-Founder of Apple.
Bild
Bild
Bild für 4 step roadmap to ensure business growth
4 step roadmap to ensure business growth4 step roadmap to ensure business growth
At adCura, we have developed a straight forward 4 step roadmap to ensure our customers strategic business growth that is designed to boost agility and flexibility in their organizations and strengthen their long-term strategies in this new global business environment. In our upcoming posts we will take a closer look on these steps and their impact.
https://adcura.comAt adCura, we have developed a straight forward 4 step roadmap to ensure our customers strategic business growth that is designed to boost agility and flexibility in their organizations and strengthen their long-term strategies in this new global business environment. In our upcoming posts we will take a closer look on these steps and their impact. https://adcura.com
Link
Link
adCura - advising founders, owners, investors & boardsadCura - advising founders, owners, investors & boards
adCuraadCura
with our network of international experts, adCura strives to make the world a better place
by empowering people and organizations having a positive impact on our environment and social-economic life.
adCuras highly experienced team & associates with deep understanding about leading global operating entities and small local units have held top management & CEO positions, and driven the growth of multinational half a billion worth companies.with our network of international experts, adCura strives to make the world a better place by empowering people and organizations having a positive impact on our environment and social-economic life. adCuras highly experienced team & associates with deep understanding about leading global operating entities and small local units have held top management & CEO positions, and driven the growth of multinational half a billion worth companies.
AktivitätenAktivitäten
1.195 Follower:innen1.195 Follower:innen
Follower:in
Beiträge
Kommentare
9 „Beiträge“-Beiträge wurden geladen
Profilfoto von Dirk Zimanky
Dirk Zimanky hat dies repostet
Link zur Grafik von Stephanie Kaudela-Baum, Prof. Dr. anzeigen
Stephanie Kaudela-Baum, Prof. Dr.Stephanie Kaudela-Baum, Prof. Dr.
• 2.Verifiziert • 2.
Professor of Leadership and Innovation I Co-Head Competence Center Business Development, Leadership and HR I Lecturer I Speaker I #leadership I #innovation I #creativityProfessor of Leadership and Innovation I Co-Head Competence Center Business Development, Leadership and HR I Lecturer I Speaker I #leadership I #innovation I #creativity
2 Wochen • vor 2 Wochen • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
In Lucerne, you definitely get into the continuous innovation flow. Late summer, lake, mountains, chocolate and great conference participants :) - register now and network with the CINet community! HSLU Hochschule Luzern HSLU Institut für Betriebs- und Regionalökonomie IBR HSLU Lucerne School of Business International
… mehr
Continuous Innovation Network (CINet)Continuous Innovation Network (CINet)
597 Follower:innen597 Follower:innen
The 26th CINet conference is coming!
Join us on 7-9 September 2025 in the beautiful Lucerne!
Registrations are still open: https://lnkd.in/dQ_ix6FG
CINet Board
Maria Carmela Annosi, Harry Boer, Tim Schweisfurth, Luca Gastaldi, René Chester Goduscheit, Katharina Hölzle, Nicolette Lakemond, Mats Magnusson, Luisa Pellegrini, Magnus Persson, Daniel Trabucchi, Jeannette Visser-Groeneveld, Melanie Wiener, Patricia Wolf
Local Organizers Committee
Patricia Wolf, Stephanie Kaudela-Baum, Prof. Dr., Julien Alain Nussbaum, Christian Hohmann, Shaun West, Prof. Dr. Petra Müller-Csernetzky
… mehr
Übersetzung anzeigen
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
like
16
2 Reposts
Link zur Grafik von Dirk Zimanky anzeigen
Dirk ZimankyDirk Zimanky
• 1.Premium • 1.
Gründer von adCura - Wir beratenEigentümer, Investoren und Verwaltungsräte. Experte in Electronic Manufacturing Services (EMS)Gründer von adCura - Wir beratenEigentümer, Investoren und Verwaltungsräte. Experte in Electronic Manufacturing Services (EMS)
Ihre Serviceleistungen anzeigen
2 Monate • vor 2 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Most companies don't have a knowledge deficit they just don't know what they already know. How can generative AI help to make hidden knowledge visible without overwhelming employees?
We have published a new article at edisconet, take a look:
… mehr
edisconetedisconet
240 Follower:innen240 Follower:innen
Die meisten Unternehmen haben kein Wissensdefizit sie wissen nur nicht, was sie schon wissen.
Wie kann generative KI helfen, verborgenes Wissen sichtbar zu machen ganz ohne Ihre Mitarbeitenden zu überfordern?
In unserem neuen Artikel zeigen wir,
- wie KI stilles Wissen in nutzbares Wissen verwandelt
- was das mit CRM-Daten, Vertriebsprozessen und Transparenz zu tun hat
- und warum wir gemeinsam mit der HSLU eine Sandbox für wissensintensive Organisationen entwickeln.
👉 Jetzt lesen: „Wissensgenerierung neu denken: Wie KI zum stillen Teammitglied Ihrer Organisation wird“
🔗 https://lnkd.in/eu6iEB4a
🎯 Neugierig geworden? Am Ende des Artikels erfahren Sie, wie Sie Teil unseres spannenden Forschungsprojekts werden können.
Hashtag#Wissensmanagement Hashtag#GenerativeAI Hashtag#TacitKnowledge Hashtag#AIimUnternehmen Hashtag#edisconetCommons Hashtag#FutureOfWork
Prof. Dr. Petra Müller-Csernetzky, Stephanie Kaudela-Baum, Prof. Dr., HSLU Hochschule Luzern, Dirk Zimanky
… mehr
Größere Bilddarstellung aktivieren,
Wissensmanagement und KI_edisconet, HSLU
Größere Bilddarstellung aktivieren,
likecelebrate
15
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von edisconet
Board Member
Board Member
edisconet · Vollzeitedisconet · Vollzeit
Jan. 2022Heute · 3 Jahre 8 MonateJan. 2022Heute · 3 Jahre 8 Monate
edisconet is one platform for multiple learning paths. It allows to engage your employees with meaningful & rewarding learning experiences by integrating your internal systems with the universe of trainings, trainers & learning paths.edisconet is one platform for multiple learning paths. It allows to engage your employees with meaningful & rewarding learning experiences by integrating your internal systems with the universe of trainings, trainers & learning paths.… mehr anzeigen
Logo von adCura
Gründer und Geschäftsführender Partner
Gründer und Geschäftsführender Partner
adCuraadCura
2020Heute · 5 Jahre 8 Monate2020Heute · 5 Jahre 8 Monate
SchweizSchweiz
adCura berät Gründer, Eigentümer, Investoren und Verwaltungsräte. Mit unserem Netzwerk internationaler Experten arbeiten wir mit Start-ups, Unternehmen des Mittelstands und Risikokapitalunternehmen zusammen. Bei adCura konzentriert sich Alles darauf den Wert der Unternehmung für die verschiedenen Stakeholder zu erhöhen.adCura berät Gründer, Eigentümer, Investoren und Verwaltungsräte. Mit unserem Netzwerk internationaler Experten arbeiten wir mit Start-ups, Unternehmen des Mittelstands und Risikokapitalunternehmen zusammen. Bei adCura konzentriert sich Alles darauf den Wert der Unternehmung für die verschiedenen Stakeholder zu erhöhen.… mehr anzeigen
Logo von Enics
Enics
Enics
16 Jahre 1 Monat16 Jahre 1 Monat
Senior Vice President, Market Execution
Senior Vice President, Market Execution
20132020 · 7 Jahre20132020 · 7 Jahre
Enics ist ein Unternehmen, das Fertigungs-, Entwicklungs- und Kundendienstleistungen für elektronische Schaltungen und komplette elektronische Systeme für industrielle OEMs weltweit anbietet. Leitung des Inbound-Geschäfts mit mehr als 500 MEUR Jahresumsatz aus globalen OEM-Kunden. Verantwortlich für Gewinn und Verlust, Budgets und Planung für die verschiedenen Dienstleistungen in den Bereichen Fertigung, Engineering und After-Sales.Enics ist ein Unternehmen, das Fertigungs-, Entwicklungs- und Kundendienstleistungen für elektronische Schaltungen und komplette elektronische Systeme für industrielle OEMs weltweit anbietet. Leitung des Inbound-Geschäfts mit mehr als 500 MEUR Jahresumsatz aus globalen OEM-Kunden. Verantwortlich für Gewinn und Verlust, Budgets und Planung für die verschiedenen Dienstleistungen in den Bereichen Fertigung, Engineering und After-Sales.… mehr anzeigen
President and CEO
President and CEO
20102013 · 3 Jahre20102013 · 3 Jahre
Diese Position nach der Finanzkrise im Jahr 2009 einzunehmen, führte das Unternehmen zurück auf einen Weg des organischen Wachstums. Wir haben unsere europäischen Einheiten auf Mehrwertdienste umgestellt, den Ausbau des zweiten Standorts in China erfolgreich abgeschlossen und das Unternehmen mit einer durchschnittlichen jährlichen Wachstumsrate von 8 Prozent organisch ausgebaut. Ich führte die strategische und operative Leitung der Gruppe und deren acht Geschäftseinheiten mit insgesamt 3500 Mitarbeitern. Wir waren in Estland, der Schweiz, Finnland, der Slowakei, Schweden, China und Hongkong tätig.Diese Position nach der Finanzkrise im Jahr 2009 einzunehmen, führte das Unternehmen zurück auf einen Weg des organischen Wachstums. Wir haben unsere europäischen Einheiten auf Mehrwertdienste umgestellt, den Ausbau des zweiten Standorts in China erfolgreich abgeschlossen und das Unternehmen mit einer durchschnittlichen jährlichen Wachstumsrate von 8 Prozent organisch ausgebaut. Ich führte die strategische und operative Leitung der Gruppe und deren acht Geschäftseinheiten mit insgesamt 3500 Mitarbeitern. Wir waren in Estland, der Schweiz, Finnland, der Slowakei, Schweden, China und Hongkong tätig.… mehr anzeigen
Co-founder and Senior Vice President
Co-founder and Senior Vice President
20042010 · 6 Jahre20042010 · 6 Jahre
Enics wurde 2004 als Leveraged Management-Buy-Out ausgewählter Teile von Elcoteq gegründet. Von 2006 bis 2010 war ich als Senior Vice President, CRM sowie von 2004 bis 2006 als Vice President Business Development tätig. In diesem Zeitraum haben wir mehrere Akquisitionen und operative Erweiterungen durchgeführt und das Unternehmen erfolgreich von 116 Mio. EUR auf über 300 Mio. EUR ausgebaut. Berichterstattung an den CEO.Enics wurde 2004 als Leveraged Management-Buy-Out ausgewählter Teile von Elcoteq gegründet. Von 2006 bis 2010 war ich als Senior Vice President, CRM sowie von 2004 bis 2006 als Vice President Business Development tätig. In diesem Zeitraum haben wir mehrere Akquisitionen und operative Erweiterungen durchgeführt und das Unternehmen erfolgreich von 116 Mio. EUR auf über 300 Mio. EUR ausgebaut. Berichterstattung an den CEO.… mehr anzeigen
Logo von Elcoteq
Elcoteq
Elcoteq
5 Jahre 1 Monat5 Jahre 1 Monat
Director, Business Development Business Area Communication Network Equipment & Industrial Electronic
Director, Business Development Business Area Communication Network Equipment & Industrial Electronic
20022004 · 2 Jahre20022004 · 2 Jahre
Elcoteq trat dem Unternehmen auf dem Höhepunkt des Wachstums des Mobilfunkmarktes bei und erbrachte elektronische Fertigungsdienstleistungen für Mobiltelefone und Basisstationen von Kommunikationsnetzwerken. Wir verzeichneten ein sehr schnelles Umsatzwachstum, haben in einigen Jahren den Umsatz mehr als verdoppelt und betrieben ein Fertigungsnetzwerk in 16 Geschäftsbereichen in Japan, China, Estland, USA, Mexiko, Russland und Finnland. Während meiner Zeit bei Elcoteq hatte ich von 2002 bis 2004 verschiedene Positionen als Director, Business Development Business Area Communication Network Equipment und Industrial Electronic inne. Von 2000 bis 2002 Director, Sales Industrial Electronic und von 1999 bis 2000 als Account Manager, Geographical Area Europe.Elcoteq trat dem Unternehmen auf dem Höhepunkt des Wachstums des Mobilfunkmarktes bei und erbrachte elektronische Fertigungsdienstleistungen für Mobiltelefone und Basisstationen von Kommunikationsnetzwerken. Wir verzeichneten ein sehr schnelles Umsatzwachstum, haben in einigen Jahren den Umsatz mehr als verdoppelt und betrieben ein Fertigungsnetzwerk in 16 Geschäftsbereichen in Japan, China, Estland, USA, Mexiko, Russland und Finnland. Während meiner Zeit bei Elcoteq hatte ich von 2002 bis 2004 verschiedene Positionen als Director, Business Development Business Area Communication Network Equipment und Industrial Electronic inne. Von 2000 bis 2002 Director, Sales Industrial Electronic und von 1999 bis 2000 als Account Manager, Geographical Area Europe.… mehr anzeigen
Director, Sales Industrial Electronic
Director, Sales Industrial Electronic
20002002 · 2 Jahre20002002 · 2 Jahre
Account Manager, Geographical Area Europe
Account Manager, Geographical Area Europe
19992000 · 1 Jahr19992000 · 1 Jahr
Stephan Elektronik
Stephan Elektronik
Stephan Elektronik
13 Jahre 1 Monat13 Jahre 1 Monat
Head of Administration (Finance, HR, Legal, IT, Sales)
Head of Administration (Finance, HR, Legal, IT, Sales)
19911999 · 8 Jahre19911999 · 8 Jahre
Der Start in einem kleineren Familienunternehmen bot mir die großartige Gelegenheit, in allen Fachgebieten zu arbeiten, von Design, Beschaffung, Betrieb bis hin zu Recht und Finanzen. Wir haben das Unternehmen auf drei Standorte mit mehr als 1300 Mitarbeitern ausgebaut.Der Start in einem kleineren Familienunternehmen bot mir die großartige Gelegenheit, in allen Fachgebieten zu arbeiten, von Design, Beschaffung, Betrieb bis hin zu Recht und Finanzen. Wir haben das Unternehmen auf drei Standorte mit mehr als 1300 Mitarbeitern ausgebaut.… mehr anzeigen
Head Of Department, Operations and supply chain
Head Of Department, Operations and supply chain
19861991 · 5 Jahre19861991 · 5 Jahre
Holding several positions as department head within operations and supply chain in Germany, Poland and Switzerland. Reporting to the owner.Holding several positions as department head within operations and supply chain in Germany, Poland and Switzerland. Reporting to the owner.
AusbildungAusbildung
Logo von Universität Konstanz
Universität Konstanz
Universität Konstanz
Master's degree, Administration ScienceMaster's degree, Administration Science
1985199119851991
Collegium Mehrerau-Bernardi
Collegium Mehrerau-Bernardi
Collegium Mehrerau-Bernardi
1976198419761984
KenntnisseKenntnisse
Leadership
Leadership
Logo von Enics
Bestätigt von 5 Kolleg:innen bei EnicsBestätigt von 5 Kolleg:innen bei Enics
10 Kenntnisbestätigungen
10 Kenntnisbestätigungen
Bestätigen
Business Development
Business Development
Logo von Enics
Bestätigt von 5 Kolleg:innen bei EnicsBestätigt von 5 Kolleg:innen bei Enics
10 Kenntnisbestätigungen
10 Kenntnisbestätigungen
Bestätigen
Alle 12 Kenntnisse anzeigen
EmpfehlungenEmpfehlungen
Dirk Zimanky empfehlen
ErhaltenErhalten
ErteiltErteilt
Noch keine Informationen verfügbar
Noch keine Informationen verfügbar
Empfehlungen, die Dirk Zimanky erhält, erscheinen hier.Empfehlungen, die Dirk Zimanky erhält, erscheinen hier.
KurseKurse
Introduction to Digital Facilitation
Introduction to Digital Facilitation
Unternehmenslogo
Assoziiert mit adCura
Assoziiert mit adCura
SprachenSprachen
English
English
FließendFließend
French
French
Grundkenntnisse

View file

@ -1,143 +0,0 @@
Florian König
Direkter Kontakt1.
So geht Marketing-Automation - Quentn.com
Quentn.com GmbH
Pfinztal, Baden-Württemberg, Deutschland Kontaktinfo
247 Kontakte
Alex Vasileva, Tobias Müller und 1 weiterer gemeinsamer KontaktAlex Vasileva, Tobias Müller und 1 weiterer gemeinsamer Kontakt
Nachricht
Mehr
HighlightsHighlights
Unternehmenslogo
Florian König folgt Ihrem Unternehmen auf LinkedIn
Florian König folgt Ihrem Unternehmen auf LinkedIn
Florian König kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.Florian König kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.
Kostenlose Insights von LinkedIn Sales Navigator
Kostenlose Insights von LinkedIn Sales Navigator
Schalten Sie mehr Einblicke in Leads frei
Bessere Kontaktaufnahme mit vertriebsrelevanten Insights
Sales Navigator für 0 CHF erneut testen
1 Probemonat mit Support rund um die Uhr. Sie können jederzeit kündigen. Sie erhalten 7 Tage vor Ablauf der Probeversion eine entsprechende Erinnerung.
AktivitätenAktivitäten
246 Follower:innen246 Follower:innen
4 „Beiträge“-Beiträge wurden geladen
Profilfoto von Florian König
Florian König hat dies repostet
edisconetedisconet
240 Follower:innen240 Follower:innen
2 Monate • vor 2 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Silent knowledge that never makes it into meetings or manuals is a part of every organisation. At edisconet, we magnify this hidden knowledge and turn it into useful insights.
It's time to give your team's unspoken expertise the spotlight it deserves.
https://edisconet.com
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
like
11
2 Kommentare
2 Reposts
Profilfoto von Florian König
Florian König hat dies repostet
MemoroMemoro
219 Follower:innen219 Follower:innen
1 Jahr • Bearbeitet • vor 1 Jahr • Bearbeitet • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
📢 Unser größtes App-Update bisher 🚀
✨ Viele neue Funktionen:
Spezielle Modi: Verschiedene Modi helfen euch, spezielle Aufgaben zu erledigen, z.B. ein Formular auszufüllen, eine Mail zu formulieren oder eine Besprechung aufzunehmen. Aktuell sind Modi in den Bereichen Pflege, Handwerk und Bau, Büro, Universität, Tagebuch und Journalismus verfügbar.
Checklisten: Jeder Modus hat seine eigene Checkliste mit Tipps, um strukturierte Aufnahmen zu erstellen und wichtige Punkte nicht zu vergessen.
26 Sprachen: Memoro kann jetzt in 26 Sprachen zuhören und mitschreiben inklusive eingebauter Übersetzung. (Deutsch, Schwiizerdütsch, Österreichisch, Englisch, Niederländisch, Französisch, Italienisch, Spanisch, Schwedisch, Norwegisch, Rumänisch, Griechisch, Ägyptisches Arabisch (Masri), Türkisch, Russisch, Ukrainisch, Ungarisch, Hindi, Chinesisch (Zhōngwén), Koreanisch, Indonesisch, Vietnamesisch)
Wichtig: Um weiterhin alle Funktionen von Memoro nutzen zu können, ist ein Upgrade auf die neueste Version erforderlich. Bitte deinstalliert eure aktuelle Version und installiert die neue Version aus dem Play Store oder App Store.
Hier findet ihr die App für Android im Play Store: https://lnkd.in/dhszvBck
und hier für Apple im App Store: https://lnkd.in/d4Kh58aj
Wir freuen uns darauf, dass ihr die neuen Funktionen von Memoro ausprobiert und sind gespannt auf euer Feedback! 🌍💬
Hashtag#Memoro Hashtag#Update Hashtag#NeueFunktionen Hashtag#Produktivität Hashtag#AppUpdate Hashtag#Sprachübersetzung Hashtag#Dokumentation
… mehr
Größere Bilddarstellung aktivieren,
Memoro, App Version 1.5, Update, Modi, Modes
Größere Bilddarstellung aktivieren,
likelovecelebrate
18
1 Kommentar
6 Reposts
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von edisconet
Vertriebsmanager
Vertriebsmanager
edisconet · Selbstständigedisconet · Selbstständig
Sept. 2024Heute · 1 JahrSept. 2024Heute · 1 Jahr
Schweiz · RemoteSchweiz · Remote
Logo von Quentn.com GmbH
Partnermanager
Partnermanager
Quentn.com GmbH · SelbstständigQuentn.com GmbH · Selbstständig
Nov. 2023Heute · 1 Jahr 10 MonateNov. 2023Heute · 1 Jahr 10 Monate
Logo von OWNERMEETING
Geschäftsführer
Geschäftsführer
OWNERMEETING · SelbstständigOWNERMEETING · Selbstständig
Jan. 2021Heute · 4 Jahre 8 MonateJan. 2021Heute · 4 Jahre 8 Monate
Pfinztal, Baden-Württemberg, Deutschland · Vor OrtPfinztal, Baden-Württemberg, Deutschland · Vor Ort
Logo von doxx-on systems GmbH
Vertriebsmitarbeiter
Vertriebsmitarbeiter
doxx-on systems GmbH · Vollzeitdoxx-on systems GmbH · Vollzeit
Jan. 2023Nov. 2023 · 11 MonateJan. 2023Nov. 2023 · 11 Monate
Ettlingen, Baden-Württemberg, Deutschland · RemoteEttlingen, Baden-Württemberg, Deutschland · Remote
Logo von Erhardt Gruppe
Key-Account-Manager
Key-Account-Manager
Erhardt BüroweltErhardt Bürowelt
Juli 2007Dez. 2022 · 15 Jahre 6 MonateJuli 2007Dez. 2022 · 15 Jahre 6 Monate
Alle 9 Berufserfahrungen anzeigen
KenntnisseKenntnisse
Microsoft Office
Microsoft Office
1 Kenntnisbestätigung
1 Kenntnisbestätigung
Bestätigen
Kundendienst
Kundendienst
1 Kenntnisbestätigung
1 Kenntnisbestätigung
Bestätigen
Alle 11 Kenntnisse anzeigen
InteressenInteressen
UnternehmenUnternehmen
GruppenGruppen
NewsletterNewsletter
Logo von Greenpeace
Greenpeace
Greenpeace
635.071 Follower:innen635.071 Follower:innen
Folgen
Logo von World Wildlife Fund
World Wildlife Fund
World Wildlife Fund
408.972 Follower:innen408.972 Follower:innen
Folgen

View file

@ -1,164 +0,0 @@
Lucas Mag
Direkter Kontakt1.
Informatikspezialist Datensicherung bei Universität Zürich | University of Zurich
Universität Zürich | University of Zurich
Elektronikschule Tettnang
Jestetten, Baden-Württemberg, Deutschland Kontaktinfo
186 Kontakte
Alex Vasileva, Tobias Müller und 5 weitere gemeinsame KontakteAlex Vasileva, Tobias Müller und 5 weitere gemeinsame Kontakte
Nachricht
Mehr
HighlightsHighlights
Unternehmenslogo
Lucas Mag folgt Ihrem Unternehmen auf LinkedIn
Lucas Mag folgt Ihrem Unternehmen auf LinkedIn
Lucas Mag kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.Lucas Mag kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.
Kostenlose Insights von LinkedIn Sales Navigator
Kostenlose Insights von LinkedIn Sales Navigator
Schalten Sie mehr Einblicke in Leads frei
Bessere Kontaktaufnahme mit vertriebsrelevanten Insights
Sales Navigator für 0 CHF erneut testen
1 Probemonat mit Support rund um die Uhr. Sie können jederzeit kündigen. Sie erhalten 7 Tage vor Ablauf der Probeversion eine entsprechende Erinnerung.
AktivitätenAktivitäten
186 Follower:innen186 Follower:innen
9 „Beiträge“-Beiträge wurden geladen
Profilfoto von Lucas Mag
Lucas Mag hat dies repostet
Link zur Grafik von Stephan Lienhard anzeigen
Stephan LienhardStephan Lienhard
• 2.Verifiziert • 2.
ICT Informatikspezialist an der Universität Zürich | IT-Architektur, Server, BackupICT Informatikspezialist an der Universität Zürich | IT-Architektur, Server, Backup
10 Monate • vor 10 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Wir suchen Verstärkung im Team :-) Bei Fragen gerne Melden!
https://lnkd.in/dPrW3kkD
UZH: ICT System Engineer
jobs.uzh.ch
like
8
2 Reposts
Profilfoto von Lucas Mag
Lucas Mag hat dies repostet
Link zur Grafik von Marco Fernandez anzeigen
Marco FernandezMarco Fernandez
• 2.Premium • 2.
Manager Presales - ACH Switzerland and Austria @ Veeam Software | Technical SalesManager Presales - ACH Switzerland and Austria @ Veeam Software | Technical Sales
10 Monate • vor 10 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
At yesterday's Meet the Architect with our customers and partners, the Universität Zürich | University of Zurich demonstrated how they use Veeam Software and the benefits it provides. We would like to thank all our enterprise customers partners who attended and look forward to the next Meet the Architect in Q1 2025.
Many thanks to Lucas Mag for the excellent and detailed presentation.
Universität Zürich | University of Zurich, Veeam Software
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
likecelebrate
37
1 Kommentar
1 Repost
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von Universität Zürich | University of Zurich
Informatikspezialist Datensicherung
Informatikspezialist Datensicherung
Universität Zürich | University of Zurich · VollzeitUniversität Zürich | University of Zurich · Vollzeit
Nov. 2022Heute · 2 Jahre 10 MonateNov. 2022Heute · 2 Jahre 10 Monate
Zürich, SchweizZürich, Schweiz
Inhaber
Inhaber
Inhaber
Haus & Heizungsautomatisierung · SelbstständigHaus & Heizungsautomatisierung · Selbstständig
Aug. 2022Apr. 2025 · 2 Jahre 9 MonateAug. 2022Apr. 2025 · 2 Jahre 9 Monate
Stühlingen, Baden-Württemberg, DeutschlandStühlingen, Baden-Württemberg, Deutschland
IT Dienstleistungen im Hausautomatisierungs- und Heizungsautomatisierungsbereich.
Ich helfe Ihnen den effizienten Weg zum autonomen Haus zu realisieren.
Das Thema Energieunabhängigkeit und effizientes Heizen war noch nie gefragter und unübersichtlicher. Profitieren auch Sie.IT Dienstleistungen im Hausautomatisierungs- und Heizungsautomatisierungsbereich. Ich helfe Ihnen den effizienten Weg zum autonomen Haus zu realisieren. Das Thema Energieunabhängigkeit und effizientes Heizen war noch nie gefragter und unübersichtlicher. Profitieren auch Sie.… mehr anzeigen
Erneuerbare Energien und IT-Beratung + 4 Kenntnisse
Logo von Bechtle
Bechtle
Bechtle
4 Jahre 3 Monate4 Jahre 3 Monate
System Engineer Backup
System Engineer Backup
VollzeitVollzeit
Juli 2021Nov. 2022 · 1 Jahr 5 MonateJuli 2021Nov. 2022 · 1 Jahr 5 Monate
Fachinformatiker Systemintegration
Fachinformatiker Systemintegration
AzubiAzubi
Sept. 2018Juli 2021 · 2 Jahre 11 MonateSept. 2018Juli 2021 · 2 Jahre 11 Monate
Friedrichshafen, Baden-Württemberg, DeutschlandFriedrichshafen, Baden-Württemberg, Deutschland
AusbildungAusbildung
Logo von CVJM
FSJ
FSJ
CVJM · Freiwilliges Soziales JahrCVJM · Freiwilliges Soziales Jahr
Aug. 2017März 2018 · 8 MonateAug. 2017März 2018 · 8 Monate
Borkum, Niedersachsen, DeutschlandBorkum, Niedersachsen, Deutschland
AusbildungAusbildung
Elektronikschule Tettnang
Elektronikschule Tettnang
Fachinformatiker Systemintegration, InformatikFachinformatiker Systemintegration, Informatik
2018202120182021
Naturwissenschaftlich-Technische Akademie Isny
Naturwissenschaftlich-Technische Akademie Isny
Naturwissenschaftlich-Technische Akademie Isny
Assistent für Informations & Kommunikationstechnik, InformatikAssistent für Informations & Kommunikationstechnik, Informatik
2015201720152017
KenntnisseKenntnisse
IT-Betrieb
IT-Betrieb
LinkedIn Kenntnistest bestandenLinkedIn Kenntnistest bestanden
Bestätigen
IT-Beratung
IT-Beratung
Inhaber bei Haus & Heizungsautomatisierung Inhaber bei Haus & Heizungsautomatisierung
Bestätigen
Alle 10 Kenntnisse anzeigen
KurseKurse
VMCEA
VMCEA
Unternehmenslogo
Assoziiert mit Bechtle
Assoziiert mit Bechtle
Veeam Certified Engineer
Veeam Certified Engineer
VMCEVMCE
Unternehmenslogo
Assoziiert mit Bechtle
Assoziiert mit Bechtle
OrganisationenOrganisationen
Feuerwehr
Feuerwehr
Mitglied des Feuerwehrausschuss, Feuerwehrmann · März 2016Juli 2022Mitglied des Feuerwehrausschuss, Feuerwehrmann · März 2016Juli 2022
InteressenInteressen
UnternehmenUnternehmen
GruppenGruppen
NewsletterNewsletter
Hochschulen/BerufsschulenHochschulen/Berufsschulen
Logo von IBM
IBM
IBM
18.555.838 Follower:innen18.555.838 Follower:innen
Folgen
Logo von Hewlett Packard Enterprise
Hewlett Packard Enterprise
Hewlett Packard Enterprise
3.673.541 Follower:innen3.673.541 Follower:innen
Folgen

View file

@ -1,178 +0,0 @@
Nils Weiser
test
Nils Weiser
Direkter Kontakt1.
Co-Founder Codify AG, Software Developer
Codify
HTWG Hochschule Konstanz Technik, Wirtschaft und Gestaltung
Kreuzlingen, Thurgau, Schweiz Kontaktinfo
313 Kontakte
Tobias Müller, Jan Kaiser und 20 weitere gemeinsame KontakteTobias Müller, Jan Kaiser und 20 weitere gemeinsame Kontakte
Nachricht
Mehr
HighlightsHighlights
Unternehmenslogo
Nils Weiser folgt Ihrem Unternehmen auf LinkedIn
Nils Weiser folgt Ihrem Unternehmen auf LinkedIn
Nils Weiser kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.Nils Weiser kennt Ihre Marke und ist möglicherweise empfänglicher für eine Kontaktaufnahme.
Kostenlose Insights von LinkedIn Sales Navigator
Kostenlose Insights von LinkedIn Sales Navigator
Schalten Sie mehr Einblicke in Leads frei
Bessere Kontaktaufnahme mit vertriebsrelevanten Insights
Sales Navigator für 0 CHF erneut testen
1 Probemonat mit Support rund um die Uhr. Sie können jederzeit kündigen. Sie erhalten 7 Tage vor Ablauf der Probeversion eine entsprechende Erinnerung.
AktivitätenAktivitäten
319 Follower:innen319 Follower:innen
Beiträge
Kommentare
Bilder
9 „Beiträge“-Beiträge wurden geladen
Link zur Grafik von Nils Weiser anzeigen
Nils WeiserNils Weiser
• 1.1.
Co-Founder Codify AG, Software DeveloperCo-Founder Codify AG, Software Developer
3 Monate • Bearbeitet • vor 3 Monaten • Bearbeitet • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Just watched an insightful video from Y Combinator with Tom on "vibe coding" with AI tools. Here are the game-changing tips I found most valuable:
1. Start with a comprehensive plan before diving into code - work section by section
2. Use version control religiously (Git is your friend!)
3. Write high-level integration tests to catch regressions
4. For bugs, simply copy-paste error messages directly to the LLM
5. Create detailed instruction files for your AI coding assistant
6. Choose tech stacks with established conventions (like Rails) for better results
Personal experience:
when running into error loops of llm calls, stop it, and tell explicitly to use the browser tool to get more context.
We also use a tech stack which is well established (Frontend: react, backend: express).
Writing tests is crucial, especially when Windsurf come with a huge wave of changes to your code base 😃
Bonus: for security you can prompt it to act in a red/blue team manner and should audit your code, this should prevent the api key leaks im reading recently on linkedIn with vibe coded products
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
likeinsightful
3
like
Link zur Grafik von Nils Weiser anzeigen
Nils WeiserNils Weiser
• 1.1.
Co-Founder Codify AG, Software DeveloperCo-Founder Codify AG, Software Developer
5 Monate • Bearbeitet • vor 5 Monaten • Bearbeitet • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
MCP Tools für IDE's!
MCP ist der Standard, um AI zu enhancen, und ist jetzt auch für die meisten IDE's verfügbar!
Ich habe heute BraveSearch integriert, damit mein LLM immer auf die neuesten Informationen zugreifen kann ganz ohne lästiges Hin- und Herspringen zwischen Tools.
Nutzt ihr schon MCP Tools? Welche findet ihr am sinnvollsten?
Open Source:
https://smithery.ai/
https://glama.ai/mcp/tools
Hashtag#MCP Hashtag#AI Hashtag#IDEs Hashtag#OpenSource Hashtag#BraveSearch
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
Größere Bilddarstellung aktivieren,
likeinsightful
9
2 Kommentare
like
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von Codify
Co-Founder Codify AG, Software Developer
Co-Founder Codify AG, Software Developer
Codify · VollzeitCodify · Vollzeit
Feb. 2019Heute · 6 Jahre 7 MonateFeb. 2019Heute · 6 Jahre 7 Monate
Kreuzlingen, Thurgau, SchweizKreuzlingen, Thurgau, Schweiz
Logo von BMT - Business meets Technology AG
Full-Stack-Entwickler
Full-Stack-Entwickler
BMT Business meets Technology AG · VollzeitBMT Business meets Technology AG · Vollzeit
März 2018Jan. 2019 · 11 MonateMärz 2018Jan. 2019 · 11 Monate
Logo von T-Systems Schweiz
Studentischer Entwickler
Studentischer Entwickler
T-Systems Schweiz · TeilzeitT-Systems Schweiz · Teilzeit
Mai 2017Sept. 2017 · 5 MonateMai 2017Sept. 2017 · 5 Monate
Logo von timeghost
Web-Entwickler
Web-Entwickler
Köllisch Gesellschaft für Prozessmanagement mbH · PraktikumKöllisch Gesellschaft für Prozessmanagement mbH · Praktikum
Juli 2014Juni 2015 · 1 JahrJuli 2014Juni 2015 · 1 Jahr
Konstanz, Baden-Württemberg, DeutschlandKonstanz, Baden-Württemberg, Deutschland
AusbildungAusbildung
Logo von HTWG Hochschule Konstanz Technik, Wirtschaft und Gestaltung
HTWG Hochschule Konstanz Technik, Wirtschaft und Gestaltung
HTWG Hochschule Konstanz Technik, Wirtschaft und Gestaltung
InformatikInformatik
Bescheinigungen und ZertifikateBescheinigungen und Zertifikate
Logo von CrewAI
Multi AI Agent Systems
Multi AI Agent Systems
CrewAICrewAI
Ausgestellt: Aug. 2024Ausgestellt: Aug. 2024
AI Agent
Nils Weiser_badge.pdfNils Weiser_badge.pdf
Learn AI Agents
Learn AI Agents
Learn AI Agents
ScrimbaScrimba
Ausgestellt: Juni 2024Ausgestellt: Juni 2024
Zertifikats-ID: 2QEZCHUJ2DSTZertifikats-ID: 2QEZCHUJ2DST
Nachweis anzeigen
Alle 4 Bescheinigungen und Zertifikate anzeigen
KenntnisseKenntnisse
AI Agent
AI Agent
Unternehmenslogo
Multi AI Agent SystemsMulti AI Agent Systems
Bestätigen
REST-API
REST-API
LinkedIn Kenntnistest bestandenLinkedIn Kenntnistest bestanden
Bestätigen
Alle 11 Kenntnisse anzeigen
InteressenInteressen
UnternehmenUnternehmen
GruppenGruppen
NewsletterNewsletter
Hochschulen/BerufsschulenHochschulen/Berufsschulen
Logo von Microsoft
Microsoft
Microsoft
26.068.419 Follower:innen26.068.419 Follower:innen
Folgen
Logo von Google
Google
Google
38.360.676 Follower:innen

View file

@ -1,183 +0,0 @@
Till Schneider
er/ihm Verifizierungs-Badge hinzufügen
Expand your thinking - Memoro.ai
Memoro
Tägerwilen, Thurgau, Schweiz Kontaktinfo
500+ Kontakte
Offen für
Profil ergänzen
Profil verbessern
Ressourcen
Zeigen Sie, dass Sie offen für Jobangebote sind. Sie bestimmen, wer diesen Hinweis sieht.
Loslegen
Sie stellen ein? Teilen Sie Ihre Stellenanzeigen und ziehen Sie qualifizierte Talente an.
Loslegen
Präsentieren Sie Ihre Serviceleistungen in einem eigenen Abschnitt in Ihrem Profil, damit Ihr Unternehmen leichter zu finden ist.
Loslegen
Vorschläge für SieVorschläge für Sie
Nur für Sie sichtbar Nur für Sie sichtbar
Schildern Sie, wer Sie sind, wie Sie ticken und was Sie beruflich auszeichnetSchildern Sie, wer Sie sind, wie Sie ticken und was Sie beruflich auszeichnet
Die Profile von Mitgliedern mit einer Zusammenfassung werden bis zu 3,9 Mal häufiger angesehen.
Die Profile von Mitgliedern mit einer Zusammenfassung werden bis zu 3,9 Mal häufiger angesehen.
Zusammenfassung hinzufügen
AnalysenAnalysen
Nur für Sie sichtbar Nur für Sie sichtbar
43 Profilansichten
43 Profilansichten
Finden Sie heraus, wer Ihr Profil besucht hat.Finden Sie heraus, wer Ihr Profil besucht hat.
4 Beitrag-Impressions
4 Beitrag-Impressions
Sehen Sie sich an, wer auf Ihre Beiträge reagiert hat.Sehen Sie sich an, wer auf Ihre Beiträge reagiert hat.
Vergangene 7 TageVergangene 7 Tage
48 Mal in Suchen erschienen
48 Mal in Suchen erschienen
Finden Sie heraus, wie oft Sie in Suchen angezeigt wurden.Finden Sie heraus, wie oft Sie in Suchen angezeigt wurden.
Alle Analysen anzeigen
AktivitätenAktivitäten
601 Follower:innen601 Follower:innen
Beitrag erstellen
Beiträge
Kommentare
Artikel
9 „Beiträge“-Beiträge wurden geladen
Link zur Grafik von Till Schneider anzeigen
Till SchneiderTill Schneider
• SieSie
Expand your thinking - Memoro.aiExpand your thinking - Memoro.ai
3 Monate • vor 3 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Ist KI der Kreativitätskiller? Warum der Durchschnitt nicht reicht und unsere Zukunft von menschlicher Intuition, Geschmack und ja sogar Fehlern abhängt. Ein Plädoyer für weniger digitale Glätte und mehr echtes Leben.
… mehr
Jenseits des Durchschnitts: Warum unsere digitale Zukunft mehr menschliches Chaos braucht
Till Schneider
like
7
1 Kommentar
Profilfoto von Till Schneider
Till Schneider hat dies repostet
MemoroMemoro
219 Follower:innen219 Follower:innen
6 Monate • vor 6 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
Wir wünschen Euch allen ein frohes Neues 2025 - vor Allem das "Neu" stand bei uns die letzten zwei Monate an erster Stelle, da wir mit Hochdruck an Memoro 2.0 arbeiten.
Till Schneider und Tobias Müller waren dazu im Podcast programmier.bar zu Besuch und haben über Ihre Entwicklungserfahrungen berichtet: Low Code: Freiheit oder Limitierung?
Im Gespräch erzählen wir:
Low Code Boost: Wie wir dank Low Code unglaublich schnell prototypen und iterieren konnten.
Die Grenzen von Low Code: Warum wir uns entschieden haben, auf einen anderen Stack zu wechseln.
Unsere Erkenntnisse: Low Code ist genial für den schnellen Start aber wenn es ums Skalieren und eine perfekte User Experience geht, braucht es Flexibilität und Kontrolle.
Reinhören lohnt sich! Die ganze Folge gibts hier: https://lnkd.in/eYcd8kRq
Neugierig auf Memoro? Hier gehts zur App: https://lnkd.in/eazwPffG
Hashtag#LowCode Hashtag#NoCode Hashtag#Startup Hashtag#Produktentwicklung Hashtag#Podcast Hashtag#App Hashtag#Innovation Hashtag#Memoro Hashtag#Tools Hashtag#Digitalisierung
… mehr
168 Ig Fb Low Code Mit Till Schneider & Tobias Müller
Deep Dive 168 Low Code mit Till Schnei... | programmier.bar
programmier.bar
likecelebrate
22
1 Repost
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von Memoro
Geschäftsführer, Founder
Geschäftsführer, Founder
Memoro · VollzeitMemoro · Vollzeit
Juli 2023Heute · 2 Jahre 2 MonateJuli 2023Heute · 2 Jahre 2 Monate
Konstanz, Baden-Württemberg, Deutschland · HybridKonstanz, Baden-Württemberg, Deutschland · Hybrid
Start-up-Unternehmen und Softwareentwicklung + 7 Kenntnisse
Filmemacher
Filmemacher
Filmemacher
Till Jakob · SelbstständigTill Jakob · Selbstständig
Juli 2011Heute · 14 Jahre 2 MonateJuli 2011Heute · 14 Jahre 2 Monate
Tägerwilen, Thurgau, SchweizTägerwilen, Thurgau, Schweiz
Kameramann und Storytelling + 3 Kenntnisse
Logo von inlume
inlume
inlume
Vollzeit · 2 Jahre 11 MonateVollzeit · 2 Jahre 11 Monate
Tägerwilen, Thurgau, SchweizTägerwilen, Thurgau, Schweiz
Geschäftsführer
Geschäftsführer
Okt. 2020Aug. 2023 · 2 Jahre 11 MonateOkt. 2020Aug. 2023 · 2 Jahre 11 Monate
UX-Design
Co-Founder
Co-Founder
Okt. 2020Aug. 2023 · 2 Jahre 11 MonateOkt. 2020Aug. 2023 · 2 Jahre 11 Monate
Start-up-Unternehmen
AusbildungAusbildung
Logo von Duale Hochschule Baden-Württemberg
Duale Hochschule Baden-Württemberg
Duale Hochschule Baden-Württemberg
Bachelor of Arts - BA, MediendesignBachelor of Arts - BA, Mediendesign
2017202020172020
KenntnisseKenntnisse
Branding
Branding
Unternehmenslogo
Geschäftsführer, Founder bei MemoroGeschäftsführer, Founder bei Memoro
UX-Design
UX-Design
Unternehmenslogo
2 Erfahrungen bei Memoro und 1 weiteren Unternehmen2 Erfahrungen bei Memoro und 1 weiteren Unternehmen
Alle 21 Kenntnisse anzeigen
InteressenInteressen
Top VoicesTop Voices
UnternehmenUnternehmen
GruppenGruppen
NewsletterNewsletter
Hochschulen/BerufsschulenHochschulen/Berufsschulen
Lex Fridman
Lex Fridman
Lex Fridman
· 3.Kontakt 3. Grades
Research Scientist, MITResearch Scientist, MIT
1.709.773 Follower:innen1.709.773 Follower:innen
Follower:in
Simon Sinek
Simon Sinek
Simon Sinek
· 3.Kontakt 3. Grades
Optimist, New York Times bestselling author of "Start with Why" and "The Infinite Game", and founder of The Optimism CompanyOptimist, New York Times bestselling author of "Start with Why" and "The Infinite Game", and founder of The Optimism Company
8.629.584 Follower:innen

View file

@ -1,286 +0,0 @@
Tobias Müller
er/ihm Direkter Kontakt1.
Expand your thinking - Memoro.ai
Memoro
Ispringen, Baden-Württemberg, Deutschland Kontaktinfo
82 Kontakte
Alex Vasileva, Gernot Doriat und 56 weitere gemeinsame KontakteAlex Vasileva, Gernot Doriat und 56 weitere gemeinsame Kontakte
Nachricht
Mehr
HighlightsHighlights
Logo von Memoro
Sie sind beide bei Memoro beschäftigt.
Sie sind beide bei Memoro beschäftigt.
Tobias Müller hat 1 Monat vor Ihnen bei Memoro angefangen.Tobias Müller hat 1 Monat vor Ihnen bei Memoro angefangen.
Nachricht
InfoInfo
Hello out there !
My name is Tobias Müller and I would like to introduce myself briefly.
I define myself as a "full-stack developer" and really have a soft spot for everything new and innovative.
Influenced by my professional past and the foundation of a start-up, I have a strong independent way of thinking and a lot of passion in development.
Two principles are important in my life:
First, always try to broaden your horizon.
And second, always go one step further.
I'm really looking forward to hearing from you.
Best regards
Tobias MüllerHello out there ! My name is Tobias Müller and I would like to introduce myself briefly. I define myself as a "full-stack developer" and really have a soft spot for everything new and innovative. Influenced by my professional past and the foundation of a start-up, I have a strong independent way of thinking and a lot of passion in development. Two principles are important in my life: First, always try to broaden your horizon. And second, always go one step further. I'm really looking forward to hearing from you. Best regards Tobias Müller… mehr anzeigen
ServiceleistungenServiceleistungen
Webentwicklung • Entwicklung kundenspezifischer Software • App-Entwicklung • App-Entwicklung für Mobilgeräte • Entwicklung von Cloud-AnwendungenWebentwicklung • Entwicklung kundenspezifischer Software • App-Entwicklung • App-Entwicklung für Mobilgeräte • Entwicklung von Cloud-Anwendungen
Serviceleistungen anfordern
Alle Serviceleistungen anzeigen
AktivitätenAktivitäten
87 Follower:innen87 Follower:innen
9 „Beiträge“-Beiträge wurden geladen
Link zur Grafik von Tobias Müller anzeigen
Tobias MüllerTobias Müller
• 1.1.
Expand your thinking - Memoro.aiExpand your thinking - Memoro.ai
10 Monate • vor 10 Monaten • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
🔥♥️🪩🎉🥳🎈🥂🍹🍻🎤
MemoroMemoro
219 Follower:innen219 Follower:innen
Ein Jahr Memoro und 32 Jahre Tobi! 🎉🎂 Am Wochenende haben wir den Geburtstag unseres Mitgründers und IT-Zauberers Tobi gefeiert und gleichzeitig auf ein fantastisches erstes Jahr mit Memoro angestoßen! 🥳✨
In bester Gesellschaft, umgeben von unseren Lieblingsmenschen, haben wir ein unvergessliches Wochenende voller Spannung, Spaß und positiver Emotionen verbracht. 🤩🥂 Mit Memoro möchten wir mehr Raum für das Wichtigste im Leben schaffen die kostbare Zeit mit den Menschen, die unsere Arbeit und unser Leben bereichern. 💖🌟
Alles Gute zum Geburtstag, Tobi! 🎂🎈 Auf viele weitere Jahre voller Magie und gemeinsamer Erinnerungen! 🎊
Dieses Wochenende haben wir viele tolle Gespräche geführt und natürlich mit Memoro festgehalten.
Lade Dir Memoro kostenlos herunter: https://lnkd.in/eazwPffG
Tobias Müller Till Schneider Dirk Zimanky Ludwig Kaftan Albashir Mohamed Jose Ignacio Campos Domínguez
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
1/10
Größere Bilddarstellung aktivieren,
likelovecelebrate
9
1 Kommentar
Profilfoto von Tobias Müller
Tobias Müller hat dies repostet
MemoroMemoro
219 Follower:innen219 Follower:innen
1 Jahr • Bearbeitet • vor 1 Jahr • Bearbeitet • Alle Mitglieder und Nicht-Mitglieder von LinkedIn
🚀 Innovationsschub beim BW Startup Summit in Stuttgart!
Unser Team tauchte ein in eine Welt voller kreativer Ideen - von Mode bis Medizin. Highlights:
- Spannender Ideenaustausch mit Gleichgesinnten
- Till's Präsentation von Memoro auf dem Startup Festival
- Networking beim Kicker-Match (Fußballfieber inklusive!)
Unsere Key Learnings:
- Alex: "Die Startup-Welt ist bunt. Leidenschaft und Kreativität sind der Schlüssel zum Erfolg."
- Till: "Faszinierende Spezialisierungen. Mehr Vernetzung könnte Synergien schaffen."
- Tobi: "Startup-Events sind Magneten für inspirierende Gespräche."
Danke an alle Inspirationsquellen, besonders Julia Zimmermann, Timon Sutter (Eversion), Antje Freyth, Robert Rapp, Jan Marvin Wickert, Simone Harr, Dmitriy Sevkovych, Ira Romy Alice Stoll, Jürke Hartz (myScribe) und Kolja B..
, mit denen wir tolle Gespräche führen konnten, die natürlich mit Memoro festgehalten wurden. Lade Memoro kostenlos herunter: https://lnkd.in/eazwPffG
Was war euer letztes Startup-Event? Teilt eure Erfahrungen in den Kommentaren!
Hashtag#BWStartupSummit Hashtag#Innovation Hashtag#Networking Hashtag#StartupLife
… mehr
Größere Bilddarstellung aktivieren,
Keine alternative Textbeschreibung für dieses Bild vorhanden
1/4
Größere Bilddarstellung aktivieren,
likecelebrate
51
3 Kommentare
4 Reposts
Alle Beiträge anzeigen
BerufserfahrungBerufserfahrung
Logo von Memoro
Founder
Founder
Memoro · VollzeitMemoro · Vollzeit
Juni 2023Heute · 2 Jahre 3 MonateJuni 2023Heute · 2 Jahre 3 Monate
Founder bei MemoroFounder bei Memoro
Freelancer
Freelancer
Freelancer
Tobias Müller Software Entwicklung · SelbstständigTobias Müller Software Entwicklung · Selbstständig
Juni 2021Juni 2023 · 2 Jahre 1 MonatJuni 2021Juni 2023 · 2 Jahre 1 Monat
UI und JavaScript + 22 Kenntnisse
Backend Entwicklung
Backend Entwicklung
Backend Entwicklung
DEKRA SE · FreiberuflichDEKRA SE · Freiberuflich
Nov. 2021Aug. 2022 · 10 MonateNov. 2021Aug. 2022 · 10 Monate
Global CMS Redesign
• Projekt Initiierung mit npm workspaces
• Backend Entwicklung mit Nest.js
• Planung der Azure Cloud Architektur
• Cloud Migration zu Azure Cloud
• Einrichten der Azure DevOps Pipeline (CI/CD)
Knowledge:
TypeScript, Node.js, npm workspaces, Docker, Cloud, Agile, CI/CD
Products:
JetBrains, Docker, Azure Cloud, Azure DevOps, Conflunce, JIRA, Bitbucket, Git, Notion, Ubuntu, Nest.js, Elastic Search, OWASPGlobal CMS Redesign • Projekt Initiierung mit npm workspaces • Backend Entwicklung mit Nest.js • Planung der Azure Cloud Architektur • Cloud Migration zu Azure Cloud • Einrichten der Azure DevOps Pipeline (CI/CD) Knowledge: TypeScript, Node.js, npm workspaces, Docker, Cloud, Agile, CI/CD Products: JetBrains, Docker, Azure Cloud, Azure DevOps, Conflunce, JIRA, Bitbucket, Git, Notion, Ubuntu, Nest.js, Elastic Search, OWASP… mehr anzeigen
JavaScript und Databases + 15 Kenntnisse
Startup Founder
Startup Founder
Startup Founder
compan.one · Vollzeitcompan.one · Vollzeit
Sept. 2020Okt. 2021 · 1 Jahr 2 MonateSept. 2020Okt. 2021 · 1 Jahr 2 Monate
Software für Führungskräfte
• Begleitung des Projektes als Teil des Design-Thinking-Teams
• Einsatz verschiedenster Prototypen (Mockup, Click-Dummy, concierge MVP)
• Entwicklung einer Progressiv Web App inkl. CMS Backend
• Fokus auf UsablititySoftware für Führungskräfte • Begleitung des Projektes als Teil des Design-Thinking-Teams • Einsatz verschiedenster Prototypen (Mockup, Click-Dummy, concierge MVP) • Entwicklung einer Progressiv Web App inkl. CMS Backend • Fokus auf Usablitity… mehr anzeigen
UI und JavaScript + 26 Kenntnisse
Teamleader - Software Development
Teamleader - Software Development
Teamleader - Software Development
Conecpt Hero · TeilzeitConecpt Hero · Teilzeit
Apr. 2019Okt. 2020 · 1 Jahr 7 MonateApr. 2019Okt. 2020 · 1 Jahr 7 Monate
Heilbronn (Landkreis), Baden-Württemberg, DeutschlandHeilbronn (Landkreis), Baden-Württemberg, Deutschland
Software Entwicklung mit dem Schwerpunkt Prototyping
Teamleitung, Projektmanagement und Optimierung der Unternehmensprozesse
Knowledge:
JavaScript, TypeScript, Node.js, Express.js, Vue.js, React.js, HTML, CSS, SQL, PWA, Java, Python, C#, UI, UX, Usability Testing, Unity3D, VR, AR, Agile, Prototyping, Capacitor, Ionic, Cordova
Products:
Docker, Gitlab, Bitbucket, Git, Ubuntu, ARKit, ARCore, AR Foundation, JetBrains IDE, VS Code, Android, iOS, WearOS, Firebase, Google CloudSoftware Entwicklung mit dem Schwerpunkt Prototyping Teamleitung, Projektmanagement und Optimierung der Unternehmensprozesse Knowledge: JavaScript, TypeScript, Node.js, Express.js, Vue.js, React.js, HTML, CSS, SQL, PWA, Java, Python, C#, UI, UX, Usability Testing, Unity3D, VR, AR, Agile, Prototyping, Capacitor, Ionic, Cordova Products: Docker, Gitlab, Bitbucket, Git, Ubuntu, ARKit, ARCore, AR Foundation, JetBrains IDE, VS Code, Android, iOS, WearOS, Firebase, Google Cloud… mehr anzeigen
UI und JavaScript + 35 Kenntnisse
Alle 12 Berufserfahrungen anzeigen
AusbildungAusbildung
Logo von Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Bachelor of Science - BS, Software EngineeringBachelor of Science - BS, Software Engineering
März 2016Sept. 2020März 2016Sept. 2020
Bachelor of Science Software Engineering mit der Vertiefung Games EngineeringBachelor of Science Software Engineering mit der Vertiefung Games Engineering
Linux und Full-Stack + 37 Kenntnisse
Logo von Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Bachelor of Science - BS, Software EngineeringBachelor of Science - BS, Software Engineering
2016202020162020
Linux und Full-Stack + 37 Kenntnisse
Alle 7 Ausbildungen anzeigen
Bescheinigungen und ZertifikateBescheinigungen und Zertifikate
Logo von Vabro.ai and VMEdu.com (Scrum/Kanban/AI/Business Analysis/OKRs/Six Sigma/Sales and Marketing etc.)
Scrum Fundamentals Certified
Scrum Fundamentals Certified
Vabro.ai and VMEdu.com (Scrum/Kanban/AI/Business Analysis/OKRs/Six Sigma/Sales and Marketing etc.)Vabro.ai and VMEdu.com (Scrum/Kanban/AI/Business Analysis/OKRs/Six Sigma/Sales and Marketing etc.)
Ausgestellt: Juni 2017Ausgestellt: Juni 2017
Zertifikats-ID: 582311Zertifikats-ID: 582311
Scrum
Startup Simulation - STARTUP | edu
Startup Simulation - STARTUP | edu
Startup Simulation - STARTUP | edu
STARTUP | eduSTARTUP | edu
Ausgestellt: Mai 2017Ausgestellt: Mai 2017
Prototyping und Startup
Alle 5 Bescheinigungen und Zertifikate anzeigen
EhrenamtEhrenamt
Voluntary assistant for logistics and transport
Voluntary assistant for logistics and transport
Voluntary assistant for logistics and transport
DreamCenter Sozialwerk e.V.DreamCenter Sozialwerk e.V.
März 2016Feb. 2017 · 1 JahrMärz 2016Feb. 2017 · 1 Jahr
ArmutsbekämpfungArmutsbekämpfung
Voluntary assistant for logistics and transportVoluntary assistant for logistics and transport
Logo von Lernstiftung Hück
Voluntary position as Information Technology Coordinator
Voluntary position as Information Technology Coordinator
Lernstiftung HückLernstiftung Hück
Feb. 2016 · 1 MonatFeb. 2016 · 1 Monat
Unterstützung benachteiligter GruppenUnterstützung benachteiligter Gruppen
- Voluntary position as Information Technology Coordinator
- System Installation
- Rights Management
- Network Configuration
- Support in the supervision- Voluntary position as Information Technology Coordinator - System Installation - Rights Management - Network Configuration - Support in the supervision… mehr anzeigen
KenntnisseKenntnisse
Git
Git
7 Erfahrungen bei Tobias Müller Software Entwicklung und 6 weiteren Unternehmen7 Erfahrungen bei Tobias Müller Software Entwicklung und 6 weiteren Unternehmen
Unternehmenslogo
7 Ausbildungserfahrungen bei Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik und 4 weiteren Ausbildungsstätten7 Ausbildungserfahrungen bei Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik und 4 weiteren Ausbildungsstätten
Bestätigen
Frontend
Frontend
6 Erfahrungen bei Tobias Müller Software Entwicklung und 5 weiteren Unternehmen6 Erfahrungen bei Tobias Müller Software Entwicklung und 5 weiteren Unternehmen
Unternehmenslogo
7 Ausbildungserfahrungen bei Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik und 4 weiteren Ausbildungsstätten7 Ausbildungserfahrungen bei Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik und 4 weiteren Ausbildungsstätten
Bestätigen
Alle 48 Kenntnisse anzeigen
Auszeichnungen/PreiseAuszeichnungen/Preise
Letter of Recommendation for Tobias Müller
Letter of Recommendation for Tobias Müller
Von: Prof. Dr. Tim Reichert · Juni 2020Von: Prof. Dr. Tim Reichert · Juni 2020
Logo von Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Assoziiert mit Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Assoziiert mit Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Letter of Recommendation for Tobias Müller
Heilbronn, 17 June 2020
Prof. Dr. Tim Reichert
Games Engineering
Heilbronn University
of Applied Sciences
Max-Planck-Str. 39
74081 Heilbronn, Germany
> > Document only on request<<Letter of Recommendation for Tobias Müller Heilbronn, 17 June 2020 Prof. Dr. Tim Reichert Games Engineering Heilbronn University of Applied Sciences Max-Planck-Str. 39 74081 Heilbronn, Germany >>Document only on request<<… mehr anzeigen
> > Winner - Fujitsu Botathon
> > Winner - Fujitsu Botathon
> > Juli 2019Juli 2019
1. Place at Automation Inspiration University Botathon
Topic: Explore Anywhere1. Place at Automation Inspiration University Botathon Topic: Explore Anywhere… mehr anzeigen
Alle 4 Auszeichnungen/Preise anzeigen
OrganisationenOrganisationen
Hochschule Heilbronn
Hochschule Heilbronn
Faculty council IT · Sept. 2017Feb. 2018 Faculty council IT · Sept. 2017Feb. 2018
Logo von Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Assoziiert mit Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Assoziiert mit Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Studentischer Vertreter (Gemäß LHG §25)Studentischer Vertreter (Gemäß LHG §25)
Hochschule Heilbronn
Hochschule Heilbronn
Student council IT · März 2017Aug. 2017Student council IT · März 2017Aug. 2017
Logo von Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Assoziiert mit Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Assoziiert mit Hochschule Heilbronn - Hochschule für Technik, Wirtschaft und Informatik
Member of the student council IT.
Implementation of projects for quality assurance of teaching and studies.Member of the student council IT. Implementation of projects for quality assurance of teaching and studies.… mehr anzeigen
Alle 3 Organisationen anzeigen
InteressenInteressen
UnternehmenUnternehmen
Hochschulen/BerufsschulenHochschulen/Berufsschulen
Logo von heise
heise
heise
24.100 Follower:innen24.100 Follower:innen
Folgen
Logo von Stack Overflow
Stack Overflow
Stack Overflow
1.580.636 Follower:innen1.580.636 Follower:innen

View file

@ -1,68 +0,0 @@
# Nils Weiser
## CTO bei Memoro
Nils Weiser ist seit Mai 2025 CTO bei Memoro und bringt eine einzigartige Kombination aus unternehmerischer Erfahrung und technischer Leidenschaft mit. Als Co-Founder der Codify AG seit 2019 hat er gelernt, wie man technologische Visionen in funktionierende Produkte verwandelt durch viel Ausprobieren und gelegentliches Scheitern.
## Der Entdecker-Entwickler
"We are at our human finest, dancing with our minds, when there are more choices than ten, even twenty different ways to go, all but one bound to be wrong, and the richness of the selection in such situations can lift us onto totally new ground."\* Dieses Zitat bringt Nils' Philosophie auf den Punkt: Trial and Error ist für ihn kein bloßes Verfahren, sondern eine Kunstform — ein Tanz zwischen Irrtum und Erkenntnis.
Seine Reise begann früh mit 16 programmierte er seinen ersten Taschenrechner in C++. Was als Neugier begann, wurde zu einer lebenslangen Leidenschaft für das Erkunden technischer Möglichkeiten. Diese Leidenschaft führte ihn dazu, an einigen der größten Apps der Schweiz mitzuarbeiten und sogar für das Bundesamt zu entwickeln.
Als Co-Founder der Codify AG in Kreuzlingen sammelt Nils seit über 6 Jahren Erfahrung im Aufbau von Software-Unternehmen. Diese unternehmerische Perspektive kombiniert mit seiner technischen Experimentierfreude macht ihn zum idealen CTO für Memoros nächste Wachstumsphase.
## Technische Expertise
### Full-Stack mit Experimentierfreude
Mit seiner Trial-and-Error-Mentalität hat sich Nils ein breites technisches Spektrum erarbeitet:
```javascript
// Nils' Approach to Tech
function solveProblem(challenge, availableTools) {
const myApproach = "trial_and_error";
const timeToLearn = "as_long_as_it_takes";
const bestTool = findOptimalSolution(challenge, availableTools);
// Whether it's Rust, Go, Python, TypeScript, or that new
// framework everyone's talking about...
const result = experiment(bestTool)
.then(() => "got it working!")
.catch((error) => {
learnFromMistakes(error);
const remainingTools = availableTools.filter((tool) => tool !== bestTool);
return solveProblem(challenge, remainingTools);
});
return result; // 🚀
}
// Current toolkit (but always expanding):
const expertise = {
frontend: ["Angular", "React", "Vue", "plain", "whatever_works"],
backend: [
"Node.js",
"SpringBoot",
"GO",
"Express",
"FastAPI",
"if_needed_anything",
],
ai: ["MCP Tools", "AI Agents", "LLM Integration"],
testing: ["Jest", "Cypress", "the_art_of_breaking_things"],
devops: ["Git", "Docker", "CI/CD", "Terraform", "making_it_work_everywhere"],
philosophy: ["trial_and_error", "fail_fast_learn_faster"],
};
```
_"Die beste Sprache ist die, die das Problem löst. Die beste Library ist die, die funktioniert. Der beste Ansatz ist der, der nach ein paar Fehlversuchen zum Ziel führt."_
---
**Zitat-Referenz:**
- **Autor**: Lewis Thomas
- **Werk**: Essay _„Computers"_ in _The Medusa and the Snail: More Notes of a Biology Watcher_ (1974, überarbeitete Ausgabe 1979)
- **Zitaterweiterung**: Auch aufgeführt als Teil von _A Long Line of Cells: Collected Essays_

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 MiB

View file

@ -1,254 +0,0 @@
Basierend auf der
Memoro-Website und
Best Practices für
SaaS-Landingpages,
hier sind sinnvolle
A/B-Tests:
🎯 Empfohlene
A/B-Tests für Memoro
1. Navigation
Download-Button 🔝
Hypothese: Ein
prominenterer
Download-Button
erhöht Conversions
Varianten:
- A: "App
herunterladen"
(rechts, normal)
- B: "Kostenlos
testen" (mit Badge
"Neu!")
- C: Pulsierender
Button mit Icon
Metriken:
Download-Klicks,
Scroll-Verhalten
---
2.
Pricing-Darstellung
💰
Hypothese:
Verschiedene
Pricing-Layouts
beeinflussen
Kaufentscheidung
Varianten:
- A: Grid-Layout
(aktuell)
- B:
Vergleichstabelle
- C: Slider
(Monatlich/Jährlich
Toggle)
Metriken:
Time-on-Page,
Plan-Auswahl,
Conversions
---
3. Social Proof
Position ⭐
Hypothese:
Testimonials direkt
nach Hero erhöhen
Vertrauen
Varianten:
- A: Testimonials
unten (aktuell)
- B:
Mini-Testimonials
direkt nach Hero
- C: Floating
Testimonial-Ticker
Metriken:
Scroll-Tiefe,
Verweildauer,
Conversions
---
4. Video vs. Demo 🎥
Hypothese: Live-Demo
ist überzeugender als
Video
Varianten:
- A: "Video ansehen"
(aktuell)
- B: "Interaktive
Demo"
- C: "30-Sek
Kurzvideo" (autoplay)
Metriken:
Engagement-Rate,
Demo-Completions
---
5. Urgency/Scarcity
Test ⏰ -> Wichtig: SOWAS WOLLEN WIR NICHT MACHEN - KEINERLEI DARK PATTERNS ETC
Hypothese: Zeitliche
Begrenzung erhöht
Conversions
Varianten:
- A: Keine Urgency
- B: "Nur noch heute:
50% Rabatt"
- C: "Begrenzt: Erste
100 Nutzer
kostenlos"
Metriken:
Conversion-Rate,
Absprungrate
---
6.
Feature-Priorisierung
📋
Hypothese:
KI-Features sind
wichtiger als
Datenschutz
Varianten:
- A: Datenschutz
zuerst
- B: KI-Features
zuerst
- C: Use-Cases zuerst
Metriken: Engagement,
Feature-Klicks
---
7. Mobile-First Hero
📱
Hypothese:
App-Screenshots
performen besser als
Illustrations
Varianten:
- A: Aktuelle
Illustration
- B: iPhone-Mockup
mit App
- C: Animiertes
App-Demo
Metriken: Mobile
Conversions, Bounce
Rate
---
8. Sprache & Tone 💬
Hypothese:
Persönlichere
Ansprache erhöht
Engagement
Varianten:
- A: "Ihre Gedanken"
(formell)
- B: "Deine Ideen"
(informell)
- C: "Sprechen Sie
einfach drauflos"
(aktiv)
Metriken:
Verweildauer,
Engagement
---
9. Trust-Badges 🛡️
Hypothese:
Sicherheits-Badges
erhöhen Vertrauen
Varianten:
- A: Keine Badges
- B: "DSGVO-konform"
+ "SSL-verschlüsselt"
- C: "TÜV-geprüft" +
Kundenzahlen
Metriken:
Conversion-Rate,
Trust-Umfragen
---
10. Onboarding-Flow
🚀
Hypothese: Geführtes
Onboarding erhöht
Aktivierung
Varianten:
- A: Direkt zur App
- B: 3-Schritt
Tutorial
- C: Persönliche Demo
buchen
Metriken:
Aktivierungsrate,
7-Tage Retention
---
📊
Priorisierungs-Matrix
| Test |
Aufwand | Impact |
Empfehlung |
|-------------------|
---------|--------|--
----------|
| Navigation Button |
Niedrig | Hoch |
⭐⭐⭐ |
| Pricing Layout |
Mittel | Hoch |
⭐⭐⭐ |
| Video vs Demo |
Hoch | Mittel |
⭐⭐ |
| Trust Badges |
Niedrig | Mittel |
⭐⭐ |
| Mobile Hero |
Mittel | Hoch |
⭐⭐⭐ |
🚀 Nächste Schritte
1. Navigation-Button
Test (einfach &
high-impact)
2. Pricing-Layout
(wichtig für
Conversion)
3. Mobile-Hero (50%+
Traffic ist mobil)

View file

@ -1,104 +0,0 @@
# Hero Design Verbesserungen
## 🎨 Aktuelle Design-Probleme
1. **Social Proof Box**: Zu dunkel und verschmilzt mit dem Hintergrund
2. **Trust Badges**: Zu klein und unauffällig
3. **Bild-Qualität**: Das Bild wirkt etwas dunkel/unscharf
4. **Visueller Fluss**: Die Elemente wirken noch nicht optimal verbunden
5. **CTAs**: Könnten noch mehr hervorstechen
## 💡 Design-Verbesserungsvorschläge
### 1. Social Proof Redesign
```css
/* Heller, auffälliger Hintergrund */
background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
border: 1px solid rgba(255,255,255,0.2);
backdrop-filter: blur(10px);
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
```
### 2. Trust Badges Enhancement
- Größere Icons und Text
- Eigene Cards für jeden Badge
- Leichter Hover-Effekt
- Bessere Spacing
### 3. Hero Image Improvements
- Overlay mit Gradient für besseren Kontrast
- Subtle Animation beim Laden
- Optional: Mehrere Bilder im Wechsel
### 4. Typography & Spacing
- Größerer Zeilenabstand für bessere Lesbarkeit
- Stärkerer Kontrast zwischen Headline und Subtitle
- Mehr Whitespace zwischen Elementen
### 5. CTA Buttons Enhancement
- Primär-Button: Stärkerer Glow-Effekt
- Sekundär-Button: Besserer Kontrast
- Micro-interactions beim Hover
### 6. Animations & Effects
- Fade-in Animation für alle Elemente
- Parallax-Effekt für das Bild
- Smooth reveal beim Scrollen
## 🚀 Quick Implementation
### Sofort umsetzbar:
1. Social Proof Box aufhellen
2. Trust Badges vergrößern und besser positionieren
3. CTA Buttons optimieren
4. Micro-Copy prominenter machen
### Mittelfristig:
1. Animationen hinzufügen
2. Bild-Overlay optimieren
3. Mobile Optimierungen
## 📐 Konkrete CSS-Änderungen
### Social Proof Box
```astro
<div class="mt-8 p-6 bg-gradient-to-br from-white/10 to-white/5 rounded-xl border border-white/20 backdrop-blur-sm shadow-lg">
```
### Trust Badges
```astro
<div class="flex flex-wrap gap-6 mt-10 justify-center md:justify-start">
{trustBadges.map((badge) => (
<div class="flex items-center gap-3 px-4 py-2 bg-white/5 rounded-lg border border-white/10 hover:bg-white/10 transition-all">
<span class="text-2xl">{badge.icon}</span>
<span class="text-sm font-medium text-text-primary">{badge.text}</span>
</div>
))}
</div>
```
### CTA Buttons
```css
/* Primary Button mit Glow */
.bg-primary {
box-shadow: 0 4px 20px rgba(255, 193, 7, 0.3);
transition: all 0.3s ease;
}
.bg-primary:hover {
box-shadow: 0 6px 30px rgba(255, 193, 7, 0.5);
transform: translateY(-2px);
}
```
### Hero Image Container
```astro
<div class="relative order-2 md:order-1 overflow-hidden rounded-2xl">
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent z-10"></div>
<img
src={image}
alt={title}
class="w-full h-auto object-cover shadow-2xl transform hover:scale-105 transition-transform duration-700"
/>
</div>
```

View file

@ -1,231 +0,0 @@
# Hero Section Quick Wins - Umsetzungskonzept
## 🎯 Übersicht
Dieses Dokument beschreibt die sofort umsetzbaren Verbesserungen für die Hero-Section der Memoro Landing Page. Alle Vorschläge sind darauf ausgelegt, mit minimalem Aufwand maximalen Impact zu erzielen.
## 1. Headlines & Subtitles Optimierung
### Deutsche Headlines (3 Varianten zum Testen)
#### Variante A: Nutzen-fokussiert
```
Headline: "Verwandeln Sie jedes Gespräch in verwertbares Wissen"
Subtitle: "KI-gestützte Gesprächsdokumentation, die mitdenkt. Sparen Sie 3+ Stunden pro Woche."
```
#### Variante B: Problem-fokussiert
```
Headline: "Nie wieder wichtige Details aus Meetings verlieren"
Subtitle: "Memoro dokumentiert, strukturiert und erinnert während Sie sich aufs Wesentliche konzentrieren."
```
#### Variante C: Zeitersparnis-fokussiert
```
Headline: "3 Stunden pro Woche zurückgewinnen"
Subtitle: "Lassen Sie KI Ihre Gespräche dokumentieren. Automatisch. Präzise. DSGVO-konform."
```
### Englische Headlines (3 Varianten zum Testen)
#### Variante A: Benefit-focused
```
Headline: "Turn Every Conversation Into Actionable Insights"
Subtitle: "AI-powered documentation that thinks along. Save 3+ hours every week."
```
#### Variante B: Problem-focused
```
Headline: "Never Miss Important Meeting Details Again"
Subtitle: "Memoro captures, structures, and reminds while you focus on what matters."
```
#### Variante C: Time-saving focused
```
Headline: "Get Back 3 Hours Every Week"
Subtitle: "Let AI document your conversations. Automatically. Accurately. GDPR-compliant."
```
## 2. CTA Micro-Copy
### Primärer CTA
**Button Text**: "Jetzt kostenlos starten" / "Start Free Now"
**Micro-Copy darunter**:
- "✓ Keine Kreditkarte erforderlich"
- "✓ In 30 Sekunden eingerichtet"
- "✓ 14 Tage kostenlos testen"
### Sekundärer CTA
**Button Text**: "Demo ansehen (2 Min)" / "Watch Demo (2 min)"
**Micro-Copy**: "Sehen Sie Memoro in Aktion"
## 3. Trust-Badges & Sicherheitssignale
### Badge-Leiste unter den CTAs
```
[🔒 DSGVO-konform] [🇩🇪 Made in Germany] [🛡️ SSL-verschlüsselt] [✓ ISO 27001]
```
### Alternativ als Text
"Ihre Daten sind sicher: DSGVO-konform • Ende-zu-Ende verschlüsselt • Server in Deutschland"
## 4. Social Proof Integration
### Option A: Bewertungszeile
```
⭐⭐⭐⭐⭐ 4.9/5 basierend auf 127 Bewertungen
"Die beste Investition in unsere Produktivität" - Thomas M., Geschäftsführer
```
### Option B: Nutzer-Statistik
```
Bereits 2.500+ Professionals sparen Zeit mit Memoro
Über 50.000 Stunden Gespräche erfolgreich dokumentiert
```
### Option C: Logo-Leiste
```
"Vertraut von Teams bei:"
[Siemens Logo] [SAP Logo] [Bosch Logo] [Mercedes Logo] [Telekom Logo]
```
## 5. Urgency/Scarcity Elemente (Optional)
### Zeitlich begrenzt
```
🎯 Black Friday Special: 50% Rabatt auf alle Pläne nur noch 48 Stunden
```
### Begrenzte Plätze
```
🚀 Early Access: Nur noch 23 kostenlose Beta-Plätze verfügbar
```
## 6. Implementation Details
### HeroSection.astro Anpassungen
1. **Micro-Copy Component** hinzufügen:
```astro
{microCopy && (
<div class="flex items-center gap-2 text-sm text-gray-400 mt-2">
<span class="text-green-500"></span>
<span>{microCopy}</span>
</div>
)}
```
2. **Trust Badges Component**:
```astro
<div class="flex flex-wrap gap-4 mt-8 opacity-70">
<div class="flex items-center gap-2">
<span class="text-lg">🔒</span>
<span class="text-sm">{t('hero.trust.gdpr')}</span>
</div>
<div class="flex items-center gap-2">
<span class="text-lg">🇩🇪</span>
<span class="text-sm">{t('hero.trust.madeInGermany')}</span>
</div>
<div class="flex items-center gap-2">
<span class="text-lg">🛡️</span>
<span class="text-sm">{t('hero.trust.encrypted')}</span>
</div>
</div>
```
3. **Social Proof Component**:
```astro
<div class="mt-6 p-4 bg-white/5 rounded-lg border border-white/10">
<div class="flex items-center gap-2 mb-2">
<div class="flex text-yellow-400">
{"⭐".repeat(5)}
</div>
<span class="text-white font-semibold">4.9/5</span>
<span class="text-gray-400 text-sm">({reviewCount} Bewertungen)</span>
</div>
<p class="text-sm text-gray-300 italic">"{testimonialQuote}"</p>
<p class="text-xs text-gray-400 mt-1"> {testimonialAuthor}</p>
</div>
```
## 7. A/B Testing Strategie
### Test 1: Headlines
- Control: Aktuelle Headline
- Variante A: Nutzen-fokussiert
- Variante B: Problem-fokussiert
- Variante C: Zeitersparnis-fokussiert
### Test 2: Social Proof
- Control: Keine Social Proof
- Variante A: Bewertungszeile
- Variante B: Nutzer-Statistik
- Variante C: Logo-Leiste
### Test 3: Micro-Copy
- Control: Kein Micro-Copy
- Variante A: "Keine Kreditkarte erforderlich"
- Variante B: Alle 3 Punkte
## 8. Tracking & Erfolgsmessung
### KPIs
1. **Click-Through-Rate (CTR)** auf primären CTA
2. **Conversion Rate** zu Registrierung
3. **Bounce Rate** der Landing Page
4. **Time on Page**
5. **Scroll Depth**
### Event Tracking
```javascript
// Hero View
gtag('event', 'hero_view', {
'variant': currentVariant,
'headline': headlineText
});
// CTA Click
gtag('event', 'hero_cta_click', {
'button': 'primary',
'variant': currentVariant,
'position': 'hero'
});
// Trust Badge Hover
gtag('event', 'trust_badge_hover', {
'badge': badgeType
});
```
## 9. Mobile Optimierungen
### Kürzere Mobile Headlines
```
Desktop: "Verwandeln Sie jedes Gespräch in verwertbares Wissen"
Mobile: "Gespräche in Wissen verwandeln"
Desktop: "3 Stunden pro Woche zurückgewinnen"
Mobile: "3h/Woche sparen"
```
### Angepasste Layouts
- Trust Badges: 2x2 Grid auf Mobile
- Social Proof: Kompaktere Darstellung
- CTAs: Full-width auf Mobile
## 10. Nächste Schritte
1. **Sofort (diese Woche)**:
- [ ] Neue Headlines in home.mdx implementieren
- [ ] Micro-Copy zu CTAs hinzufügen
- [ ] Trust Badges einbauen
- [ ] A/B Test erweitern
2. **Kurzfristig (nächste 2 Wochen)**:
- [ ] Social Proof Komponente entwickeln
- [ ] Logo-Leiste designen
- [ ] Mobile Optimierungen
3. **Follow-up**:
- [ ] Erste Test-Ergebnisse analysieren
- [ ] Gewinner-Varianten ausrollen
- [ ] Neue Test-Hypothesen entwickeln

View file

@ -1,145 +0,0 @@
08.08.2025
{"memo_number":13896,
"memo_entry_number":225932,
"time_recorded":"5363:20:20.815 (hh:mm:ss,ms)",
"transcript_words":46.314.389,
"user_number":2553}
28.03.2025
Memos:11.134
"memo_entry_number":176033,"
time_recorded":"4062:08:07.633 (hh:mm:ss,ms)","
transcript_words":35845740,"
user_number":2009}
03.03.2025
Memos: 10.405,
"memo_entry_number":162540,
time_recorded":"3735:49:37.421
(hh:mm:ss,ms)",
Wörter: 33.177.948
Users :1828
04.02.2025
{"memo_number":9392,
"memo_entry_number":143793,"
time_recorded":"3370:57:54.073 (hh:mm:ss,ms)",
202260 Minutes
"transcript_words":30007331,"user_number":1638}
27.01.2025
69 Paying Users
12 Android
56 iOS
Länder:
DE - 62
CH - 2
AT - 2
US - 1
Sweden - 1
Finnland - 1
21.01.2025
Über 9000 Memos aufgenommen
{"memo_number":9054,"memo_entry_number":137677,"time_recorded":"3145:37:24.003 (hh:mm:ss,ms)","transcript_words":28213286,"user_number":1564}
14.01.2025
1504 Nutzer
MAUs:
495 Users (RevenueCat)
281 Users (Google Analytics)
151 Users (Admin App)
309 Durchschnitt
{"memo_number":8782,"memo_entry_number":133301,"time_recorded":"3036:02:55.864 (hh:mm:ss,ms)","transcript_words":27319354,"user_number":1504}
10.12.2024
1310 Nutzer
8200 erstellte Memos
2800 Stunden Aufnahme
25 Millionen gesprochene Wörter
{"memo_number":8200,"memo_entry_number":122550,"time_recorded":"2834:35:37.030 (hh:mm:ss,ms)","transcript_words":25693505,"user_number":1310}
26.11.2024
Memos: 7852
Stunden: 2708 h
Wörter: 24.555.821
{"memo_number":7852,"memo_entry_number":116352,"time_recorded":"2708:35:00.093 (hh:mm:ss,ms)","transcript_words":24555821,"user_number":1238}
06.11.2024
Words: 22.513.813
Hours: 2471
{"memo_number":7237,"memo_entry_number":105938,"time_recorded":"2471:20:49.687 (hh:mm:ss,ms)","transcript_words":22513813,"user_number":1126}
16.10.2024
{"memo_number":6677,"memo_entry_number":95212,"time_recorded":"2213:26:39.882 (hh:mm:ss,ms)","transcript_words":20363477,"user_number":1031}
07.10.2024
Memos: 6460Time: 2105:58 (Stunden)Minuten: 126358 Minuten
Words: 19444341
Users: 978
{"memo_number":6460,"memo_entry_number":90728,"time_recorded":"2105:58:07.954 (hh:mm:ss,ms)","transcript_words":19444341,"user_number":978}
01.10.2024
{"memo_number":6389,
"memo_entry_number":89327,
"time_recorded":"2075:02:10.580
2075 h in minuten
(hh:mm:ss,ms)","transcript_words":19226681,"user_number":953}
24.08.2024
09.08.2024
{"memo_number":5495,"memo_entry_number":72678,"time_recorded":"1640:08:14.997 (hh:mm:ss,ms)","transcript_words":15770544,"user_number":699}
24.07.2024
{"memo_number":5235,"memo_entry_number":68122,"time_recorded":"1507:57:39.116 (hh:mm:ss,ms)","transcript_words":14694220,"user_number":640}
24.07.2024 - 02:30
{"memo_number":5222,"memo_entry_number":67964,"time_recorded":"1503:55:22.022 (hh:mm:ss,ms)","transcript_words":14.664.072,"user_number":639} Durchschnittlichte Wörtanzahl pro Stunde: 10.000

Some files were not shown because too many files have changed in this diff Show more