diff --git a/packages/shared-branding/src/mana-apps.ts b/packages/shared-branding/src/mana-apps.ts index f3356c7d4..70d6c0438 100644 --- a/packages/shared-branding/src/mana-apps.ts +++ b/packages/shared-branding/src/mana-apps.ts @@ -723,40 +723,49 @@ export const APP_SLIDER_LABELS = { /** * Default app URLs for local development and production */ +/** + * App URLs — unified app uses internal paths, separate apps use subdomains. + * + * All productivity apps are now served under mana.how/{appId}. + * Games and Matrix remain on separate subdomains. + */ export const APP_URLS: Record = { - chat: { dev: 'http://localhost:5174', prod: 'https://chat.mana.how' }, - memoro: { dev: 'http://localhost:5175', prod: 'https://memoro.mana.how' }, - presi: { dev: 'http://localhost:5176', prod: 'https://presi.mana.how' }, - cards: { dev: 'http://localhost:5177', prod: 'https://cards.mana.how' }, - picture: { dev: 'http://localhost:5185', prod: 'https://picture.mana.how' }, - zitare: { dev: 'http://localhost:5180', prod: 'https://zitare.mana.how' }, - wisekeep: { dev: 'http://localhost:5181', prod: 'https://wisekeep.mana.how' }, - nutriphi: { dev: 'http://localhost:5182', prod: 'https://nutriphi.mana.how' }, + // ─── Unified App (internal paths) ───────────────────────── manacore: { dev: 'http://localhost:5173', prod: 'https://mana.how' }, mana: { dev: 'http://localhost:5173', prod: 'https://mana.how' }, - moodlit: { dev: 'http://localhost:5182', prod: 'https://moodlit.mana.how' }, - contacts: { dev: 'http://localhost:5184', prod: 'https://contacts.mana.how' }, - calendar: { dev: 'http://localhost:5179', prod: 'https://calendar.mana.how' }, - storage: { dev: 'http://localhost:5185', prod: 'https://storage.mana.how' }, - clock: { dev: 'http://localhost:5187', prod: 'https://clock.mana.how' }, - todo: { dev: 'http://localhost:5188', prod: 'https://todo.mana.how' }, - mail: { dev: 'http://localhost:5186', prod: 'https://mail.mana.how' }, - inventory: { dev: 'http://localhost:5189', prod: 'https://inventory.mana.how' }, - questions: { dev: 'http://localhost:5111', prod: 'https://questions.mana.how' }, - matrix: { dev: 'http://localhost:5180', prod: 'https://matrix.mana.how' }, - playground: { dev: 'http://localhost:5190', prod: 'https://playground.mana.how' }, - context: { dev: 'http://localhost:5192', prod: 'https://context.mana.how' }, - citycorners: { dev: 'http://localhost:5196', prod: 'https://citycorners.mana.how' }, - times: { dev: 'http://localhost:5197', prod: 'https://times.mana.how' }, - uload: { dev: 'http://localhost:5173', prod: 'https://ulo.ad' }, + todo: { dev: 'http://localhost:5173/todo', prod: 'https://mana.how/todo' }, + calendar: { dev: 'http://localhost:5173/calendar', prod: 'https://mana.how/calendar' }, + contacts: { dev: 'http://localhost:5173/contacts', prod: 'https://mana.how/contacts' }, + chat: { dev: 'http://localhost:5173/chat', prod: 'https://mana.how/chat' }, + picture: { dev: 'http://localhost:5173/picture', prod: 'https://mana.how/picture' }, + 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' }, + 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' }, + inventory: { dev: 'http://localhost:5173/inventar', prod: 'https://mana.how/inventar' }, + photos: { dev: 'http://localhost:5173/photos', prod: 'https://mana.how/photos' }, + skilltree: { dev: 'http://localhost:5173/skilltree', prod: 'https://mana.how/skilltree' }, + citycorners: { dev: 'http://localhost:5173/citycorners', prod: 'https://mana.how/citycorners' }, + times: { dev: 'http://localhost:5173/times', prod: 'https://mana.how/times' }, + context: { dev: 'http://localhost:5173/context', prod: 'https://mana.how/context' }, + questions: { dev: 'http://localhost:5173/questions', prod: 'https://mana.how/questions' }, + nutriphi: { dev: 'http://localhost:5173/nutriphi', prod: 'https://mana.how/nutriphi' }, + planta: { dev: 'http://localhost:5173/planta', prod: 'https://mana.how/planta' }, + uload: { dev: 'http://localhost:5173/uload', prod: 'https://mana.how/uload' }, + calc: { dev: 'http://localhost:5173/calc', prod: 'https://mana.how/calc' }, + moodlit: { dev: 'http://localhost:5173/moodlit', prod: 'https://mana.how/moodlit' }, + memoro: { dev: 'http://localhost:5173/memoro', prod: 'https://mana.how/memoro' }, + playground: { dev: 'http://localhost:5173/playground', prod: 'https://mana.how/playground' }, + guides: { dev: 'http://localhost:5173/guides', prod: 'https://mana.how/guides' }, + wisekeep: { dev: 'http://localhost:5173/wisekeep', prod: 'https://mana.how/wisekeep' }, + news: { dev: 'http://localhost:5173/news', prod: 'https://mana.how/news' }, + mail: { dev: 'http://localhost:5173/mail', prod: 'https://mana.how/mail' }, reader: { dev: 'exp://localhost:8081', prod: 'https://reader.mana.how' }, - news: { dev: 'http://localhost:5174', prod: 'https://news.mana.how' }, - calc: { dev: 'http://localhost:5198', prod: 'https://calc.mana.how' }, - guides: { dev: 'http://localhost:5200', prod: 'https://guides.mana.how' }, - mukke: { dev: 'http://localhost:5191', prod: 'https://mukke.mana.how' }, - photos: { dev: 'http://localhost:5193', prod: 'https://photos.mana.how' }, - planta: { dev: 'http://localhost:5194', prod: 'https://planta.mana.how' }, - skilltree: { dev: 'http://localhost:5195', prod: 'https://skilltree.mana.how' }, + // ─── Separate Apps (own subdomains) ─────────────────────── + matrix: { dev: 'http://localhost:5180', prod: 'https://matrix.mana.how' }, arcade: { dev: 'http://localhost:5201', prod: 'https://arcade.mana.how' }, }; diff --git a/packages/shared-ui/src/navigation/AppDrawer.svelte b/packages/shared-ui/src/navigation/AppDrawer.svelte index fd24e847b..e2d22d82c 100644 --- a/packages/shared-ui/src/navigation/AppDrawer.svelte +++ b/packages/shared-ui/src/navigation/AppDrawer.svelte @@ -76,7 +76,14 @@ } else if (app.isCurrent) { window.location.href = '/'; } else if (app.url) { - window.open(app.url, '_blank', 'noopener,noreferrer'); + const isInternal = + app.url.startsWith('/') || + new URL(app.url, window.location.origin).origin === window.location.origin; + if (isInternal) { + window.location.href = app.url; + } else { + window.open(app.url, '_blank', 'noopener,noreferrer'); + } } close(); diff --git a/packages/shared-ui/src/navigation/GlobalSpotlight.svelte b/packages/shared-ui/src/navigation/GlobalSpotlight.svelte index 153c582ba..03ce59bd3 100644 --- a/packages/shared-ui/src/navigation/GlobalSpotlight.svelte +++ b/packages/shared-ui/src/navigation/GlobalSpotlight.svelte @@ -152,7 +152,14 @@ if (app.isCurrent) { window.location.href = '/'; } else if (app.url) { - window.open(app.url, '_blank', 'noopener,noreferrer'); + const isInternal = + app.url.startsWith('/') || + new URL(app.url, window.location.origin).origin === window.location.origin; + if (isInternal) { + window.location.href = app.url; + } else { + window.open(app.url, '_blank', 'noopener,noreferrer'); + } } } } else { diff --git a/packages/shared-ui/src/navigation/PillNavigation.svelte b/packages/shared-ui/src/navigation/PillNavigation.svelte index c06d50420..f82863189 100644 --- a/packages/shared-ui/src/navigation/PillNavigation.svelte +++ b/packages/shared-ui/src/navigation/PillNavigation.svelte @@ -170,7 +170,15 @@ // Navigate to home route for current app window.location.href = '/'; } else if (app.url) { - window.open(app.url, '_blank', 'noopener,noreferrer'); + // Internal paths (same-origin) navigate directly, external URLs open in new tab + const isInternal = + app.url.startsWith('/') || + new URL(app.url, window.location.origin).origin === window.location.origin; + if (isInternal) { + window.location.href = app.url; + } else { + window.open(app.url, '_blank', 'noopener,noreferrer'); + } } }, active: app.isCurrent, @@ -292,6 +300,8 @@ spiralHref?: string; /** Help page href (shown in user dropdown). Set to empty string to hide. */ helpHref?: string; + /** Bottom offset from viewport bottom (default: '0px'). Use to position above other fixed bars. */ + bottomOffset?: string; } let { @@ -340,6 +350,7 @@ themesHref, spiralHref, helpHref, + bottomOffset = '0px', }: Props = $props(); // Type guards for elements @@ -369,14 +380,6 @@ return localPart.substring(0, maxLength) + '…'; } - // Local state for uncontrolled mode - let internalCollapsed = $state(false); - - // Use external or internal state - const isCollapsed = $derived( - onCollapsedChange !== undefined ? (externalCollapsed ?? false) : internalCollapsed - ); - // Dropdown direction: always up since nav is always at bottom const dropdownDirection = 'up' as const; @@ -386,31 +389,17 @@ // Global spotlight (Cmd+K) — only active when spotlightActions are provided const spotlight = spotlightActions ? createGlobalSpotlightState() : null; - function collapseNav() { - if (onCollapsedChange) { - onCollapsedChange(true); - } else { - internalCollapsed = true; - } - } - - function expandNav() { - if (onCollapsedChange) { - onCollapsedChange(false); - } else { - internalCollapsed = false; - } - } - function isActive(path: string) { return currentPath === path; } -{#if !isCollapsed} +{#if !(externalCollapsed ?? false)} {/if} - -{#if isCollapsed} - -{/if} - {#if spotlight && spotlightActions} .pill-nav { position: fixed; - bottom: 0; + bottom: var(--pill-nav-bottom, 0px); left: 0; right: 0; z-index: 1000; @@ -991,22 +968,6 @@ display: inline; } - /* FAB for collapsed state - positioned at bottom right */ - .nav-fab { - position: fixed; - bottom: 0; - right: 0; - z-index: 1001; - display: flex; - align-items: center; - justify-content: center; - padding: 0.875rem; - padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 0.875rem); - border-radius: 1rem 0 0 0; - cursor: pointer; - border: none; - } - /* Transitions */ .pill-nav { transition: all 0.3s ease;