diff --git a/src/content.resize.test.ts b/src/content.resize.test.ts
index 1aca53e..f02f73a 100644
--- a/src/content.resize.test.ts
+++ b/src/content.resize.test.ts
@@ -17,7 +17,7 @@ describe('resize handling', () => {
});
it('re-renders the grid when the window is resized', async () => {
- const stored = normalizeSettings(DEFAULT_SETTINGS);
+ const stored = normalizeSettings({ ...DEFAULT_SETTINGS, enabled: true });
vi.stubGlobal('chrome', {
runtime: {
getURL: (path: string) => `chrome-extension://test-id/${path}`,
diff --git a/src/content.test.ts b/src/content.test.ts
index 75bdc06..490d0b2 100644
--- a/src/content.test.ts
+++ b/src/content.test.ts
@@ -95,9 +95,15 @@ afterEach(() => {
});
describe('initial render', () => {
- it('renders the overlay with the default stretch column grid', async () => {
+ it('does not render the overlay while disabled by default', async () => {
await loadContentScript();
+ expect(document.getElementById(GRID_OVERLAY_ID)).toBeNull();
+ });
+
+ it('renders the overlay with the default stretch column grid when enabled', async () => {
+ await loadContentScript({ enabled: true });
+
const overlay = getOverlay();
const gridLayer = overlay.querySelector('.grid-ui__layer') as HTMLElement;
const frame = gridLayer.firstElementChild as HTMLElement;
@@ -106,10 +112,10 @@ describe('initial render', () => {
expect(frame.style.gridTemplateColumns).toBe(`repeat(${DEFAULT_SETTINGS.count}, minmax(0, 1fr))`);
});
- it('reads the site identity from the page metadata for the popover header', async () => {
+ it('reads the site identity from the page metadata for the popover header when enabled', async () => {
document.head.innerHTML = '';
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const title = document.querySelector('.grid-ui__popover-title');
expect(title?.textContent).toBe('Acme Corp');
@@ -118,7 +124,7 @@ describe('initial render', () => {
describe('visibility toggle', () => {
it('hides the grid layer and flips the trigger icon state', async () => {
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const overlay = getOverlay();
const axisTrigger = overlay.querySelector('.grid-ui__anchor--start') as HTMLButtonElement;
const gridLayer = overlay.querySelector('.grid-ui__layer') as HTMLElement;
@@ -131,7 +137,7 @@ describe('visibility toggle', () => {
});
it('persists the change to chrome.storage.sync after the debounce delay', async () => {
- const { chromeMock, getStoredSettings } = await loadContentScript();
+ const { chromeMock, getStoredSettings } = await loadContentScript({ enabled: true });
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] });
const overlay = getOverlay();
@@ -149,7 +155,7 @@ describe('visibility toggle', () => {
describe('adjust popover', () => {
it('opens on trigger click and closes when clicked again', async () => {
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const overlay = getOverlay();
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
const popover = overlay.querySelector('[data-popover="adjust"]') as HTMLElement;
@@ -166,7 +172,7 @@ describe('adjust popover', () => {
describe('pattern picker', () => {
it('lists all variations and defaults regardless of the active axis', async () => {
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const overlay = getOverlay();
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
const patternTrigger = overlay.querySelector('.grid-ui__pattern-trigger') as HTMLButtonElement;
@@ -202,7 +208,7 @@ describe('pattern picker', () => {
});
it('applies defaults onto the active variation and can snapshot a new version', async () => {
- const { getStoredSettings } = await loadContentScript();
+ const { getStoredSettings } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
const patternTrigger = overlay.querySelector('.grid-ui__pattern-trigger') as HTMLButtonElement;
@@ -241,7 +247,7 @@ describe('pattern picker', () => {
describe('measurement controls', () => {
it('updates the count and the rendered grid when the count slider changes', async () => {
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const overlay = getOverlay();
const countRange = overlay.querySelector('.grid-ui__field--slider input[min="1"][max="24"]') as HTMLInputElement;
@@ -253,7 +259,7 @@ describe('measurement controls', () => {
});
it('hides the size field for stretch and shows it for a fixed distribution', async () => {
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const overlay = getOverlay();
const sizeField = overlay.querySelector('.grid-ui__field--slider--color')
?.parentElement?.querySelectorAll('label')[1] as HTMLLabelElement | undefined;
@@ -272,7 +278,7 @@ describe('measurement controls', () => {
describe('axis switching', () => {
it('resets the distribution and re-renders the grid for the new axis', async () => {
- const { getStoredSettings } = await loadContentScript();
+ const { getStoredSettings } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const rowsOption = overlay.querySelector('[data-axis="rows"]') as HTMLButtonElement;
@@ -298,7 +304,7 @@ describe('axis switching', () => {
describe('disabling', () => {
it('removes the overlay from the DOM and persists enabled:false immediately when closed', async () => {
- const { chromeMock, getStoredSettings } = await loadContentScript();
+ const { chromeMock, getStoredSettings } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const closeTrigger = overlay.querySelector('.grid-ui__close') as HTMLButtonElement;
@@ -313,12 +319,12 @@ describe('disabling', () => {
describe('background/popup messaging', () => {
it('re-renders when a GRID_SETTINGS_UPDATED message arrives', async () => {
- const { emitMessage } = await loadContentScript();
+ const { emitMessage } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
emitMessage({
type: GRID_MESSAGE_TYPE,
- settings: { ...DEFAULT_SETTINGS, axis: 'grid', size: 70 },
+ settings: { ...DEFAULT_SETTINGS, enabled: true, axis: 'grid', size: 70 },
});
const frame = (overlay.querySelector('.grid-ui__layer') as HTMLElement).firstElementChild as HTMLElement;
@@ -326,12 +332,12 @@ describe('background/popup messaging', () => {
});
it('shows only size and color controls in grid mode', async () => {
- const { emitMessage } = await loadContentScript();
+ const { emitMessage } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
emitMessage({
type: GRID_MESSAGE_TYPE,
- settings: { ...DEFAULT_SETTINGS, axis: 'grid', size: 70 },
+ settings: { ...DEFAULT_SETTINGS, enabled: true, axis: 'grid', size: 70 },
});
const adjustTrigger = overlay.querySelector('.grid-ui__anchor--center') as HTMLButtonElement;
@@ -359,7 +365,7 @@ describe('background/popup messaging', () => {
});
it('ignores messages with an unrelated type', async () => {
- const { emitMessage } = await loadContentScript();
+ const { emitMessage } = await loadContentScript({ enabled: true });
const overlay = getOverlay();
const frameBefore = (overlay.querySelector('.grid-ui__layer') as HTMLElement).firstElementChild as HTMLElement;
const childCountBefore = frameBefore.children.length;
@@ -373,7 +379,7 @@ describe('background/popup messaging', () => {
describe('controller drag', () => {
it('adds and removes dragging class during pointer interaction', async () => {
- await loadContentScript();
+ await loadContentScript({ enabled: true });
const overlay = getOverlay();
const controller = overlay.querySelector('.grid-ui__controller') as HTMLElement;
diff --git a/src/site-patterns.ts b/src/site-patterns.ts
index 639a91c..cd88e35 100644
--- a/src/site-patterns.ts
+++ b/src/site-patterns.ts
@@ -219,6 +219,19 @@ function normalizeSession(
};
}
+function mergeSessionSettings(
+ settings: GridSettings,
+ siteState: SiteGridState,
+): GridSettings {
+ return normalizeSettings({
+ ...settings,
+ enabled: siteState.session?.enabled ?? settings.enabled,
+ visible: siteState.session?.visible ?? settings.visible,
+ toolbarX: siteState.session?.toolbarX ?? settings.toolbarX,
+ toolbarY: siteState.session?.toolbarY ?? settings.toolbarY,
+ });
+}
+
function mergePresetPattern(
pattern: GridPattern,
siteState: SiteGridState,
@@ -227,19 +240,13 @@ function mergePresetPattern(
color: DEFAULT_SETTINGS.color,
opacity: DEFAULT_SETTINGS.opacity,
};
- const session: Partial> =
- siteState.session ?? {};
return {
...pattern,
- settings: normalizeSettings({
- ...pattern.settings,
- ...appearance,
- enabled: session.enabled ?? DEFAULT_SETTINGS.enabled,
- visible: session.visible ?? DEFAULT_SETTINGS.visible,
- toolbarX: session.toolbarX ?? DEFAULT_SETTINGS.toolbarX,
- toolbarY: session.toolbarY ?? DEFAULT_SETTINGS.toolbarY,
- }),
+ settings: mergeSessionSettings(
+ normalizeSettings({ ...pattern.settings, ...appearance }),
+ siteState,
+ ),
};
}
@@ -400,7 +407,10 @@ export function getActivePattern(siteState: SiteGridState): GridPattern {
return mergePresetPattern(pattern, siteState);
}
- return pattern;
+ return {
+ ...pattern,
+ settings: mergeSessionSettings(pattern.settings, siteState),
+ };
}
export function getPatternsForAxis(
@@ -448,7 +458,6 @@ export function selectPattern(siteState: SiteGridState, patternId: string): Site
...siteState,
activePatternId: patternId,
appearance: undefined,
- session: undefined,
};
}
@@ -458,13 +467,20 @@ export function ensureAxisPattern(siteState: SiteGridState, axis: GridAxis): Sit
return selectPattern(siteState, existingVariation.id);
}
+ const currentSettings = getActivePattern(siteState).settings;
const template = getFirstPresetForAxis(siteState, axis)?.settings ?? { ...DEFAULT_SETTINGS, axis };
- const nextPattern = createVariationPattern(siteState.patterns, template);
+ const nextPattern = createVariationPattern(siteState.patterns, {
+ ...template,
+ enabled: currentSettings.enabled,
+ visible: currentSettings.visible,
+ toolbarX: currentSettings.toolbarX,
+ toolbarY: currentSettings.toolbarY,
+ });
return {
activePatternId: nextPattern.id,
patterns: [nextPattern, ...siteState.patterns],
appearance: undefined,
- session: undefined,
+ session: siteState.session,
};
}
@@ -473,30 +489,30 @@ export function updateActivePatternSettings(
settings: GridSettings,
): SiteGridState {
const normalized = normalizeSettings(settings);
+ const currentSettings = getActivePattern(siteState).settings;
+
+ if (!patternSettingsChanged(currentSettings, normalized)) {
+ return {
+ ...siteState,
+ session: {
+ enabled: normalized.enabled,
+ visible: normalized.visible,
+ toolbarX: normalized.toolbarX,
+ toolbarY: normalized.toolbarY,
+ },
+ };
+ }
+
const rawActive = siteState.patterns.find((pattern) => pattern.id === siteState.activePatternId);
if (rawActive?.kind === 'preset') {
- const currentSettings = getActivePattern(siteState).settings;
-
- if (!patternSettingsChanged(currentSettings, normalized)) {
- return {
- ...siteState,
- session: {
- enabled: normalized.enabled,
- visible: normalized.visible,
- toolbarX: normalized.toolbarX,
- toolbarY: normalized.toolbarY,
- },
- };
- }
-
const nextPattern = createVariationPattern(siteState.patterns, normalized);
return {
...siteState,
activePatternId: nextPattern.id,
patterns: [nextPattern, ...siteState.patterns],
appearance: undefined,
- session: undefined,
+ session: siteState.session,
};
}
@@ -505,7 +521,7 @@ export function updateActivePatternSettings(
...ensuredState,
activePatternId: getActivePattern(ensuredState).id,
appearance: undefined,
- session: undefined,
+ session: siteState.session,
patterns: ensuredState.patterns.map((pattern) =>
pattern.id === ensuredState.activePatternId
? {
@@ -537,7 +553,12 @@ export function applyPatternSelection(siteState: SiteGridState, patternId: strin
color: currentSettings.color,
opacity: currentSettings.opacity,
},
- session: siteState.session,
+ session: siteState.session ?? {
+ enabled: currentSettings.enabled,
+ visible: currentSettings.visible,
+ toolbarX: currentSettings.toolbarX,
+ toolbarY: currentSettings.toolbarY,
+ },
};
}
diff --git a/src/utils.ts b/src/utils.ts
index 94605b7..7d64bdb 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -36,7 +36,7 @@ const MIN_SPACING = 0;
const MAX_SPACING = 240;
export const DEFAULT_SETTINGS: GridSettings = {
- enabled: true,
+ enabled: false,
visible: true,
toolbarX: null,
toolbarY: null,