Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f679a52
fix(web-e2e): resolve Human Chat Panel navigation without Playwright …
cursoragent Apr 27, 2026
5e204f8
fix(epics): show only display name in human chat @ mention picker
cursoragent Apr 27, 2026
106fc1d
feat(epics): composer @ mentions use display names before Matrix send
cursoragent Apr 27, 2026
4da40ab
fix(epics): clip horizontal scroll on dual-panel main column
cursoragent Apr 27, 2026
2b48687
fix(web): DHO space layout fills main column beside chat panel
cursoragent Apr 27, 2026
1711efe
fix(epics): composer and pills use Hypha-resolved mention names
cursoragent Apr 27, 2026
9878826
feat(epics): group call Hypha names on tiles + idle leave + enter sta…
cursoragent Apr 27, 2026
ec51b68
fix(epics): unblock check-types for human chat panel mentions
cursoragent Apr 27, 2026
3dabe32
test(web-e2e): use locale path for my-spaces in ai-chat flag spec
cursoragent Apr 27, 2026
f7aef89
fix(web-e2e): trim BASE_URL in playwright config like nav-url
cursoragent Apr 27, 2026
5b56fc0
fix(chat): gate mention picks on JWT readiness for roster-backed subs
cursoragent Apr 27, 2026
c14ddb1
fix(chat): disambiguate duplicate display names in Matrix mention tokens
cursoragent Apr 27, 2026
4ed2e29
fix(chat): reclassify own messages after Matrix user id hydrates
cursoragent Apr 27, 2026
cb87d1e
fix(chat): resolve Hypha names for @mxid mention pills after reload
cursoragent Apr 27, 2026
c1b2f5a
feat(matrix): debug group-call media stalls and warn when remote feed…
cursoragent Apr 27, 2026
3ee0438
fix(chat): Hypha-resolved names on group call video tiles
cursoragent Apr 27, 2026
357c691
style(chat): Discord-tight @mention pills without ring
cursoragent Apr 27, 2026
ac59dbe
feat(chat): auto-link URLs in plain message text (target blank)
cursoragent Apr 27, 2026
8ad1094
feat(chat): shorten copy-message links and Discord-style Hypha pills
cursoragent Apr 27, 2026
ea34bc8
fix(epics): avoid polynomial regex on URL punctuation trim
cursoragent Apr 28, 2026
3dba9fc
fix(epics): remove green accent overlay on first-unread chat row
cursoragent Apr 27, 2026
56f91a4
build: add pnpm overrides for hono, DOMPurify, and Hono node adapter
cursoragent Apr 27, 2026
5781da3
fix(epics): satisfy TS on URL punctuation trim character access
cursoragent Apr 28, 2026
1b8d788
refactor(epics): rename mergeMentionDisplayLabel local to avoid shado…
cursoragent Apr 28, 2026
830ded0
fix(epics): show composer text in pending upload caption preview
cursoragent Apr 28, 2026
abd7697
fix(epics): scroll chat panel tabs into view when clipped
cursoragent Apr 28, 2026
bb16d2d
fix(epics): group call Start vs Join and remote media stall on tiles
cursoragent Apr 28, 2026
0056d56
fix(epics): resolve Hypha names in Mentions tab like main chat
cursoragent Apr 28, 2026
104899f
feat(epics): global mention badge and aggregated Mentions inbox
cursoragent Apr 28, 2026
944fd42
feat(core): WebRTC diagnostics for Matrix group calls (TURN/ICE telem…
cursoragent Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web-e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { nxE2EPreset } from '@nx/playwright/preset';
import { workspaceRoot } from '@nx/devkit';

// For CI, you may want to set BASE_URL to the deployed application.
const baseURL = process.env['BASE_URL'] || 'http://127.0.0.1:3000';
const baseURL = process.env['BASE_URL']?.trim() || 'http://127.0.0.1:3000';

/**
* Read environment variables from file.
Expand Down
9 changes: 5 additions & 4 deletions apps/web-e2e/src/ai-chat-feature-flag.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test, expect } from '@playwright/test';
import { AiChatPanelPage } from './pages/ai-chat-panel.page';
import { gotoApp } from './utils/nav-url';

/**
* Feature Flag: NEXT_PUBLIC_ENABLE_AI_CHAT
Expand Down Expand Up @@ -57,7 +58,7 @@ test.describe('AI Chat Panel — Feature Flag Enabled', () => {
test('should not render AI trigger button on non-space pages', async ({
page,
}) => {
await page.goto('/my-spaces');
await gotoApp(page, '/en/my-spaces');
await page.waitForLoadState('domcontentloaded');

const aiButton = page.getByRole('button', {
Expand Down Expand Up @@ -91,7 +92,7 @@ test.describe('AI Chat Panel — Feature Flag Disabled', () => {
test('should not render AI trigger button on space page', async ({
page,
}) => {
await page.goto('/en/dho/hypha');
await gotoApp(page, '/en/dho/hypha');
await page.waitForLoadState('domcontentloaded');

const aiButton = page.getByRole('button', {
Expand All @@ -101,15 +102,15 @@ test.describe('AI Chat Panel — Feature Flag Disabled', () => {
});

test('should not render sidebar panel markup', async ({ page }) => {
await page.goto('/en/dho/hypha');
await gotoApp(page, '/en/dho/hypha');
await page.waitForLoadState('domcontentloaded');

const sidebar = page.locator('[data-sidebar="sidebar"]');
await expect(sidebar).toHaveCount(0);
});

test('should render page content normally', async ({ page }) => {
await page.goto('/en/dho/hypha');
await gotoApp(page, '/en/dho/hypha');
await page.waitForLoadState('domcontentloaded');

// Space page content should still render
Expand Down
13 changes: 7 additions & 6 deletions apps/web-e2e/src/coherence-chat-panel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import { test, expect } from '@playwright/test';
import { CoherenceChatPanelPage } from './pages/coherence-chat-panel.page';
import { HumanChatPanelPage } from './pages/human-chat-panel.page';
import { gotoApp } from './utils/nav-url';

test.describe('Coherence Chat Panel Integration', () => {
const SPACE_SLUG = 'hypha';
Expand All @@ -46,7 +47,7 @@ test.describe('Coherence Chat Panel Integration', () => {
page,
}) => {
const chatPanel = new HumanChatPanelPage(page);
await page.goto(`/en/dho/${SPACE_SLUG}/coherence`);
await gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`);
await page.waitForLoadState('domcontentloaded');

await expect(chatPanel.openButton).toBeVisible();
Expand All @@ -60,7 +61,7 @@ test.describe('Coherence Chat Panel Integration', () => {
page,
}) => {
const chatPanel = new HumanChatPanelPage(page);
await page.goto(`/en/dho/${SPACE_SLUG}/coherence`);
await gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`);
await page.waitForLoadState('domcontentloaded');
await chatPanel.openPanel();

Expand All @@ -73,7 +74,7 @@ test.describe('Coherence Chat Panel Integration', () => {
page,
}) => {
const chatPanel = new CoherenceChatPanelPage(page, SPACE_SLUG);
await page.goto(`/en/dho/${SPACE_SLUG}/coherence`);
await gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`);
await page.waitForLoadState('domcontentloaded');
// Open button exists; back button should NOT be present in space mode
await chatPanel.openPanelButton.click();
Expand All @@ -85,7 +86,7 @@ test.describe('Coherence Chat Panel Integration', () => {
page,
}) => {
const chatPanel = new HumanChatPanelPage(page);
await page.goto(`/en/dho/${SPACE_SLUG}/coherence`);
await gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`);
await page.waitForLoadState('domcontentloaded');
await chatPanel.openPanel();

Expand Down Expand Up @@ -440,7 +441,7 @@ test.describe('Coherence Chat Panel Integration', () => {
'space chat mode is the default state of the panel on coherence page',
async ({ page }) => {
const chatPanel = new HumanChatPanelPage(page);
await page.goto(`/en/dho/${SPACE_SLUG}/coherence`);
await gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`);
await page.waitForLoadState('domcontentloaded');

await chatPanel.openPanel();
Expand All @@ -461,7 +462,7 @@ test.describe('Coherence Chat Panel Integration', () => {
await expect(chatPanel.headerText).toBeVisible(); // "Chat"

// Navigate to coherence page
await page.goto(`/en/dho/${SPACE_SLUG}/coherence`);
await gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`);
await page.waitForLoadState('domcontentloaded');

// Panel should still be in space chat mode (no signal was clicked)
Expand Down
5 changes: 3 additions & 2 deletions apps/web-e2e/src/human-chat-panel-feature-flag.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test, expect } from '@playwright/test';
import { HumanChatPanelPage } from './pages/human-chat-panel.page';
import { gotoApp } from './utils/nav-url';

/**
* Default runtime: off. The enabled describe block sets `HYPHA_ENABLE_HUMAN_CHAT=true`.
Expand Down Expand Up @@ -62,7 +63,7 @@ test.describe('Human Chat Panel — kill switch (disabled)', () => {
test('should not render Human Chat trigger button on space page', async ({
page,
}) => {
await page.goto('/en/dho/hypha/agreements');
await gotoApp(page, '/en/dho/hypha/agreements');
await page.waitForLoadState('domcontentloaded');

const chatButton = page.getByRole('button', {
Expand All @@ -72,7 +73,7 @@ test.describe('Human Chat Panel — kill switch (disabled)', () => {
});

test('should render page content normally', async ({ page }) => {
await page.goto('/en/dho/hypha/agreements');
await gotoApp(page, '/en/dho/hypha/agreements');
await page.waitForLoadState('domcontentloaded');

await expect(page.getByText('Agreements')).toBeVisible();
Expand Down
7 changes: 4 additions & 3 deletions apps/web-e2e/src/menu-top-consistent-height.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test, expect } from '@playwright/test';
import { LayoutPage } from './pages/layout.page';
import { gotoApp } from './utils/nav-url';

/**
* MenuTop — Consistent Height
Expand Down Expand Up @@ -35,15 +36,15 @@ test.describe('MenuTop consistent height', () => {
const menuSelector = 'header.sticky';

// Measure on a space page (trigger icons present)
await page.goto('/en/dho/hypha/agreements');
await gotoApp(page, '/en/dho/hypha/agreements');
await page.waitForLoadState('domcontentloaded');
const spaceHeader = page.locator(menuSelector).first();
await spaceHeader.waitFor({ state: 'visible' });
const spaceBox = await spaceHeader.boundingBox();
expect(spaceBox).not.toBeNull();

// Measure on a non-space page (no trigger icons)
await page.goto('/en/network');
await gotoApp(page, '/en/network');
await page.waitForLoadState('domcontentloaded');
const networkHeader = page.locator(menuSelector).first();
await networkHeader.waitFor({ state: 'visible' });
Expand All @@ -57,7 +58,7 @@ test.describe('MenuTop consistent height', () => {
test('--menu-top-height CSS variable should be set and integer', async ({
page,
}) => {
await page.goto('/en/dho/hypha/agreements');
await gotoApp(page, '/en/dho/hypha/agreements');
await page.waitForLoadState('domcontentloaded');

// Poll until --menu-top-height is set by ResizeObserver
Expand Down
2 changes: 1 addition & 1 deletion apps/web-e2e/src/pages/ai-chat-panel.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export class AiChatPanelPage extends BasePage {
}

async open() {
await this.page.goto('/en/dho/hypha');
await this.gotoApp('/en/dho/hypha');
await this.waitForPageLoad();
}

Expand Down
6 changes: 6 additions & 0 deletions apps/web-e2e/src/pages/base.page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Page } from '@playwright/test';
import { resolveAppUrl } from '../utils/nav-url';

export class BasePage {
readonly page: Page;
Expand All @@ -7,6 +8,11 @@ export class BasePage {
this.page = page;
}

/** Same origin as Playwright `use.baseURL`; safe when config is not loaded. */
async gotoApp(path: string) {
await this.page.goto(resolveAppUrl(path));
}

async waitForPageLoad() {
await this.page.waitForLoadState('domcontentloaded');
}
Expand Down
2 changes: 1 addition & 1 deletion apps/web-e2e/src/pages/coherence-chat-panel.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class CoherenceChatPanelPage extends BasePage {
* Navigate directly to the coherence page.
*/
async openCoherencePage() {
await this.page.goto(`/en/dho/${this.spaceSlug}/coherence`);
await this.gotoApp(`/en/dho/${this.spaceSlug}/coherence`);
await this.waitForPageLoad();
}

Expand Down
4 changes: 2 additions & 2 deletions apps/web-e2e/src/pages/coherence.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ export class CoherencePage extends BasePage {
* from which we can click the Coherence navigation tab.
*/
async openDhoPage() {
await this.page.goto(`/en/dho/${this.spaceSlug}/agreements`);
await this.gotoApp(`/en/dho/${this.spaceSlug}/agreements`);
await this.waitForPageLoad();
}

/**
* Navigate directly to the coherence page URL.
*/
async openCoherencePage() {
await this.page.goto(`/en/dho/${this.spaceSlug}/coherence`);
await this.gotoApp(`/en/dho/${this.spaceSlug}/coherence`);
await this.waitForPageLoad();
}

Expand Down
2 changes: 1 addition & 1 deletion apps/web-e2e/src/pages/human-chat-panel.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class HumanChatPanelPage extends BasePage {

/** Navigate to a space's agreements page. Defaults to 'hypha'. */
async navigateToSpace(spaceSlug = 'hypha') {
await this.page.goto(`/en/dho/${spaceSlug}/agreements`);
await this.gotoApp(`/en/dho/${spaceSlug}/agreements`);
await this.waitForPageLoad();
}

Expand Down
2 changes: 1 addition & 1 deletion apps/web-e2e/src/pages/layout.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class LayoutPage extends BasePage {
}

async open(path = '/en/dho/hypha/agreements') {
await this.page.goto(path);
await this.gotoApp(path);
await this.waitForPageLoad();
}

Expand Down
2 changes: 1 addition & 1 deletion apps/web-e2e/src/pages/my-spaces.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class MySpaces extends BasePage {
}

async open() {
await this.page.goto('/my-spaces');
await this.gotoApp('/my-spaces');
await this.waitForPageLoad();
}

Expand Down
17 changes: 9 additions & 8 deletions apps/web-e2e/src/panels-space-context.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { test, expect } from '@playwright/test';
import { gotoApp } from './utils/nav-url';

/**
* Side Panels — Space Context Only
Expand Down Expand Up @@ -34,7 +35,7 @@ test.describe('Panels visible on space pages', () => {
});

test('should show Human Chat trigger on a space page', async ({ page }) => {
await page.goto('/en/dho/hypha/agreements');
await gotoApp(page, '/en/dho/hypha/agreements');
await page.waitForLoadState('domcontentloaded');

await expect(
Expand All @@ -43,7 +44,7 @@ test.describe('Panels visible on space pages', () => {
});

test('should show AI trigger on a space page', async ({ page }) => {
await page.goto('/en/dho/hypha/agreements');
await gotoApp(page, '/en/dho/hypha/agreements');
await page.waitForLoadState('domcontentloaded');

await expect(page.getByRole('button', { name: AI_TRIGGER })).toBeVisible();
Expand All @@ -69,7 +70,7 @@ test.describe('Panels hidden on non-space pages', () => {
});

test('should NOT show Human Chat trigger on /network', async ({ page }) => {
await page.goto('/en/network');
await gotoApp(page, '/en/network');
await page.waitForLoadState('domcontentloaded');

await expect(page.getByRole('button', { name: CHAT_TRIGGER })).toHaveCount(
Expand All @@ -78,14 +79,14 @@ test.describe('Panels hidden on non-space pages', () => {
});

test('should NOT show AI trigger on /network', async ({ page }) => {
await page.goto('/en/network');
await gotoApp(page, '/en/network');
await page.waitForLoadState('domcontentloaded');

await expect(page.getByRole('button', { name: AI_TRIGGER })).toHaveCount(0);
});

test('should NOT show Human Chat trigger on /my-spaces', async ({ page }) => {
await page.goto('/en/my-spaces');
await gotoApp(page, '/en/my-spaces');
await page.waitForLoadState('domcontentloaded');

await expect(page.getByRole('button', { name: CHAT_TRIGGER })).toHaveCount(
Expand All @@ -94,14 +95,14 @@ test.describe('Panels hidden on non-space pages', () => {
});

test('should NOT show AI trigger on /my-spaces', async ({ page }) => {
await page.goto('/en/my-spaces');
await gotoApp(page, '/en/my-spaces');
await page.waitForLoadState('domcontentloaded');

await expect(page.getByRole('button', { name: AI_TRIGGER })).toHaveCount(0);
});

test('should NOT render sidebar markup on /network', async ({ page }) => {
await page.goto('/en/network');
await gotoApp(page, '/en/network');
await page.waitForLoadState('domcontentloaded');

// No panel sidebars should be in the DOM on non-space pages
Expand Down Expand Up @@ -134,7 +135,7 @@ test.describe('Panels appear after navigating into a space', () => {
page,
}) => {
// Start on a non-space page
await page.goto('/en/network');
await gotoApp(page, '/en/network');
await page.waitForLoadState('domcontentloaded');

// Verify triggers are absent
Expand Down
25 changes: 25 additions & 0 deletions apps/web-e2e/src/utils/nav-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Page } from '@playwright/test';

/**
* Default matches `playwright.config.ts` when `BASE_URL` is unset.
* Ensures `page.goto` works when Playwright runs without that config (e.g. wrong CWD),
* where `use.baseURL` is missing and relative URLs throw "Cannot navigate to invalid URL".
*/
const DEFAULT_BASE_URL = 'http://127.0.0.1:3000';

function resolveBaseUrl(): string {
return process.env['BASE_URL']?.trim() || DEFAULT_BASE_URL;
}

/**
* Absolute URL for an app-relative path (must start with `/`).
*/
export function resolveAppUrl(path: string): string {
const normalized = path.startsWith('/') ? path : `/${path}`;
return new URL(normalized, resolveBaseUrl()).href;
}

/** Navigate using the same base URL convention as the Playwright config. */
export async function gotoApp(page: Page, path: string): Promise<void> {
await page.goto(resolveAppUrl(path));
}
12 changes: 5 additions & 7 deletions apps/web/src/app/[lang]/dho/[id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
} from '@hypha-platform/epics';
import '../../_shared/space-accent.css';
import { Locale } from '@hypha-platform/i18n';
import { Container } from '@hypha-platform/ui';
import { findSpaceBySlug } from '@hypha-platform/core/server';
import { getDhoPathAgreements } from './@tab/agreements/constants';
import { ActionButtons } from './_components/action-buttons';
Expand Down Expand Up @@ -113,13 +112,12 @@ export default async function DhoLayout({
return (
<SpaceAccentPortalBridge>
{/*
Full-width row: `Container` already applies max-width + horizontal padding.
Dropping the extra `mx-auto max-w-container-2xl` wrapper avoided double
max-width + centering that made the main column look pushed with a void on the left.
Main column must span the full width next to side panels: `Container` max-width + `mx-auto`
centers content and leaves empty gutters — very visible when the Human chat panel narrows
the column (reads as a dead strip beside the hero / secondary chrome). Use padding only.
*/}
<div className="flex w-full min-w-0">
{/* `px-4!` = 16px: tighter than default Container `px-5` (20px) for DHO hero/tabs vs app chrome */}
<Container size="lg" className="min-w-0 flex-1 px-4!">
<div className="min-w-0 flex-1 px-4 sm:px-5">
{/* React 19+: link rel="preload" is hoisted to document head */}
{heroBannerImageHref !== DEFAULT_SPACE_LEAD_IMAGE ? (
<link
Expand Down Expand Up @@ -224,7 +222,7 @@ export default async function DhoLayout({
{tab}
{children}
</SpaceAccentFromImages>
</Container>
</div>
{aside}
</div>
</SpaceAccentPortalBridge>
Expand Down
Loading
Loading