feat: rename mukke to music, add cover art upload via mana-media

Rename the music module from "Mukke" to "Music" across the entire
codebase: API routes, web app module, shared packages, search provider,
dashboard widgets, i18n keys, app registry, and route paths.

Add POST /api/v1/music/cover/upload endpoint that uploads cover art
images through mana-media for deduplication, thumbnails, and Photos
gallery visibility.

Dexie table names (mukkePlaylists, mukkeProjects) kept unchanged to
preserve existing IndexedDB data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-05 15:25:34 +02:00
parent ee7ff7d5e8
commit d4700a07f9
64 changed files with 258 additions and 214 deletions

View file

@ -58,7 +58,7 @@
questions: ['Recherche mit System', 'Quelloffen & unabhängig', 'Privat by Design'],
context: ['Dein Wissen, strukturiert', 'Quelloffen & unabhängig', 'Privat by Design'],
presi: ['Präsentationen neu gedacht', 'Quelloffen & unabhängig', 'Privat by Design'],
mukke: ['Musik machen, einfach so', 'Quelloffen & unabhängig', 'Privat by Design'],
music: ['Musik machen, einfach so', 'Quelloffen & unabhängig', 'Privat by Design'],
storage: ['Deine Dateien, dein Tresor', 'Quelloffen & unabhängig', 'Privat by Design'],
times: ['Zeiterfassung ohne Overhead', 'Quelloffen & unabhängig', 'Privat by Design'],
inventar: ['Alles im Überblick behalten', 'Quelloffen & unabhängig', 'Privat by Design'],
@ -91,7 +91,7 @@
questions: ['Research with structure', 'Open-source & independent', 'Private by design'],
context: ['Your knowledge, organized', 'Open-source & independent', 'Private by design'],
presi: ['Presentations reimagined', 'Open-source & independent', 'Private by design'],
mukke: ['Make music, just like that', 'Open-source & independent', 'Private by design'],
music: ['Make music, just like that', 'Open-source & independent', 'Private by design'],
storage: ['Your files, your vault', 'Open-source & independent', 'Private by design'],
times: ['Time tracking without overhead', 'Open-source & independent', 'Private by design'],
inventar: ['Keep track of everything', 'Open-source & independent', 'Private by design'],

View file

