Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,14 @@ describe('ChatDebugComponent — edge-claim attribute', () => {
document.documentElement.removeAttribute('data-ngaf-chat-debug');
});

it('sets data-ngaf-chat-debug=dock on <html> while open', () => {
it('reads PEER --ngaf-chat-sidebar-claim-right (not aggregate occupy-right)', () => {
// Reading the aggregate occupy-right causes self-feedback: when
// chat-debug docks right, it WRITES occupy-right; if it also READS
// occupy-right, the panel offsets itself by its own width. Read the
// peer-specific sidebar-claim-right instead.
const styles = (ChatDebugComponent as unknown as { ɵcmp: { styles: string[] } }).ɵcmp.styles.join('\n');
expect(styles).toMatch(/\.panel--bottom[^{]*\{[^}]*right:\s*var\(--ngaf-chat-occupy-right/);
expect(styles).toMatch(/\.panel--right[^{]*\{[^}]*right:\s*var\(--ngaf-chat-occupy-right/);
expect(styles).toMatch(/\.panel--bottom[^{]*\{[^}]*right:\s*var\(--ngaf-chat-sidebar-claim-right/);
expect(styles).toMatch(/\.panel--right[^{]*\{[^}]*right:\s*var\(--ngaf-chat-sidebar-claim-right/);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ interface TabEntry {
}
.panel--right {
top: 0;
right: var(--ngaf-chat-occupy-right, 0);
right: var(--ngaf-chat-sidebar-claim-right, 0);
bottom: 0;
width: var(--panel-size, 420px);
border-right: 0;
Expand All @@ -119,7 +119,7 @@ interface TabEntry {
}
.panel--bottom {
left: 0;
right: var(--ngaf-chat-occupy-right, 0);
right: var(--ngaf-chat-sidebar-claim-right, 0);
bottom: 0;
height: var(--panel-size, 40vh);
border-bottom: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,17 @@ describe('ChatSidebarComponent — edge-claim attribute', () => {
});
});

it('panel CSS includes bottom: var(--ngaf-chat-occupy-bottom)', () => {
// Styles array is the second member of the @Component decorator metadata.
// Easier path: stringify the styles and look for the declaration.
it('panel CSS reads PEER --ngaf-chat-debug-claim-bottom (not aggregate)', () => {
// Components must read PEER per-component claim vars, never the
// aggregate occupy-* (which they write to themselves). The aggregate
// is for external consumer convenience; internal panels read
// peer-specific to avoid self-feedback.
const styles = (ChatSidebarComponent as unknown as { ɵcmp: { styles: string[] } }).ɵcmp.styles.join('\n');
expect(styles).toMatch(/\.chat-sidebar__panel[^{]*\{[^}]*bottom:\s*var\(--ngaf-chat-occupy-bottom/);
expect(styles).toMatch(/\.chat-sidebar__panel[^{]*\{[^}]*bottom:\s*var\(--ngaf-chat-debug-claim-bottom/);
});

it('launcher CSS includes calc(1rem + var(--ngaf-chat-occupy-bottom))', () => {
it('launcher CSS reads PEER --ngaf-chat-debug-claim-bottom (not aggregate)', () => {
const styles = (ChatSidebarComponent as unknown as { ɵcmp: { styles: string[] } }).ɵcmp.styles.join('\n');
expect(styles).toMatch(/\.chat-sidebar__launcher[^{]*\{[^}]*bottom:\s*calc\(1rem\s*\+\s*var\(--ngaf-chat-occupy-bottom/);
expect(styles).toMatch(/\.chat-sidebar__launcher[^{]*\{[^}]*bottom:\s*calc\(1rem\s*\+\s*var\(--ngaf-chat-debug-claim-bottom/);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens';
.chat-sidebar__panel {
position: fixed;
top: 0; right: 0;
bottom: var(--ngaf-chat-occupy-bottom, 0);
bottom: var(--ngaf-chat-debug-claim-bottom, 0);
width: 28rem;
background: var(--ngaf-chat-bg);
border-left: 1px solid var(--ngaf-chat-separator);
Expand Down Expand Up @@ -55,7 +55,7 @@ import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens';
.chat-sidebar__close:hover { background: var(--ngaf-chat-surface-alt); color: var(--ngaf-chat-text); }
.chat-sidebar__launcher {
position: fixed;
bottom: calc(1rem + var(--ngaf-chat-occupy-bottom, 0));
bottom: calc(1rem + var(--ngaf-chat-debug-claim-bottom, 0));
right: 1rem;
z-index: 30;
transition: bottom 200ms ease-out;
Expand Down
34 changes: 32 additions & 2 deletions libs/chat/src/lib/styles/chat-tokens.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('ROOT_TOKEN_STYLES — edge-claim primitive', () => {

it('maps data-ngaf-chat-sidebar="open" to occupy-right', () => {
expect(ROOT_TOKEN_STYLES).toMatch(
/:root\[data-ngaf-chat-sidebar="open"\]\s*\{\s*--ngaf-chat-occupy-right:\s*var\(--ngaf-chat-sidebar-width-drawer/,
/:root\[data-ngaf-chat-sidebar="open"\]\s*\{[^}]*--ngaf-chat-occupy-right:\s*var\(--ngaf-chat-sidebar-width-drawer/,
);
});

Expand All @@ -69,7 +69,37 @@ describe('ROOT_TOKEN_STYLES — edge-claim primitive', () => {
['left', '--ngaf-chat-occupy-left', '--ngaf-chat-debug-panel-size-w'],
])('maps data-ngaf-chat-debug=%s to %s via %s', (dock, occupyVar, sizeVar) => {
const pattern = new RegExp(
`:root\\[data-ngaf-chat-debug="${dock}"\\]\\s*\\{\\s*${occupyVar}:\\s*var\\(${sizeVar}`,
`:root\\[data-ngaf-chat-debug="${dock}"\\]\\s*\\{[^}]*${occupyVar}:\\s*var\\(${sizeVar}`,
);
expect(ROOT_TOKEN_STYLES).toMatch(pattern);
});

// ── per-component claim vars (peer-only reads) ────────────────────────
// Components must NOT read their own aggregate claim (would feedback).
// Each component publishes a per-component claim var that peers read.
it.each([
'--ngaf-chat-sidebar-claim-right: 0px;',
'--ngaf-chat-debug-claim-top: 0px;',
'--ngaf-chat-debug-claim-right: 0px;',
'--ngaf-chat-debug-claim-bottom: 0px;',
'--ngaf-chat-debug-claim-left: 0px;',
])('defines per-component default %s', (decl) => {
expect(ROOT_TOKEN_STYLES).toContain(decl);
});

it('sidebar attribute mapping also sets per-component claim var', () => {
expect(ROOT_TOKEN_STYLES).toMatch(
/:root\[data-ngaf-chat-sidebar="open"\]\s*\{[^}]*--ngaf-chat-sidebar-claim-right:\s*var\(--ngaf-chat-sidebar-width-drawer/,
);
});

it.each([
['bottom', '--ngaf-chat-debug-claim-bottom', '--ngaf-chat-debug-panel-size-h'],
['right', '--ngaf-chat-debug-claim-right', '--ngaf-chat-debug-panel-size-w'],
['left', '--ngaf-chat-debug-claim-left', '--ngaf-chat-debug-panel-size-w'],
])('debug attribute mapping for %s also sets %s', (dock, claimVar, sizeVar) => {
const pattern = new RegExp(
`:root\\[data-ngaf-chat-debug="${dock}"\\]\\s*\\{[^}]*${claimVar}:\\s*var\\(${sizeVar}`,
);
expect(ROOT_TOKEN_STYLES).toMatch(pattern);
});
Expand Down
21 changes: 20 additions & 1 deletion libs/chat/src/lib/styles/chat-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,27 @@ const EDGE_CLAIM_TOKENS = `
Each docked panel publishes the edge it occupies via a
data-ngaf-chat-* attribute on <html>; other panels read these
custom properties to leave room. Defaults to 0px so consumers
not using chat-sidebar/chat-debug see zero overhead. */
not using chat-sidebar/chat-debug see zero overhead.

TWO LAYERS:
1. Per-component claim vars (--ngaf-chat-<component>-claim-<edge>)
are read by PEERS only — never by the component that wrote
them. This eliminates self-feedback (where a right-docked
panel would offset itself by reading its own claim).
2. Aggregate occupy-* vars are convenience reads for external
consumers and for cases where any-panel-on-edge matters. */
--ngaf-chat-occupy-top: 0px;
--ngaf-chat-occupy-right: 0px;
--ngaf-chat-occupy-bottom: 0px;
--ngaf-chat-occupy-left: 0px;

/* Per-component claims (peer-only reads). */
--ngaf-chat-sidebar-claim-right: 0px;
--ngaf-chat-debug-claim-top: 0px;
--ngaf-chat-debug-claim-right: 0px;
--ngaf-chat-debug-claim-bottom: 0px;
--ngaf-chat-debug-claim-left: 0px;

/* Sizes the chat-debug dock contributes when it claims an edge.
Split by orientation so consumers can override independently. */
--ngaf-chat-debug-panel-size-h: 40vh;
Expand Down Expand Up @@ -327,15 +342,19 @@ export const ROOT_TOKEN_STYLES = `
chat-sidebar sets data-ngaf-chat-sidebar="open" while its panel is open.
chat-debug sets data-ngaf-chat-debug to its current dock while open. */
:root[data-ngaf-chat-sidebar="open"] {
--ngaf-chat-sidebar-claim-right: var(--ngaf-chat-sidebar-width-drawer, 28rem);
--ngaf-chat-occupy-right: var(--ngaf-chat-sidebar-width-drawer, 28rem);
}
:root[data-ngaf-chat-debug="bottom"] {
--ngaf-chat-debug-claim-bottom: var(--ngaf-chat-debug-panel-size-h, 40vh);
--ngaf-chat-occupy-bottom: var(--ngaf-chat-debug-panel-size-h, 40vh);
}
:root[data-ngaf-chat-debug="right"] {
--ngaf-chat-debug-claim-right: var(--ngaf-chat-debug-panel-size-w, 420px);
--ngaf-chat-occupy-right: var(--ngaf-chat-debug-panel-size-w, 420px);
}
:root[data-ngaf-chat-debug="left"] {
--ngaf-chat-debug-claim-left: var(--ngaf-chat-debug-panel-size-w, 420px);
--ngaf-chat-occupy-left: var(--ngaf-chat-debug-panel-size-w, 420px);
}
}
Expand Down
Loading