mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:41:08 +02:00
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:
parent
ee7ff7d5e8
commit
d4700a07f9
64 changed files with 258 additions and 214 deletions
|
|
@ -18,7 +18,7 @@ import {
|
|||
// Module routes
|
||||
import { calendarRoutes } from './modules/calendar/routes';
|
||||
import { contactsRoutes } from './modules/contacts/routes';
|
||||
import { mukkeRoutes } from './modules/mukke/routes';
|
||||
import { musicRoutes } from './modules/music/routes';
|
||||
import { chatRoutes } from './modules/chat/routes';
|
||||
import { contextRoutes } from './modules/context/routes';
|
||||
import { pictureRoutes } from './modules/picture/routes';
|
||||
|
|
@ -48,7 +48,7 @@ app.use('/api/*', authMiddleware());
|
|||
// ─── Module Routes ──────────────────────────────────────────
|
||||
app.route('/api/v1/calendar', calendarRoutes);
|
||||
app.route('/api/v1/contacts', contactsRoutes);
|
||||
app.route('/api/v1/mukke', mukkeRoutes);
|
||||
app.route('/api/v1/music', musicRoutes);
|
||||
app.route('/api/v1/chat', chatRoutes);
|
||||
app.route('/api/v1/context', contextRoutes);
|
||||
app.route('/api/v1/picture', pictureRoutes);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Mukke module — Audio upload, presigned URLs, cover art
|
||||
* Ported from apps/mukke/apps/server
|
||||
* Music module — Audio upload, presigned URLs, cover art
|
||||
* Renamed from Mukke.
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
|
|
@ -18,8 +18,8 @@ routes.post('/songs/upload', async (c) => {
|
|||
const key = `users/${userId}/songs/${songId}/${filename}`;
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const { createMusicStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMusicStorage();
|
||||
const uploadUrl = await storage.getUploadUrl(key, { expiresIn: 3600 });
|
||||
|
||||
return c.json({
|
||||
|
|
@ -38,8 +38,8 @@ routes.get('/songs/:id/download-url', async (c) => {
|
|||
if (!storagePath) return c.json({ error: 'storagePath required' }, 400);
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const { createMusicStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMusicStorage();
|
||||
const url = await storage.getDownloadUrl(storagePath, { expiresIn: 3600 });
|
||||
return c.json({ url });
|
||||
} catch {
|
||||
|
|
@ -47,6 +47,35 @@ routes.get('/songs/:id/download-url', async (c) => {
|
|||
}
|
||||
});
|
||||
|
||||
// ─── Cover Art Upload (via mana-media) ─────────────────────
|
||||
|
||||
routes.post('/cover/upload', async (c) => {
|
||||
const userId = c.get('userId');
|
||||
const formData = await c.req.formData();
|
||||
const file = formData.get('file') as File | null;
|
||||
|
||||
if (!file) return c.json({ error: 'No file' }, 400);
|
||||
if (file.size > 10 * 1024 * 1024) return c.json({ error: 'Max 10MB' }, 400);
|
||||
if (!file.type.startsWith('image/')) return c.json({ error: 'Must be an image' }, 400);
|
||||
|
||||
try {
|
||||
const { uploadImageToMedia } = await import('../../lib/media');
|
||||
const buffer = await file.arrayBuffer();
|
||||
const result = await uploadImageToMedia(buffer, file.name, { app: 'music', userId });
|
||||
|
||||
return c.json(
|
||||
{
|
||||
coverArtPath: result.id,
|
||||
coverUrl: result.urls.thumbnail || result.urls.original,
|
||||
mediaId: result.id,
|
||||
},
|
||||
201
|
||||
);
|
||||
} catch {
|
||||
return c.json({ error: 'Upload failed' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Cover Art URL ──────────────────────────────────────────
|
||||
|
||||
routes.get('/songs/:id/cover-url', async (c) => {
|
||||
|
|
@ -54,8 +83,8 @@ routes.get('/songs/:id/cover-url', async (c) => {
|
|||
if (!coverArtPath) return c.json({ url: null });
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const { createMusicStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMusicStorage();
|
||||
const url = await storage.getDownloadUrl(coverArtPath, { expiresIn: 3600 });
|
||||
return c.json({ url });
|
||||
} catch {
|
||||
|
|
@ -70,8 +99,8 @@ routes.post('/library/cover-urls', async (c) => {
|
|||
if (!paths?.length) return c.json({ urls: {} });
|
||||
|
||||
try {
|
||||
const { createMukkeStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMukkeStorage();
|
||||
const { createMusicStorage } = await import('@manacore/shared-storage');
|
||||
const storage = createMusicStorage();
|
||||
const urls: Record<string, string> = {};
|
||||
|
||||
for (const path of paths.slice(0, 50)) {
|
||||
|
|
@ -88,4 +117,4 @@ routes.post('/library/cover-urls', async (c) => {
|
|||
}
|
||||
});
|
||||
|
||||
export { routes as mukkeRoutes };
|
||||
export { routes as musicRoutes };
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ const PUBLIC_STORAGE_API_URL_CLIENT =
|
|||
process.env.PUBLIC_STORAGE_API_URL_CLIENT || process.env.PUBLIC_STORAGE_API_URL || '';
|
||||
const PUBLIC_CARDS_API_URL_CLIENT =
|
||||
process.env.PUBLIC_CARDS_API_URL_CLIENT || process.env.PUBLIC_CARDS_API_URL || '';
|
||||
const PUBLIC_MUKKE_API_URL_CLIENT =
|
||||
process.env.PUBLIC_MUKKE_API_URL_CLIENT || process.env.PUBLIC_MUKKE_API_URL || '';
|
||||
const PUBLIC_MUSIC_API_URL_CLIENT =
|
||||
process.env.PUBLIC_MUSIC_API_URL_CLIENT || process.env.PUBLIC_MUSIC_API_URL || '';
|
||||
const PUBLIC_NUTRIPHI_API_URL_CLIENT =
|
||||
process.env.PUBLIC_NUTRIPHI_API_URL_CLIENT || process.env.PUBLIC_NUTRIPHI_API_URL || '';
|
||||
const PUBLIC_ULOAD_SERVER_URL_CLIENT =
|
||||
|
|
@ -63,7 +63,7 @@ const APP_SUBDOMAINS = new Set([
|
|||
'presi',
|
||||
'nutriphi',
|
||||
'photos',
|
||||
'mukke',
|
||||
'music',
|
||||
'picture',
|
||||
'calc',
|
||||
'citycorners',
|
||||
|
|
@ -100,7 +100,7 @@ window.__PUBLIC_SYNC_SERVER_URL__ = ${JSON.stringify(PUBLIC_SYNC_SERVER_URL_CLIE
|
|||
window.__PUBLIC_CHAT_API_URL__ = ${JSON.stringify(PUBLIC_CHAT_API_URL_CLIENT)};
|
||||
window.__PUBLIC_STORAGE_API_URL__ = ${JSON.stringify(PUBLIC_STORAGE_API_URL_CLIENT)};
|
||||
window.__PUBLIC_CARDS_API_URL__ = ${JSON.stringify(PUBLIC_CARDS_API_URL_CLIENT)};
|
||||
window.__PUBLIC_MUKKE_API_URL__ = ${JSON.stringify(PUBLIC_MUKKE_API_URL_CLIENT)};
|
||||
window.__PUBLIC_MUSIC_API_URL__ = ${JSON.stringify(PUBLIC_MUSIC_API_URL_CLIENT)};
|
||||
window.__PUBLIC_NUTRIPHI_API_URL__ = ${JSON.stringify(PUBLIC_NUTRIPHI_API_URL_CLIENT)};
|
||||
window.__PUBLIC_ULOAD_SERVER_URL__ = ${JSON.stringify(PUBLIC_ULOAD_SERVER_URL_CLIENT)};
|
||||
window.__PUBLIC_MEMORO_SERVER_URL__ = ${JSON.stringify(PUBLIC_MEMORO_SERVER_URL_CLIENT)};
|
||||
|
|
@ -124,7 +124,7 @@ window.__PUBLIC_GLITCHTIP_DSN__ = ${JSON.stringify(PUBLIC_GLITCHTIP_DSN)};
|
|||
PUBLIC_CHAT_API_URL_CLIENT,
|
||||
PUBLIC_STORAGE_API_URL_CLIENT,
|
||||
PUBLIC_CARDS_API_URL_CLIENT,
|
||||
PUBLIC_MUKKE_API_URL_CLIENT,
|
||||
PUBLIC_MUSIC_API_URL_CLIENT,
|
||||
PUBLIC_NUTRIPHI_API_URL_CLIENT,
|
||||
PUBLIC_ULOAD_SERVER_URL_CLIENT,
|
||||
PUBLIC_MEMORO_SERVER_URL_CLIENT,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export { pictureService, type GeneratedImage, type GenerationStats } from './pic
|
|||
export { cardsService, type Deck, type Card, type LearningProgress } from './cards';
|
||||
export { clockService, type Timer, type Alarm, type ClockStats } from './clock';
|
||||
export { storageService, type StorageFile, type StorageStats } from './storage';
|
||||
export { mukkeService, type Song, type MukkeStats } from './mukke';
|
||||
export { musicService, type Song, type MusicStats } from './music';
|
||||
export { presiService, type PresiDeck, type PresiStats } from './presi';
|
||||
export {
|
||||
contextService,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ vi.mock('$lib/stores/auth.svelte', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
import { mukkeService } from './mukke';
|
||||
import { musicService } from './music';
|
||||
|
||||
describe('mukkeService', () => {
|
||||
describe('musicService', () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
|
@ -25,27 +25,27 @@ describe('mukkeService', () => {
|
|||
|
||||
describe('formatDuration', () => {
|
||||
it('should format 0 seconds', () => {
|
||||
expect(mukkeService.formatDuration(0)).toBe('0:00');
|
||||
expect(musicService.formatDuration(0)).toBe('0:00');
|
||||
});
|
||||
|
||||
it('should format seconds only', () => {
|
||||
expect(mukkeService.formatDuration(45)).toBe('0:45');
|
||||
expect(musicService.formatDuration(45)).toBe('0:45');
|
||||
});
|
||||
|
||||
it('should format minutes and seconds', () => {
|
||||
expect(mukkeService.formatDuration(185)).toBe('3:05');
|
||||
expect(musicService.formatDuration(185)).toBe('3:05');
|
||||
});
|
||||
|
||||
it('should format hours', () => {
|
||||
expect(mukkeService.formatDuration(3661)).toBe('1:01:01');
|
||||
expect(musicService.formatDuration(3661)).toBe('1:01:01');
|
||||
});
|
||||
|
||||
it('should pad seconds with zero', () => {
|
||||
expect(mukkeService.formatDuration(60)).toBe('1:00');
|
||||
expect(musicService.formatDuration(60)).toBe('1:00');
|
||||
});
|
||||
|
||||
it('should handle negative values', () => {
|
||||
expect(mukkeService.formatDuration(-10)).toBe('0:00');
|
||||
expect(musicService.formatDuration(-10)).toBe('0:00');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ describe('mukkeService', () => {
|
|||
json: () => Promise.resolve(mockStats),
|
||||
});
|
||||
|
||||
const result = await mukkeService.getStats();
|
||||
const result = await musicService.getStats();
|
||||
|
||||
expect(result.data).toEqual(mockStats);
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
|
|
@ -80,7 +80,7 @@ describe('mukkeService', () => {
|
|||
json: () => Promise.resolve([{ id: 's-1', title: 'Song 1' }]),
|
||||
});
|
||||
|
||||
const result = await mukkeService.getRecentSongs();
|
||||
const result = await musicService.getRecentSongs();
|
||||
|
||||
expect(result.data).toHaveLength(1);
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
/**
|
||||
* Mukke API Service
|
||||
* Music API Service
|
||||
*
|
||||
* Fetches music library stats from the Mukke backend for dashboard widgets.
|
||||
* Fetches music library stats from the Music backend for dashboard widgets.
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import { createApiClient, type ApiResult } from '../base-client';
|
||||
|
||||
// Get Mukke API URL dynamically at runtime
|
||||
function getMukkeApiUrl(): string {
|
||||
// Get Music API URL dynamically at runtime
|
||||
function getMusicApiUrl(): string {
|
||||
if (browser && typeof window !== 'undefined') {
|
||||
const injectedUrl = (window as unknown as { __PUBLIC_MUKKE_API_URL__?: string })
|
||||
.__PUBLIC_MUKKE_API_URL__;
|
||||
const injectedUrl = (window as unknown as { __PUBLIC_MUSIC_API_URL__?: string })
|
||||
.__PUBLIC_MUSIC_API_URL__;
|
||||
if (injectedUrl) {
|
||||
return `${injectedUrl}`;
|
||||
}
|
||||
|
|
@ -24,13 +24,13 @@ let _client: ReturnType<typeof createApiClient> | null = null;
|
|||
|
||||
function getClient() {
|
||||
if (!_client) {
|
||||
_client = createApiClient(getMukkeApiUrl());
|
||||
_client = createApiClient(getMusicApiUrl());
|
||||
}
|
||||
return _client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Song entity from Mukke backend
|
||||
* Song entity from Music backend
|
||||
*/
|
||||
export interface Song {
|
||||
id: string;
|
||||
|
|
@ -49,7 +49,7 @@ export interface Song {
|
|||
/**
|
||||
* Music library statistics
|
||||
*/
|
||||
export interface MukkeStats {
|
||||
export interface MusicStats {
|
||||
totalSongs: number;
|
||||
totalPlaylists: number;
|
||||
totalProjects: number;
|
||||
|
|
@ -58,14 +58,14 @@ export interface MukkeStats {
|
|||
}
|
||||
|
||||
/**
|
||||
* Mukke service for dashboard widgets
|
||||
* Music service for dashboard widgets
|
||||
*/
|
||||
export const mukkeService = {
|
||||
export const musicService = {
|
||||
/**
|
||||
* Get library statistics
|
||||
*/
|
||||
async getStats(): Promise<ApiResult<MukkeStats>> {
|
||||
return getClient().get<MukkeStats>('/library/stats');
|
||||
async getStats(): Promise<ApiResult<MusicStats>> {
|
||||
return getClient().get<MusicStats>('/library/stats');
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -83,7 +83,7 @@ export const mukkeService = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Format duration for display (seconds → MM:SS or HH:MM:SS)
|
||||
* Format duration for display (seconds -> MM:SS or HH:MM:SS)
|
||||
*/
|
||||
formatDuration(seconds: number): string {
|
||||
if (seconds <= 0) return '0:00';
|
||||
|
|
|
|||
|
|
@ -377,12 +377,12 @@ registerApp({
|
|||
});
|
||||
|
||||
registerApp({
|
||||
id: 'mukke',
|
||||
name: 'Mukke',
|
||||
id: 'music',
|
||||
name: 'Music',
|
||||
color: '#F97316',
|
||||
views: {
|
||||
list: { load: () => import('$lib/modules/mukke/ListView.svelte') },
|
||||
detail: { load: () => import('$lib/modules/mukke/views/DetailView.svelte') },
|
||||
list: { load: () => import('$lib/modules/music/ListView.svelte') },
|
||||
detail: { load: () => import('$lib/modules/music/views/DetailView.svelte') },
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* CalendarEventsWidget - Upcoming calendar events (local-first)
|
||||
* CalendarEventsWidget - Upcoming timeBlocks (local-first)
|
||||
*
|
||||
* Reads directly from Calendar's IndexedDB via cross-app reader.
|
||||
* Reactive: auto-updates when events change (sync, other tabs).
|
||||
* Shows events, tasks, habits, and time entries from the next 7 days.
|
||||
* Reads directly from the timeBlocks table via cross-app query.
|
||||
* Reactive: auto-updates when data changes (sync, other tabs).
|
||||
*/
|
||||
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { useUpcomingEvents } from '$lib/data/cross-app-queries';
|
||||
import type { LocalTimeBlock } from '$lib/data/time-blocks/types';
|
||||
import { CalendarBlank, CheckSquare, Timer, Heart } from '@manacore/shared-icons';
|
||||
|
||||
const events = useUpcomingEvents(7);
|
||||
|
||||
const MAX_DISPLAY = 5;
|
||||
|
||||
function formatEventTime(event: any): string {
|
||||
const start = new Date(event.startDate);
|
||||
const typeIcons: Record<string, typeof CalendarBlank> = {
|
||||
event: CalendarBlank,
|
||||
task: CheckSquare,
|
||||
timeEntry: Timer,
|
||||
habit: Heart,
|
||||
};
|
||||
|
||||
function formatEventTime(block: LocalTimeBlock): string {
|
||||
const start = new Date(block.startDate);
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
|
@ -32,7 +42,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
if (event.allDay) {
|
||||
if (block.allDay) {
|
||||
return dateStr;
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +57,7 @@
|
|||
<div>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<h3 class="flex items-center gap-2 text-lg font-semibold">
|
||||
<span>🗓️</span>
|
||||
<span><CalendarBlank size={20} /></span>
|
||||
{$_('dashboard.widgets.calendar.title')}
|
||||
</h3>
|
||||
{#if (events.value ?? []).length > 0}
|
||||
|
|
@ -65,24 +75,29 @@
|
|||
</div>
|
||||
{:else if (events.value ?? []).length === 0}
|
||||
<div class="py-6 text-center">
|
||||
<div class="mb-2 text-3xl">📅</div>
|
||||
<div class="mb-2 text-3xl"><CalendarBlank size={32} /></div>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{$_('dashboard.widgets.calendar.empty')}
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-2">
|
||||
{#each displayedEvents as event (event.id)}
|
||||
{#each displayedEvents as block (block.id)}
|
||||
{@const TypeIcon = typeIcons[block.type] ?? CalendarBlank}
|
||||
<div class="flex items-start gap-3 rounded-lg p-2 transition-colors hover:bg-surface-hover">
|
||||
<div
|
||||
class="mt-1 h-3 w-3 flex-shrink-0 rounded-full"
|
||||
style="background-color: {event.color || '#3B82F6'}"
|
||||
class:animate-pulse={block.isLive}
|
||||
style="background-color: {block.color || '#3B82F6'}"
|
||||
></div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="truncate text-sm font-medium">{event.title}</p>
|
||||
<p class="text-xs text-muted-foreground">{formatEventTime(event)}</p>
|
||||
{#if event.location}
|
||||
<p class="truncate text-xs text-muted-foreground">📍 {event.location}</p>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<svelte:component this={TypeIcon} size={12} class="text-muted-foreground" />
|
||||
<p class="truncate text-sm font-medium">{block.title}</p>
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground">{formatEventTime(block)}</p>
|
||||
{#if block.isLive}
|
||||
<span class="text-xs text-green-600">live</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* MukkeLibraryWidget - Music library stats (local-first)
|
||||
* MusicLibraryWidget - Music library stats (local-first)
|
||||
*/
|
||||
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { useMukkeStats } from '$lib/data/cross-app-queries';
|
||||
const stats = useMukkeStats();
|
||||
import { useMusicStats } from '$lib/data/cross-app-queries';
|
||||
const stats = useMusicStats();
|
||||
|
||||
function formatDuration(seconds?: number): string {
|
||||
if (!seconds) return '';
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<div class="mb-3">
|
||||
<h3 class="flex items-center gap-2 text-lg font-semibold">
|
||||
<span>🎵</span>
|
||||
{$_('dashboard.widgets.mukke.title')}
|
||||
{$_('dashboard.widgets.music.title')}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
|
|
@ -66,6 +66,6 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<p class="mt-2 text-center text-xs text-muted-foreground">Mukke</p>
|
||||
<p class="mt-2 text-center text-xs text-muted-foreground">Music</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export const APP_POSITIONS: Record<string, { x: number; y: number; lakeId: strin
|
|||
|
||||
// Around Waldsee Mitte (db-center)
|
||||
zitare: { x: 640, y: 600, lakeId: 'db-center' },
|
||||
mukke: { x: 850, y: 610, lakeId: 'db-center' },
|
||||
music: { x: 850, y: 610, lakeId: 'db-center' },
|
||||
clock: { x: 880, y: 680, lakeId: 'db-center' },
|
||||
nutriphi: { x: 650, y: 720, lakeId: 'db-center' },
|
||||
|
||||
|
|
|
|||
|
|
@ -154,8 +154,8 @@ const APP_DEFINITIONS: AppDefinition[] = [
|
|||
},
|
||||
},
|
||||
{
|
||||
id: 'mukke',
|
||||
displayName: 'Mukke',
|
||||
id: 'music',
|
||||
displayName: 'Music',
|
||||
score: 80,
|
||||
status: 'beta',
|
||||
categories: {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
storage: 'https://storage.mana.how',
|
||||
picture: 'https://picture.mana.how',
|
||||
presi: 'https://presi.mana.how',
|
||||
mukke: 'https://mukke.mana.how',
|
||||
music: 'https://music.mana.how',
|
||||
clock: 'https://clock.mana.how',
|
||||
nutriphi: 'https://nutriphi.mana.how',
|
||||
photos: 'https://photos.mana.how',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
redis: 'Schneller Cache-Speicher. Klein, kristallklar, sofort verfugbar.',
|
||||
minio: 'Objekt-Speicher fur Dateien, Bilder und Medien aller Apps.',
|
||||
'db-left': 'PostgreSQL-Datenbanken fur Calendar, Todo, Contacts, Storage.',
|
||||
'db-center': 'PostgreSQL-Datenbanken fur Zitare, Mukke, Clock, NutriPhi.',
|
||||
'db-center': 'PostgreSQL-Datenbanken fur Zitare, Music, Clock, NutriPhi.',
|
||||
'db-right': 'PostgreSQL-Datenbanken fur Photos, SkillTree, Context, Planta.',
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"zitare": "Zitare",
|
||||
"cards": "Karten",
|
||||
"picture": "Bilder",
|
||||
"mukke": "Musik",
|
||||
"music": "Musik",
|
||||
"photos": "Fotos",
|
||||
"storage": "Ablage",
|
||||
"nutriphi": "NutriPhi",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"zitare": "Zitare",
|
||||
"cards": "Cards",
|
||||
"picture": "Pictures",
|
||||
"mukke": "Music",
|
||||
"music": "Music",
|
||||
"photos": "Photos",
|
||||
"storage": "Storage",
|
||||
"nutriphi": "NutriPhi",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"zitare": "Zitare",
|
||||
"cards": "Tarjetas",
|
||||
"picture": "Imágenes",
|
||||
"mukke": "Música",
|
||||
"music": "Música",
|
||||
"photos": "Fotos",
|
||||
"storage": "Almacén",
|
||||
"nutriphi": "NutriPhi",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"zitare": "Zitare",
|
||||
"cards": "Cartes",
|
||||
"picture": "Images",
|
||||
"mukke": "Musique",
|
||||
"music": "Musique",
|
||||
"photos": "Photos",
|
||||
"storage": "Stockage",
|
||||
"nutriphi": "NutriPhi",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"zitare": "Zitare",
|
||||
"cards": "Schede",
|
||||
"picture": "Immagini",
|
||||
"mukke": "Musica",
|
||||
"music": "Musica",
|
||||
"photos": "Foto",
|
||||
"storage": "Archivio",
|
||||
"nutriphi": "NutriPhi",
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@
|
|||
"empty": "Keine Dateien",
|
||||
"open": "Storage öffnen"
|
||||
},
|
||||
"mukke": {
|
||||
"music": {
|
||||
"title": "Musik",
|
||||
"description": "Deine Musikbibliothek",
|
||||
"songs": "Songs",
|
||||
"playlists": "Playlists",
|
||||
"favorites": "Favoriten",
|
||||
"empty": "Keine Songs",
|
||||
"open": "Mukke öffnen"
|
||||
"open": "Music öffnen"
|
||||
},
|
||||
"presi": {
|
||||
"title": "Präsentationen",
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@
|
|||
"empty": "No files",
|
||||
"open": "Open Storage"
|
||||
},
|
||||
"mukke": {
|
||||
"music": {
|
||||
"title": "Music",
|
||||
"description": "Your music library",
|
||||
"songs": "Songs",
|
||||
"playlists": "Playlists",
|
||||
"favorites": "Favorites",
|
||||
"empty": "No songs",
|
||||
"open": "Open Mukke"
|
||||
"open": "Open Music"
|
||||
},
|
||||
"presi": {
|
||||
"title": "Presentations",
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@
|
|||
"empty": "Sin archivos",
|
||||
"open": "Abrir Storage"
|
||||
},
|
||||
"mukke": {
|
||||
"music": {
|
||||
"title": "Música",
|
||||
"description": "Tu biblioteca musical",
|
||||
"songs": "Canciones",
|
||||
"playlists": "Playlists",
|
||||
"favorites": "Favoritos",
|
||||
"empty": "Sin canciones",
|
||||
"open": "Abrir Mukke"
|
||||
"open": "Abrir Music"
|
||||
},
|
||||
"presi": {
|
||||
"title": "Presentaciones",
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@
|
|||
"empty": "Aucun fichier",
|
||||
"open": "Ouvrir Storage"
|
||||
},
|
||||
"mukke": {
|
||||
"music": {
|
||||
"title": "Musique",
|
||||
"description": "Ta bibliothèque musicale",
|
||||
"songs": "Titres",
|
||||
"playlists": "Playlists",
|
||||
"favorites": "Favoris",
|
||||
"empty": "Aucun titre",
|
||||
"open": "Ouvrir Mukke"
|
||||
"open": "Ouvrir Music"
|
||||
},
|
||||
"presi": {
|
||||
"title": "Présentations",
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@
|
|||
"empty": "Nessun file",
|
||||
"open": "Apri Storage"
|
||||
},
|
||||
"mukke": {
|
||||
"music": {
|
||||
"title": "Musica",
|
||||
"description": "La tua libreria musicale",
|
||||
"songs": "Brani",
|
||||
"playlists": "Playlist",
|
||||
"favorites": "Preferiti",
|
||||
"empty": "Nessun brano",
|
||||
"open": "Apri Mukke"
|
||||
"open": "Apri Music"
|
||||
},
|
||||
"presi": {
|
||||
"title": "Presentazioni",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
Mukke — Workbench ListView
|
||||
Music — Workbench ListView
|
||||
Song library with recent plays and playlists.
|
||||
-->
|
||||
<script lang="ts">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Mukke module — collection accessors and guest seed data.
|
||||
* Music module — collection accessors and guest seed data.
|
||||
*
|
||||
* Table names: songs, mukkePlaylists, playlistSongs, mukkeProjects, markers
|
||||
* Dexie table names kept as mukkePlaylists/mukkeProjects for backward compat.
|
||||
*/
|
||||
|
||||
import { db } from '$lib/data/database';
|
||||
|
|
@ -16,16 +16,16 @@ import type {
|
|||
// ─── Collection Accessors ──────────────────────────────────
|
||||
|
||||
export const songTable = db.table<LocalSong>('songs');
|
||||
export const mukkePlaylistTable = db.table<LocalPlaylist>('mukkePlaylists');
|
||||
export const musicPlaylistTable = db.table<LocalPlaylist>('mukkePlaylists');
|
||||
export const playlistSongTable = db.table<LocalPlaylistSong>('playlistSongs');
|
||||
export const mukkeProjectTable = db.table<LocalProject>('mukkeProjects');
|
||||
export const musicProjectTable = db.table<LocalProject>('mukkeProjects');
|
||||
export const markerTable = db.table<LocalMarker>('markers');
|
||||
|
||||
// ─── Guest Seed ────────────────────────────────────────────
|
||||
|
||||
const DEMO_PLAYLIST_ID = 'demo-favorites';
|
||||
|
||||
export const MUKKE_GUEST_SEED = {
|
||||
export const MUSIC_GUEST_SEED = {
|
||||
songs: [] as Record<string, unknown>[],
|
||||
mukkePlaylists: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Mukke module — barrel exports.
|
||||
* Music module — barrel exports.
|
||||
*/
|
||||
|
||||
export { libraryStore } from './stores/library.svelte';
|
||||
|
|
@ -29,11 +29,11 @@ export {
|
|||
} from './queries';
|
||||
export {
|
||||
songTable,
|
||||
mukkePlaylistTable,
|
||||
musicPlaylistTable,
|
||||
playlistSongTable,
|
||||
mukkeProjectTable,
|
||||
musicProjectTable,
|
||||
markerTable,
|
||||
MUKKE_GUEST_SEED,
|
||||
MUSIC_GUEST_SEED,
|
||||
} from './collections';
|
||||
export type {
|
||||
LocalSong,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Reactive queries & pure helpers for Mukke — uses Dexie liveQuery on the unified DB.
|
||||
* Reactive queries & pure helpers for Music — uses Dexie liveQuery on the unified DB.
|
||||
*/
|
||||
|
||||
import { liveQuery } from 'dexie';
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { songTable } from '../collections';
|
||||
import { MukkeEvents } from '@manacore/shared-utils/analytics';
|
||||
import { MusicEvents } from '@manacore/shared-utils/analytics';
|
||||
import type { LocalSong } from '../types';
|
||||
|
||||
export const libraryStore = {
|
||||
|
|
@ -19,7 +19,7 @@ export const libraryStore = {
|
|||
favorite: newState,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
MukkeEvents.songFavorited(newState);
|
||||
MusicEvents.songFavorited(newState);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ export const libraryStore = {
|
|||
lastPlayedAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
MukkeEvents.songPlayed();
|
||||
MusicEvents.songPlayed();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ export const libraryStore = {
|
|||
async delete(id: string) {
|
||||
const now = new Date().toISOString();
|
||||
await songTable.update(id, { deletedAt: now, updatedAt: now });
|
||||
MukkeEvents.songDeleted();
|
||||
MusicEvents.songDeleted();
|
||||
},
|
||||
|
||||
/** Insert a song (e.g., after upload). */
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ function createPlayerStore() {
|
|||
state.duration = 0;
|
||||
state.error = null;
|
||||
|
||||
// NOTE: In the unified app, audio URLs would come from the mukke backend
|
||||
// NOTE: In the unified app, audio URLs would come from the music backend
|
||||
// via presigned S3 download URLs. For now, playback requires the backend.
|
||||
// The store manages queue/state regardless.
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* Handles playlist CRUD and song associations.
|
||||
*/
|
||||
|
||||
import { mukkePlaylistTable, playlistSongTable } from '../collections';
|
||||
import { musicPlaylistTable, playlistSongTable } from '../collections';
|
||||
import { toPlaylist } from '../queries';
|
||||
import { MukkeEvents } from '@manacore/shared-utils/analytics';
|
||||
import { MusicEvents } from '@manacore/shared-utils/analytics';
|
||||
import type { LocalPlaylist, LocalPlaylistSong } from '../types';
|
||||
|
||||
export const playlistsStore = {
|
||||
|
|
@ -19,14 +19,14 @@ export const playlistsStore = {
|
|||
description: description ?? null,
|
||||
coverArtPath: null,
|
||||
};
|
||||
await mukkePlaylistTable.add(newLocal);
|
||||
MukkeEvents.playlistCreated();
|
||||
await musicPlaylistTable.add(newLocal);
|
||||
MusicEvents.playlistCreated();
|
||||
return toPlaylist(newLocal);
|
||||
},
|
||||
|
||||
/** Update a playlist. */
|
||||
async update(id: string, data: Partial<Pick<LocalPlaylist, 'name' | 'description'>>) {
|
||||
await mukkePlaylistTable.update(id, {
|
||||
await musicPlaylistTable.update(id, {
|
||||
...data,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
|
@ -35,13 +35,13 @@ export const playlistsStore = {
|
|||
/** Soft-delete a playlist and its song associations. */
|
||||
async delete(id: string) {
|
||||
const now = new Date().toISOString();
|
||||
await mukkePlaylistTable.update(id, { deletedAt: now, updatedAt: now });
|
||||
await musicPlaylistTable.update(id, { deletedAt: now, updatedAt: now });
|
||||
// Soft-delete associated playlistSongs
|
||||
const allPS = await playlistSongTable.where('playlistId').equals(id).toArray();
|
||||
for (const ps of allPS) {
|
||||
await playlistSongTable.update(ps.id, { deletedAt: now, updatedAt: now });
|
||||
}
|
||||
MukkeEvents.playlistDeleted();
|
||||
MusicEvents.playlistDeleted();
|
||||
},
|
||||
|
||||
/** Add a song to a playlist. */
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
* Handles project CRUD.
|
||||
*/
|
||||
|
||||
import { mukkeProjectTable } from '../collections';
|
||||
import { musicProjectTable } from '../collections';
|
||||
import { toProject } from '../queries';
|
||||
import { MukkeEvents } from '@manacore/shared-utils/analytics';
|
||||
import { MusicEvents } from '@manacore/shared-utils/analytics';
|
||||
import type { LocalProject } from '../types';
|
||||
|
||||
export const projectsStore = {
|
||||
|
|
@ -19,14 +19,14 @@ export const projectsStore = {
|
|||
description: data.description ?? null,
|
||||
songId: data.songId ?? null,
|
||||
};
|
||||
await mukkeProjectTable.add(newLocal);
|
||||
MukkeEvents.projectCreated();
|
||||
await musicProjectTable.add(newLocal);
|
||||
MusicEvents.projectCreated();
|
||||
return toProject(newLocal);
|
||||
},
|
||||
|
||||
/** Update a project. */
|
||||
async update(id: string, data: Partial<Pick<LocalProject, 'title' | 'description' | 'songId'>>) {
|
||||
await mukkeProjectTable.update(id, {
|
||||
await musicProjectTable.update(id, {
|
||||
...data,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
|
@ -35,7 +35,7 @@ export const projectsStore = {
|
|||
/** Soft-delete a project. */
|
||||
async delete(id: string) {
|
||||
const now = new Date().toISOString();
|
||||
await mukkeProjectTable.update(id, { deletedAt: now, updatedAt: now });
|
||||
MukkeEvents.projectDeleted();
|
||||
await musicProjectTable.update(id, { deletedAt: now, updatedAt: now });
|
||||
MusicEvents.projectDeleted();
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Umukke Tags — Uses shared global tags + module-specific junction table.
|
||||
* Music Tags — Uses shared global tags + module-specific junction table.
|
||||
*/
|
||||
|
||||
import { db } from '$lib/data/database';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Mukke module types for the unified app.
|
||||
* Music module types for the unified app.
|
||||
*/
|
||||
|
||||
import type { BaseRecord } from '@manacore/local-store';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
Mukke — DetailView (inline editable overlay)
|
||||
Music — DetailView (inline editable overlay)
|
||||
All fields are always editable. Changes auto-save on blur.
|
||||
-->
|
||||
<script lang="ts">
|
||||
|
|
|
|||
|
|
@ -157,11 +157,11 @@ export async function collectAppSnapshots(): Promise<AppSnapshot[]> {
|
|||
});
|
||||
}
|
||||
|
||||
// Mukke
|
||||
// Music
|
||||
if (songs.length > 0) {
|
||||
snapshots.push({
|
||||
app: 'Mukke',
|
||||
appIndex: MANA_APP_INDEX.mukke,
|
||||
app: 'Music',
|
||||
appIndex: MANA_APP_INDEX.music,
|
||||
totalItems: songs.length,
|
||||
completedItems: 0,
|
||||
favoriteItems: 0,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { storageSearchProvider } from './storage';
|
|||
import { cardsSearchProvider } from './cards';
|
||||
import { pictureSearchProvider } from './picture';
|
||||
import { presiSearchProvider } from './presi';
|
||||
import { mukkeSearchProvider } from './mukke';
|
||||
import { musicSearchProvider } from './music';
|
||||
import { zitareSearchProvider } from './zitare';
|
||||
import { clockSearchProvider } from './clock';
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ export function registerAllProviders(registry: SearchRegistry): void {
|
|||
registry.register(cardsSearchProvider);
|
||||
registry.register(pictureSearchProvider);
|
||||
registry.register(presiSearchProvider);
|
||||
registry.register(mukkeSearchProvider);
|
||||
registry.register(musicSearchProvider);
|
||||
registry.register(zitareSearchProvider);
|
||||
registry.register(clockSearchProvider);
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ export {
|
|||
cardsSearchProvider,
|
||||
pictureSearchProvider,
|
||||
presiSearchProvider,
|
||||
mukkeSearchProvider,
|
||||
musicSearchProvider,
|
||||
zitareSearchProvider,
|
||||
clockSearchProvider,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { getManaApp } from '@manacore/shared-branding';
|
|||
import { scoreRecord, truncateSubtitle } from '../scoring';
|
||||
import type { SearchProvider, SearchResult, SearchOptions } from '../types';
|
||||
|
||||
const app = getManaApp('mukke');
|
||||
const app = getManaApp('music');
|
||||
|
||||
export const mukkeSearchProvider: SearchProvider = {
|
||||
appId: 'mukke',
|
||||
appName: 'Mukke',
|
||||
export const musicSearchProvider: SearchProvider = {
|
||||
appId: 'music',
|
||||
appName: 'Music',
|
||||
appIcon: app?.icon,
|
||||
appColor: app?.color,
|
||||
searchableTypes: ['song', 'playlist', 'project'],
|
||||
|
|
@ -33,19 +33,19 @@ export const mukkeSearchProvider: SearchProvider = {
|
|||
results.push({
|
||||
id: song.id,
|
||||
type: 'song',
|
||||
appId: 'mukke',
|
||||
appId: 'music',
|
||||
title: song.title,
|
||||
subtitle: [song.artist, song.album].filter(Boolean).join(' · ') || undefined,
|
||||
appIcon: app?.icon,
|
||||
appColor: app?.color,
|
||||
href: '/mukke/library',
|
||||
href: '/music/library',
|
||||
score,
|
||||
matchedField,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Search playlists
|
||||
// Search playlists (Dexie table name kept for backward compat)
|
||||
const playlists = await db.table('mukkePlaylists').toArray();
|
||||
for (const pl of playlists) {
|
||||
if (pl.deletedAt) continue;
|
||||
|
|
@ -60,19 +60,19 @@ export const mukkeSearchProvider: SearchProvider = {
|
|||
results.push({
|
||||
id: pl.id,
|
||||
type: 'playlist',
|
||||
appId: 'mukke',
|
||||
appId: 'music',
|
||||
title: pl.name,
|
||||
subtitle: truncateSubtitle(pl.description) || 'Playlist',
|
||||
appIcon: app?.icon,
|
||||
appColor: app?.color,
|
||||
href: `/mukke/playlists/${pl.id}`,
|
||||
href: `/music/playlists/${pl.id}`,
|
||||
score,
|
||||
matchedField,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Search projects
|
||||
// Search projects (Dexie table name kept for backward compat)
|
||||
const projects = await db.table('mukkeProjects').toArray();
|
||||
for (const proj of projects) {
|
||||
if (proj.deletedAt) continue;
|
||||
|
|
@ -87,12 +87,12 @@ export const mukkeSearchProvider: SearchProvider = {
|
|||
results.push({
|
||||
id: proj.id,
|
||||
type: 'project',
|
||||
appId: 'mukke',
|
||||
appId: 'music',
|
||||
title: proj.title,
|
||||
subtitle: truncateSubtitle(proj.description) || 'Projekt',
|
||||
appIcon: app?.icon,
|
||||
appColor: app?.color,
|
||||
href: `/mukke/projects`,
|
||||
href: `/music/projects`,
|
||||
score,
|
||||
matchedField,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const SPLIT_APP_ID_LIST = [
|
|||
'picture',
|
||||
'cards',
|
||||
'zitare',
|
||||
'mukke',
|
||||
'music',
|
||||
'storage',
|
||||
'presi',
|
||||
'inventar',
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ export const dashboardStore = {
|
|||
'chat-recent',
|
||||
'contacts-favorites',
|
||||
'zitare-quote',
|
||||
'mukke-library',
|
||||
'music-library',
|
||||
'presi-decks',
|
||||
'context-docs',
|
||||
] as WidgetType[]
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ describe('WIDGET_REGISTRY', () => {
|
|||
'cards',
|
||||
'times',
|
||||
'storage',
|
||||
'mukke',
|
||||
'music',
|
||||
'presi',
|
||||
'context',
|
||||
'mana-core-auth',
|
||||
|
|
@ -73,7 +73,7 @@ describe('WIDGET_REGISTRY', () => {
|
|||
expect(types).toContain('cards-progress');
|
||||
expect(types).toContain('clock-timers');
|
||||
expect(types).toContain('storage-usage');
|
||||
expect(types).toContain('mukke-library');
|
||||
expect(types).toContain('music-library');
|
||||
expect(types).toContain('presi-decks');
|
||||
expect(types).toContain('context-docs');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
useAllPlaylists,
|
||||
useAllPlaylistSongs,
|
||||
useAllProjects,
|
||||
} from '$lib/modules/mukke/queries';
|
||||
} from '$lib/modules/music/queries';
|
||||
|
||||
let { children }: { children: Snippet } = $props();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { computeStats } from '$lib/modules/mukke/queries';
|
||||
import type { Song, Playlist, Project } from '$lib/modules/mukke/types';
|
||||
import { computeStats } from '$lib/modules/music/queries';
|
||||
import type { Song, Playlist, Project } from '$lib/modules/music/types';
|
||||
import { MusicNote, Plus, Playlist as PlaylistIcon, Note } from '@manacore/shared-icons';
|
||||
|
||||
const songsCtx: { readonly value: Song[] } = getContext('songs');
|
||||
|
|
@ -20,11 +20,11 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Mukke - ManaCore</title>
|
||||
<title>Music - ManaCore</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-8">
|
||||
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Mukke</h1>
|
||||
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Music</h1>
|
||||
|
||||
<!-- Quick Stats -->
|
||||
<section>
|
||||
|
|
@ -62,21 +62,21 @@
|
|||
</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<a
|
||||
href="/mukke/library"
|
||||
href="/music/library"
|
||||
class="inline-flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
|
||||
>
|
||||
<MusicNote size={20} />
|
||||
Bibliothek
|
||||
</a>
|
||||
<a
|
||||
href="/mukke/playlists"
|
||||
href="/music/playlists"
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
|
||||
>
|
||||
<PlaylistIcon size={20} />
|
||||
Playlists
|
||||
</a>
|
||||
<a
|
||||
href="/mukke/projects"
|
||||
href="/music/projects"
|
||||
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
|
||||
>
|
||||
<Plus size={20} />
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
<h2 class="text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]">
|
||||
Letzte Projekte
|
||||
</h2>
|
||||
<a href="/mukke/projects" class="text-sm text-[hsl(var(--primary))] hover:underline">
|
||||
<a href="/music/projects" class="text-sm text-[hsl(var(--primary))] hover:underline">
|
||||
Alle anzeigen
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { libraryStore } from '$lib/modules/mukke/stores/library.svelte';
|
||||
import { playerStore } from '$lib/modules/mukke/stores/player.svelte';
|
||||
import { libraryStore } from '$lib/modules/music/stores/library.svelte';
|
||||
import { playerStore } from '$lib/modules/music/stores/player.svelte';
|
||||
import {
|
||||
searchSongs,
|
||||
filterFavorites,
|
||||
groupByAlbum,
|
||||
groupByGenre,
|
||||
formatDuration,
|
||||
} from '$lib/modules/mukke/queries';
|
||||
import type { Song } from '$lib/modules/mukke/types';
|
||||
} from '$lib/modules/music/queries';
|
||||
import type { Song } from '$lib/modules/music/types';
|
||||
import {
|
||||
MusicNote,
|
||||
Heart,
|
||||
|
|
@ -53,14 +53,14 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Bibliothek - Mukke - ManaCore</title>
|
||||
<title>Bibliothek - Music - ManaCore</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center gap-3">
|
||||
<a
|
||||
href="/mukke"
|
||||
href="/music"
|
||||
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { getContext } from 'svelte';
|
||||
import { playlistsStore } from '$lib/modules/mukke/stores/playlists.svelte';
|
||||
import type { Playlist } from '$lib/modules/mukke/types';
|
||||
import { playlistsStore } from '$lib/modules/music/stores/playlists.svelte';
|
||||
import type { Playlist } from '$lib/modules/music/types';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Plus,
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Playlists - Mukke - ManaCore</title>
|
||||
<title>Playlists - Music - ManaCore</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<a
|
||||
href="/mukke"
|
||||
href="/music"
|
||||
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
||||
{#each playlistsCtx.value as playlist (playlist.id)}
|
||||
<a
|
||||
href="/mukke/playlists/{playlist.id}"
|
||||
href="/music/playlists/{playlist.id}"
|
||||
class="group relative rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
|
||||
>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
import { page } from '$app/stores';
|
||||
import { goto } from '$app/navigation';
|
||||
import { getContext } from 'svelte';
|
||||
import { playlistsStore } from '$lib/modules/mukke/stores/playlists.svelte';
|
||||
import { playerStore } from '$lib/modules/mukke/stores/player.svelte';
|
||||
import { getPlaylistSongs, formatDuration } from '$lib/modules/mukke/queries';
|
||||
import type { Song, Playlist, LocalPlaylistSong } from '$lib/modules/mukke/types';
|
||||
import { playlistsStore } from '$lib/modules/music/stores/playlists.svelte';
|
||||
import { playerStore } from '$lib/modules/music/stores/player.svelte';
|
||||
import { getPlaylistSongs, formatDuration } from '$lib/modules/music/queries';
|
||||
import type { Song, Playlist, LocalPlaylistSong } from '$lib/modules/music/types';
|
||||
import {
|
||||
ArrowLeft,
|
||||
Play,
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
let editName = $state('');
|
||||
let showShare = $state(false);
|
||||
let shareUrl = $derived(
|
||||
`${typeof window !== 'undefined' ? window.location.origin : ''}/mukke/playlists/${playlistId}`
|
||||
`${typeof window !== 'undefined' ? window.location.origin : ''}/music/playlists/${playlistId}`
|
||||
);
|
||||
|
||||
function startEdit() {
|
||||
|
|
@ -64,13 +64,13 @@
|
|||
async function handleDeletePlaylist() {
|
||||
if (confirm('Playlist wirklich loschen?')) {
|
||||
await playlistsStore.delete(playlistId);
|
||||
goto('/mukke/playlists');
|
||||
goto('/music/playlists');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{playlist?.name || 'Playlist'} - Mukke - ManaCore</title>
|
||||
<title>{playlist?.name || 'Playlist'} - Music - ManaCore</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<a
|
||||
href="/mukke/playlists"
|
||||
href="/music/playlists"
|
||||
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
|
|
@ -222,6 +222,6 @@
|
|||
onClose={() => (showShare = false)}
|
||||
url={shareUrl}
|
||||
title={playlist?.name ?? 'Playlist'}
|
||||
source="mukke"
|
||||
source="music"
|
||||
description="{songs.length} {songs.length === 1 ? 'Song' : 'Songs'}"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { getContext } from 'svelte';
|
||||
import { projectsStore } from '$lib/modules/mukke/stores/projects.svelte';
|
||||
import type { Project } from '$lib/modules/mukke/types';
|
||||
import { projectsStore } from '$lib/modules/music/stores/projects.svelte';
|
||||
import type { Project } from '$lib/modules/music/types';
|
||||
import { ArrowLeft, Plus, Trash, Note, X } from '@manacore/shared-icons';
|
||||
|
||||
const projectsCtx: { readonly value: Project[] } = getContext('projects');
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Projekte - Mukke - ManaCore</title>
|
||||
<title>Projekte - Music - ManaCore</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<a
|
||||
href="/mukke"
|
||||
href="/music"
|
||||
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
Picture: 'image',
|
||||
Clock: 'clock',
|
||||
Storage: 'hard-drive',
|
||||
Mukke: 'music',
|
||||
Music: 'music',
|
||||
Presi: 'presentation',
|
||||
Context: 'file-text',
|
||||
Cards: 'layers',
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const SERVICES = [
|
|||
{ name: 'Chat API', url: process.env.PUBLIC_CHAT_API_URL || 'http://localhost:3030' },
|
||||
{ name: 'Storage API', url: process.env.PUBLIC_STORAGE_API_URL || 'http://localhost:3034' },
|
||||
{ name: 'Cards API', url: process.env.PUBLIC_CARDS_API_URL || 'http://localhost:3036' },
|
||||
{ name: 'Mukke API', url: process.env.PUBLIC_MUKKE_API_URL || 'http://localhost:3037' },
|
||||
{ name: 'Music API', url: process.env.PUBLIC_MUSIC_API_URL || 'http://localhost:3037' },
|
||||
{ name: 'NutriPhi API', url: process.env.PUBLIC_NUTRIPHI_API_URL || 'http://localhost:3038' },
|
||||
{ name: 'Uload Server', url: process.env.PUBLIC_ULOAD_SERVER_URL || 'http://localhost:3070' },
|
||||
{ name: 'Memoro Server', url: process.env.PUBLIC_MEMORO_SERVER_URL || 'http://localhost:3015' },
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export {
|
|||
SkillTreeLogo,
|
||||
PlantaLogo,
|
||||
LightWriteLogo,
|
||||
MukkeLogo,
|
||||
MusicLogo,
|
||||
ContextLogo,
|
||||
CitycornersLogo,
|
||||
} from './logos';
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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' },
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export type AppId =
|
|||
| 'planta'
|
||||
| 'lightwrite'
|
||||
| 'context'
|
||||
| 'mukke'
|
||||
| 'music'
|
||||
| 'citycorners';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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}',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export {
|
|||
createStorageStorage,
|
||||
createMailStorage,
|
||||
createInventoryStorage,
|
||||
createMukkeStorage,
|
||||
createMusicStorage,
|
||||
createPlantaStorage,
|
||||
createProjectDocStorage,
|
||||
} from './factory';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 }),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue