fix(picture): resolve hydration error and CORS issues

- Add ssr.noExternal config for @manacore packages with .svelte.ts files
  to ensure Svelte 5 runes are properly processed
- Fix CORS configuration to return Access-Control-Allow-Origin header
  by using callback function for origin validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-01 14:14:08 +01:00
parent 120bc93f96
commit a09c389ede
6 changed files with 85 additions and 9 deletions

View file

@ -13,8 +13,12 @@
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
import { THEME_DEFINITIONS } from '@manacore/shared-theme';
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { getPillAppItems } from '@manacore/shared-branding';
import { setLocale, supportedLocales } from '$lib/i18n';
// App switcher items
const appItems = getPillAppItems('manadeck');
let { children } = $props();
let isSidebarMode = $state(false);
@ -63,6 +67,9 @@
);
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
// User email for user dropdown
let userEmail = $derived(authStore.user?.email);
// Navigation shortcuts (Ctrl+1-5)
const navRoutes = navItems.map((item) => item.href);
@ -181,7 +188,14 @@
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={true}
primaryColor="#6366f1"
showAppSwitcher={true}
{appItems}
{userEmail}
settingsHref="/settings"
manaHref="/mana"
profileHref="/profile"
/>
<!-- Main content with dynamic padding -->

View file

@ -1,20 +1,32 @@
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// Enable CORS for mobile and web apps
const allowedOrigins = [
'http://localhost:3000',
'http://localhost:5173',
'http://localhost:5174',
'http://localhost:5175',
'http://localhost:8081',
'exp://localhost:8081',
'http://localhost:3001', // Mana Core Auth
];
app.enableCors({
origin: [
'http://localhost:3000',
'http://localhost:5173',
'http://localhost:5174',
'http://localhost:8081',
'exp://localhost:8081',
'http://localhost:3001', // Mana Core Auth
],
origin: (origin, callback) => {
// Allow requests with no origin (like mobile apps or curl)
if (!origin || allowedOrigins.includes(origin)) {
callback(null, origin || '*');
} else {
callback(null, false);
}
},
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
});
@ -28,6 +40,12 @@ async function bootstrap() {
})
);
// Serve static files from uploads directory (for local development)
const uploadsPath = process.env.LOCAL_STORAGE_PATH || join(process.cwd(), 'uploads');
app.useStaticAssets(uploadsPath, {
prefix: '/uploads/',
});
// Set global prefix for API routes
app.setGlobalPrefix('api');

View file

@ -7,6 +7,7 @@
import type { PillNavItem, PillNavElement, PillDropdownItem } from '@manacore/shared-ui';
import { THEME_DEFINITIONS } from '@manacore/shared-theme';
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { getPillAppItems } from '@manacore/shared-branding';
import { setLocale, supportedLocales } from '$lib/i18n';
import KeyboardShortcutsModal from '$lib/components/ui/KeyboardShortcutsModal.svelte';
import { theme } from '$lib/stores/theme';
@ -14,6 +15,9 @@
import { viewMode, setViewMode, type ViewMode } from '$lib/stores/view';
import { browser } from '$app/environment';
// App switcher items
const appItems = getPillAppItems('picture');
let { children } = $props();
// PillNav state
@ -110,6 +114,9 @@
);
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
// User email for user dropdown
let userEmail = $derived(authStore.user?.email);
// Elements (divider + view mode tabs)
let elements: PillNavElement[] = $derived([
{ type: 'divider' as const },
@ -225,7 +232,14 @@
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={true}
primaryColor="#3b82f6"
showAppSwitcher={true}
{appItems}
{userEmail}
settingsHref="/app/settings"
manaHref="/app/mana"
profileHref="/app/profile"
/>
{/if}

View file

@ -8,4 +8,8 @@ export default defineConfig({
port: 5175,
strictPort: true,
},
ssr: {
// Process @manacore packages that contain .svelte.ts files with runes
noExternal: ['@manacore/shared-theme', '@manacore/shared-auth'],
},
});

View file

@ -13,9 +13,13 @@
isNavCollapsed as collapsedStore,
} from '$lib/stores/navigation';
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { getPillAppItems } from '@manacore/shared-branding';
import { setLocale, supportedLocales } from '$lib/i18n';
import '../app.css';
// App switcher items
const appItems = getPillAppItems('presi');
let { children } = $props();
let loading = $state(true);
@ -53,6 +57,9 @@
);
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
// User email for user dropdown
let userEmail = $derived(auth.user?.email);
// Navigation items for Presi
const navItems: PillNavItem[] = [
{ href: '/', label: 'Decks', icon: 'document' },
@ -192,6 +199,12 @@
showLogout={true}
onLogout={handleLogout}
primaryColor="#64748b"
showAppSwitcher={true}
{appItems}
{userEmail}
settingsHref="/settings"
manaHref="/mana"
profileHref="/profile"
/>
<!-- Main Content with dynamic padding based on nav mode -->

View file

@ -13,10 +13,14 @@
isNavCollapsed as collapsedStore,
} from '$lib/stores/navigation';
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { getPillAppItems } from '@manacore/shared-branding';
import { setLocale, supportedLocales } from '$lib/i18n';
import ToastContainer from '$lib/components/ToastContainer.svelte';
import '../app.css';
// App switcher items
const appItems = getPillAppItems('zitare');
let { children } = $props();
let loading = $state(true);
@ -59,6 +63,9 @@
);
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
// User email for user dropdown
let userEmail = $derived(authStore.user?.email);
// Navigation items for Zitare
const navItems: PillNavItem[] = [
{ href: '/', label: 'Start', icon: 'home' },
@ -194,6 +201,12 @@
showLogout={authStore.isAuthenticated}
onLogout={handleLogout}
primaryColor="#f59e0b"
showAppSwitcher={true}
{appItems}
{userEmail}
settingsHref="/settings"
manaHref="/mana"
profileHref="/profile"
/>
<!-- Main Content with dynamic padding based on nav mode -->