fix(eink): render Customize Toolbar preview as bordered surface, not black bar (#4839) (#4841)

The Customize Toolbar sub-page shows a content-width preview of the live
selection popup, copying its bg-gray-600 text-white styling. Unlike the real
reader popup (which gets its e-ink chrome from .popup-container in globals.css),
the preview Zone is a plain div with no e-ink override, so under
[data-eink='true'] the dark fill survived and the row painted as an unreadable
solid black bar.

Scope the dark fill to non-e-ink (not-eink:bg-gray-600 not-eink:text-white) and
let eink-bordered render the preview in e-ink as the popup's e-ink chrome: a
base-100 surface with a 1px base-content border. The chip icons already invert
to base-content via the global [data-eink] button rule. Also fall the empty-state
hint back to base-content in e-ink so it stays legible once the surface turns
base-100.

Verified via computed styles under [data-eink]: background oklch(1 0 0) (white),
1px oklch(0.2 0 0) border, dark icons — matching the reader's annotation toolbar.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-28 22:13:42 +08:00
committed by GitHub
parent 5f44c95592
commit 7972de1909
2 changed files with 70 additions and 3 deletions
@@ -0,0 +1,58 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { render, cleanup } from '@testing-library/react';
/**
* Regression guard for issue #4839 (display error under e-ink mode).
*
* The Customize Toolbar sub-page renders a content-width *preview* of the live
* selection popup. The preview surface uses `bg-gray-600 text-white` to mirror
* the real popup — but unlike the real popup (which earns its e-ink treatment
* from `.popup-container` in globals.css), the preview Zone is a plain div. With
* no e-ink override, the dark fill survives under `[data-eink='true']` and the
* whole row paints as an unreadable solid black bar.
*
* Invariant: the toolbar preview must carry `eink-bordered`, so e-ink swaps the
* dark fill for a `base-100` surface with a 1px `base-content` border — matching
* how the annotation toolbar renders in the reader.
*/
vi.mock('@/context/EnvContext', () => ({
useEnv: () => ({ envConfig: {}, appService: {} }),
}));
vi.mock('@/hooks/useTranslation', () => ({
useTranslation: () => (s: string) => s,
}));
vi.mock('@/store/readerStore', () => ({
useReaderStore: () => ({ getViewSettings: () => undefined }),
}));
vi.mock('@/store/settingsStore', () => ({
useSettingsStore: () => ({
settings: { globalViewSettings: { annotationToolbarItems: undefined } },
}),
}));
vi.mock('@/helpers/settings', () => ({
saveViewSettings: vi.fn(),
}));
vi.mock('@/utils/share', () => ({
canShareText: () => true,
}));
import AnnotationToolbarCustomizer from '@/components/settings/AnnotationToolbarCustomizer';
afterEach(() => {
cleanup();
});
describe('AnnotationToolbarCustomizer e-ink toolbar preview', () => {
it('marks the toolbar preview eink-bordered so e-ink swaps the dark fill for a bordered base-100 surface', () => {
const { container } = render(<AnnotationToolbarCustomizer bookKey='test' onBack={() => {}} />);
const toolbarPreview = container.querySelector('.selection-popup') as HTMLElement;
expect(toolbarPreview).not.toBeNull();
expect(toolbarPreview.classList.contains('eink-bordered')).toBe(true);
});
});
@@ -113,14 +113,23 @@ const Zone: React.FC<{
'flex min-h-12 flex-wrap items-center gap-2 rounded-lg p-2',
isToolbar
? // A faithful, content-width preview of the real popup, start-aligned
// with the Available row below it.
'selection-popup bg-gray-600 text-white w-fit max-w-full'
// with the Available row below it. Off e-ink it mirrors the popup's
// dark fill; in e-ink the dark fill is dropped entirely and
// `eink-bordered` renders it as the popup's e-ink chrome instead
// (.popup-container): a base-100 surface with a 1px base-content
// border, so it doesn't paint as a solid black bar (#4839).
'selection-popup eink-bordered w-fit max-w-full not-eink:bg-gray-600 not-eink:text-white'
: 'bg-base-200/60',
)}
>
{items.length === 0 ? (
<span
className={clsx('px-1 text-sm', isToolbar ? 'text-white/70' : 'text-base-content/50')}
className={clsx(
'px-1 text-sm',
// In e-ink the toolbar surface turns base-100, so the white hint would
// vanish; fall back to base-content there (#4839).
isToolbar ? 'not-eink:text-white/70 eink:text-base-content' : 'text-base-content/50',
)}
>
{emptyHint}
</span>