@ -119,7 +119,7 @@ export const APP_ICONS = {
guides: svgToDataUrl(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="gg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#0d9488"/><stop offset="100%" style="stop-color:#0f766e"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#gg)"/><rect x="18" y="25" width="28" height="50" rx="3" fill="white" fill-opacity="0.15"/><rect x="54" y="25" width="28" height="50" rx="3" fill="white" fill-opacity="0.15"/><rect x="46" y="25" width="8" height="50" fill="white" fill-opacity="0.25"/><circle cx="27" cy="40" r="4" fill="white" fill-opacity="0.9"/><rect x="34" y="37" width="11" height="3" rx="1.5" fill="white" fill-opacity="0.6"/><circle cx="27" cy="52" r="4" fill="white" fill-opacity="0.55"/><rect x="34" y="49" width="9" height="3" rx="1.5" fill="white" fill-opacity="0.4"/><circle cx="27" cy="64" r="4" fill="white" fill-opacity="0.3"/><rect x="34" y="61" width="10" height="3" rx="1.5" fill="white" fill-opacity="0.25"/><path d="M60 52l6 7 12-14" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>`
),
mukke: svgToDataUrl(
music: svgToDataUrl(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><linearGradient id="mk" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#ec4899"/><stop offset="100%" style="stop-color:#db2777"/></linearGradient></defs><rect width="100" height="100" rx="22" fill="url(#mk)"/><circle cx="35" cy="62" r="10" stroke="white" stroke-width="4" fill="none"/><circle cx="65" cy="58" r="10" stroke="white" stroke-width="4" fill="none"/><path d="M45 62V30l30-6v28" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><rect x="47" y="30" width="8" height="4" rx="1" fill="white" fill-opacity="0.5"/><rect x="57" y="27" width="8" height="4" rx="1" fill="white" fill-opacity="0.5"/></svg>`
),
photos: svgToDataUrl(

View file

@ -272,9 +272,9 @@ export const APP_BRANDING: Record<AppId, AppBranding> = {
logoStroke: true,
logoStrokeWidth: 1.5,
},
mukke: {
id: 'mukke',
name: 'Mukke',
music: {
id: 'music',
name: 'Music',
tagline: 'Music Workspace',
primaryColor: '#ec4899',
secondaryColor: '#f472b6',

View file

@ -35,7 +35,7 @@ export {
SkillTreeLogo,
PlantaLogo,
LightWriteLogo,
MukkeLogo,
MusicLogo,
ContextLogo,
CitycornersLogo,
} from './logos';

View file

@ -10,4 +10,4 @@
let { size = 55, color, class: className = '' }: Props = $props();
</script>
<AppLogo app="mukke" {size} {color} class={className} />
<AppLogo app="music" {size} {color} class={className} />

View file

@ -22,6 +22,6 @@ export { default as QuestionsLogo } from './QuestionsLogo.svelte';
export { default as SkillTreeLogo } from './SkillTreeLogo.svelte';
export { default as PlantaLogo } from './PlantaLogo.svelte';
export { default as LightWriteLogo } from './LightWriteLogo.svelte';
export { default as MukkeLogo } from './MukkeLogo.svelte';
export { default as MusicLogo } from './MusicLogo.svelte';
export { default as ContextLogo } from './ContextLogo.svelte';
export { default as CitycornersLogo } from './CitycornersLogo.svelte';

View file

@ -514,8 +514,8 @@ export const MANA_APPS: ManaApp[] = [
requiredTier: 'beta',
},
{
id: 'mukke',
name: 'Mukke',
id: 'music',
name: 'Music',
description: {
de: 'Musikproduktion',
en: 'Music Production',
@ -524,7 +524,7 @@ export const MANA_APPS: ManaApp[] = [
de: 'Erstelle und verwalte Songs, Playlists und Musikprojekte mit Markern und Arrangements.',
en: 'Create and manage songs, playlists, and music projects with markers and arrangements.',
},
icon: APP_ICONS.mukke,
icon: APP_ICONS.music,
color: '#ec4899',
comingSoon: false,
status: 'beta',
@ -759,7 +759,7 @@ export const APP_URLS: Record<AppIconId, { dev: string; prod: string }> = {
cards: { dev: 'http://localhost:5173/cards', prod: 'https://mana.how/cards' },
zitare: { dev: 'http://localhost:5173/zitare', prod: 'https://mana.how/zitare' },
clock: { dev: 'http://localhost:5173/clock', prod: 'https://mana.how/clock' },
mukke: { dev: 'http://localhost:5173/mukke', prod: 'https://mana.how/mukke' },
music: { dev: 'http://localhost:5173/music', prod: 'https://mana.how/music' },
storage: { dev: 'http://localhost:5173/storage', prod: 'https://mana.how/storage' },
presi: { dev: 'http://localhost:5173/presi', prod: 'https://mana.how/presi' },
inventar: { dev: 'http://localhost:5173/inventar', prod: 'https://mana.how/inventar' },

View file

@ -24,7 +24,7 @@ export type AppId =
| 'planta'
| 'lightwrite'
| 'context'
| 'mukke'
| 'music'
| 'citycorners';
/**

View file

@ -46,7 +46,7 @@ const DEEP_LINK_PATTERNS: Record<string, Record<string, string>> = {
decks: '/decks/{id}',
cards: '/decks/{id}', // Navigate to deck containing the card
},
mukke: {
music: {
songs: '/',
playlists: '/playlists/{id}',
},

View file

@ -27,7 +27,7 @@ Each app gets its own isolated bucket, created automatically by `minio-init`:
| `storage-storage` | Storage | Cloud drive files |
| `mail-storage` | Mail | Email attachments |
| `inventory-storage` | Inventory | Product photos |
| `mukke-storage` | Mukke | Music tracks, beats, covers |
| `music-storage` | Music | Music tracks, beats, covers |
| `planta-storage` | Planta | Plant photos |
| `projectdoc-storage` | ProjectDoc | Document files |
@ -88,7 +88,7 @@ import { createStorage } from '@manacore/shared-storage';
// Instead of app-specific factories:
const storage = createStorage('PICTURE');
const storage = createStorage('CHAT');
const storage = createStorage('MUKKE');
const storage = createStorage('MUSIC');
```
App-specific aliases still work: `createPictureStorage()`, `createChatStorage()`, etc.

View file

@ -110,6 +110,6 @@ export const createContactsStorage = () => createStorage('CONTACTS');
export const createStorageStorage = (publicUrl?: string) => createStorage('STORAGE', publicUrl);
export const createMailStorage = () => createStorage('MAIL');
export const createInventoryStorage = (publicUrl?: string) => createStorage('INVENTORY', publicUrl);
export const createMukkeStorage = () => createStorage('MUKKE');
export const createMusicStorage = () => createStorage('MUSIC');
export const createPlantaStorage = (publicUrl?: string) => createStorage('PLANTA', publicUrl);
export const createProjectDocStorage = () => createStorage('PROJECTDOC');

View file

@ -16,7 +16,7 @@ export {
createStorageStorage,
createMailStorage,
createInventoryStorage,
createMukkeStorage,
createMusicStorage,
createPlantaStorage,
createProjectDocStorage,
} from './factory';

View file

@ -136,7 +136,7 @@ export const BUCKETS = {
STORAGE: 'storage-storage',
MAIL: 'mail-storage',
INVENTORY: 'inventory-storage',
MUKKE: 'mukke-storage',
MUSIC: 'music-storage',
PLANTA: 'planta-storage',
PROJECTDOC: 'projectdoc-storage',
} as const;

View file

@ -45,7 +45,7 @@ export type AppSource =
| 'chat'
| 'storage'
| 'presi'
| 'mukke'
| 'music'
| 'cards'
| 'picture'
| 'uload'
@ -59,7 +59,7 @@ export const APP_SOURCE_LABELS: Record<string, string> = {
chat: 'Chat',
storage: 'Storage',
presi: 'Presi',
mukke: 'Mukke',
music: 'Music',
cards: 'Cards',
picture: 'Picture',
uload: 'uLoad',

View file

@ -160,7 +160,7 @@ const track = {
questions: createModuleTracker('questions'),
photos: createModuleTracker('photos'),
storage: createModuleTracker('storage'),
mukke: createModuleTracker('mukke'),
music: createModuleTracker('music'),
zitare: createModuleTracker('zitare'),
presi: createModuleTracker('presi'),
subscription: createModuleTracker('subscription'),
@ -434,21 +434,21 @@ export const StorageEvents = {
};
/**
* Mukke App Events
* Music App Events
*/
export const MukkeEvents = {
songUploaded: () => track.mukke('song_uploaded'),
songUploadFailed: () => track.mukke('song_upload_failed'),
songPlayed: () => track.mukke('song_played'),
songFavorited: (favorited: boolean) => track.mukke('song_favorited', { favorited }),
songDeleted: () => track.mukke('song_deleted'),
playlistCreated: () => track.mukke('playlist_created'),
playlistDeleted: () => track.mukke('playlist_deleted'),
playlistPlayAll: () => track.mukke('playlist_play_all'),
playlistShufflePlay: () => track.mukke('playlist_shuffle_play'),
projectCreated: () => track.mukke('project_created'),
projectDeleted: () => track.mukke('project_deleted'),
projectExported: (format: string) => track.mukke('project_exported', { format }),
export const MusicEvents = {
songUploaded: () => track.music('song_uploaded'),
songUploadFailed: () => track.music('song_upload_failed'),
songPlayed: () => track.music('song_played'),
songFavorited: (favorited: boolean) => track.music('song_favorited', { favorited }),
songDeleted: () => track.music('song_deleted'),
playlistCreated: () => track.music('playlist_created'),
playlistDeleted: () => track.music('playlist_deleted'),
playlistPlayAll: () => track.music('playlist_play_all'),
playlistShufflePlay: () => track.music('playlist_shuffle_play'),
projectCreated: () => track.music('project_created'),
projectDeleted: () => track.music('project_deleted'),
projectExported: (format: string) => track.music('project_exported', { format }),
};
/**

View file

@ -183,7 +183,7 @@ export const MANA_APP_INDEX: Record<string, number> = {
picture: 5,
clock: 6,
storage: 7,
mukke: 8,
music: 8,
presi: 9,
context: 10,
cards: 11,