Skip to content
Open
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
33 changes: 33 additions & 0 deletions openless-all/app/e2e/motion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,39 @@ test('multimodal settings fit one desktop page and keep inactive notices below l
});
});

test('pipeline selection stays evenly inset in both modes', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.getByRole('button', { name: '设置', exact: true }).click();
const dialog = page.getByRole('dialog', { name: '设置', exact: true });
await dialog.getByRole('button', { name: 'AI 与模型', exact: true }).click();
const track = dialog.locator('.ol-service-pipeline-track');
for (const name of ['传统模式', '多模态模式']) {
await dialog.getByRole('button', { name, exact: true }).click();
await expect
.poll(() =>
track.evaluate((element, selectedName) => {
const outer = element.getBoundingClientRect();
const selected = element.querySelector<HTMLElement>('button[aria-pressed="true"]')!;
const chip = element.querySelector<HTMLElement>('.ol-segmented-indicator')!;
const item = selected.getBoundingClientRect();
const inner = chip.getBoundingClientRect();
return Math.max(
Math.abs(inner.top - item.top),
Math.abs(inner.left - item.left),
Math.abs(inner.width - item.width),
Math.abs(inner.height - item.height),
Math.abs(inner.top - outer.top - (outer.bottom - inner.bottom)),
Math.abs(
(selectedName === '传统模式' ? inner.left - outer.left : outer.right - inner.right) -
(inner.top - outer.top),
),
);
}, name),
)
.toBeLessThan(0.75);
}
});

test('selection highlights move and settle correctly after a rapid mode return', async ({
page,
}) => {
Expand Down
14 changes: 14 additions & 0 deletions openless-all/app/e2e/refinements.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ import { openMicrophoneSettings, settleNavigation } from './support/navigation';
import { test, expect, type Page } from '@playwright/test';

const fixture = 'http://127.0.0.1:5326/e2e/fixtures/refinements.html';

test('style and dictionary action buttons retain compact corners', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: '风格', exact: true }).click();
await page.getByRole('button', { name: '润色模式', exact: true }).click();
await expect(page.getByRole('button', { name: '导入 ZIP' })).toHaveCSS('border-radius', '8px');
await expect(page.getByRole('button', { name: '激活', exact: true }).first()).toHaveCSS(
'border-radius',
'8px',
);
await page.getByRole('button', { name: '词典', exact: true }).click();
await expect(page.locator('.ol-action-button').first()).toHaveCSS('border-radius', '8px');
});

async function openShortcuts(page: Page) {
await page.goto('/');
await page.getByRole('button', { name: '设置', exact: true }).click();
Expand Down
39 changes: 31 additions & 8 deletions openless-all/app/src/lib/motion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function useReducedMotion() {
);
}

type Frame = { opacity: string | number; transform: string };
type Frame = { opacity: string | number; transform: string; width?: string };
const VISIBLE: Frame = { opacity: 1, transform: 'none' };
const FADED: Frame = { opacity: 0, transform: 'none' };
const POP_HIDDEN: Frame = { opacity: 0, transform: 'translate3d(0, 18px, 0) scale(0.84)' };
Expand Down Expand Up @@ -236,28 +236,51 @@ export function useSelectionMotion(
}
// Layout offsets stay correct while the parent dialog is scaled on entry.
const y = underline ? group.clientHeight - 2 : button.offsetTop;
const target = `translate3d(${button.offsetLeft}px, ${y}px, 0) scaleX(${button.offsetWidth / 100})`;
if (target === lastTarget) return;
const from = interrupted.current ?? currentFrame(indicator);
const target = `translate3d(${button.offsetLeft}px, ${y}px, 0)`;
const width = `${button.offsetWidth}px`;
const targetKey = `${target}:${width}`;
if (targetKey === lastTarget) return;
const from = interrupted.current ?? {
...currentFrame(indicator),
width: getComputedStyle(indicator).width,
};
interrupted.current = null;
motion?.cancel();
indicator.style.transform = target;
indicator.style.width = width;
indicator.style.height = `${underline ? 2 : button.offsetHeight}px`;
indicator.style.visibility = 'visible';
lastTarget = target;
lastTarget = targetKey;
if (moving && !reduced && !keyboardInteraction() && typeof indicator.animate === 'function') {
motion = animate(indicator, from, { opacity: 1, transform: target }, ENTER_MS, false);
motion = animate(
indicator,
from,
{ opacity: 1, transform: target, width },
ENTER_MS,
false,
);
motion.animation.id = 'ol-selection-move';
void motion.animation.finished.then(motion.cancel).catch(() => {});
}
};
position(previous.current !== null && previous.current !== selection);
previous.current = selection;
const observer = new ResizeObserver(() => position(false));
let resizeFrame = 0;
const observer = new ResizeObserver(() => {
if (resizeFrame) return;
resizeFrame = requestAnimationFrame(() => {
resizeFrame = 0;
position(false);
});
});
observer.observe(group);
return () => {
interrupted.current = currentFrame(indicator);
interrupted.current = {
...currentFrame(indicator),
width: getComputedStyle(indicator).width,
};
observer.disconnect();
cancelAnimationFrame(resizeFrame);
motion?.cancel();
};
}, [groupRef, indicatorRef, selection, underline, reduced]);
Expand Down
2 changes: 1 addition & 1 deletion openless-all/app/src/pages/Vocab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ export function Vocab() {
icon={wordAdded ? 'check' : 'plus'}
onClick={() => void addNewWord()}
style={{
borderRadius: 999,
borderRadius: 8,
height: 38,
minWidth: 92,
justifyContent: 'center',
Expand Down
2 changes: 1 addition & 1 deletion openless-all/app/src/pages/_atoms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export function Btn({
const muted = disabled && (variant === 'primary' || variant === 'blue');
return (
<button
className="ol-pressable"
className="ol-pressable ol-action-button"
onClick={disabled ? undefined : onClick}
disabled={disabled}
aria-label={ariaLabel}
Expand Down
12 changes: 12 additions & 0 deletions openless-all/app/src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,18 @@ html[data-ol-conservative-layout='true'] .ol-conservative-scope .ol-select-lite-
border-radius: 8px;
background: var(--ol-surface);
}
.ol-service-pipeline-track {
padding: 3px;
border: 0;
box-shadow: inset 0 0 0 0.5px var(--ol-line-strong);
}
.ol-service-pipeline-track > :is(.ol-segmented-indicator, .ol-segmented-option) {
--ol-corner-radius: calc(var(--ol-control-radius, 8px) - 3px);
border-radius: var(--ol-corner-radius);
}
.ol-action-button {
--ol-corner-radius: 8px;
}
.ol-segmented-indicator,
.ol-service-view-indicator {
position: absolute;
Expand Down
Loading