feat(sw): viewer layer popup bulb icon and all-layers switch

User-approved 2026-10-22 change to the SW Viewer layer popup.
- New public export SWLayerVisibilityIcon({ on }) + SWLayerVisibilityIconProps:
  the SW Header layer bulb art reused as is (header design and behavior unchanged).
- Layer rows: eye (Eye / EyeOff) icon replaced by the bulb, 24 x 24 button kept.
- All layers: the 전체 켜기 / 전체 끄기 buttons replaced by one SWViewerField +
  SWViewerSwitch on top. ON only when every layer is shown; a partial state reads
  OFF and a click shows all. Derived from the hidden-layer state, so it stays synced.
- AI docs (VIEWER.md 4.1, catalog, design rules, composition, AI_GUIDE sync) and
  example H follow the new rule, replacing the 2026-10-21 eye-icon rule.
- viewer.browser.mjs: layer popup checks moved to the bulb / switch behavior.
This commit is contained in:
shkim4 committed 2026-10-22 14:18:45 +09:00
1 parent 1dd8fb3aad
commit 8cb98b373f
16 files changed
+79 -52

No files matched your search

+15 -14
View File
@@ -183,22 +183,23 @@ if (comparison.reference) {
check('collapse hides body', !(await layerPanel.locator('.hm-sw-property__body').isVisible()))
await title.dblclick({ position: { x: 30, y: 15 } })
check('title double click expands', await layerPanel.locator('.hm-sw-property__body').isVisible())
// Panel content works (layer eye).
// 전체 켜기 / 전체 끄기: green on hover and while pressed, default otherwise; other panel buttons unchanged.
const allOn = layerPanel.getByRole('button', { name: '전체 켜기' })
// Panel content works. 2026-10-22: per-layer bulb buttons (SWLayerVisibilityIcon) + one all-layers switch on top
// (ON only when every layer is shown; a partial state reads OFF and a click shows all).
const face = locator => locator.evaluate(element => [getComputedStyle(element).backgroundColor, getComputedStyle(element).color].join(' '))
const restFace = await face(allOn)
await allOn.hover()
const hoverFace = await face(allOn)
await page.mouse.down()
const activeFace = await face(allOn)
await page.mouse.up()
const afterFace = await face(allOn)
await page.mouse.move(5, 5)
const leftFace = await face(allOn)
check('전체 켜기: default kept, green hover / active, back after release', restFace === 'rgb(255, 255, 255) rgb(18, 51, 40)' && hoverFace === 'rgb(38, 102, 81) rgb(255, 255, 255)' && activeFace === 'rgb(18, 51, 40) rgb(255, 255, 255)' && afterFace === hoverFace && leftFace === restFace, [restFace, hoverFace, activeFace, afterFace, leftFace].join(' | '))
const allSwitch = layerPanel.getByRole('switch', { name: '전체 레이어' })
const switchOn = async () => (await allSwitch.getAttribute('aria-checked')) === 'true'
const hiddenRows = () => layerPanel.locator('li[data-visible="false"]').count()
check('layer rows use the header bulb icon', (await layerPanel.locator('li button .hm-sw-header__layer-icon--composite').count()) === (await layerPanel.locator('li').count()) && (await layerPanel.locator('li button svg').count()) === 0)
check('all-layers switch ON when all shown, no 전체 켜기 / 끄기 buttons', await switchOn() && (await layerPanel.getByRole('button', { name: /전체 (켜기|끄기)/ }).count()) === 0)
await layerPanel.getByRole('button', { name: 'CR-TEXT 숨기기' }).click()
check('panel content callback', (await log(page)).includes('layer:CR-TEXT=false'))
check('one layer hidden -> switch OFF (partial)', !(await switchOn()) && (await hiddenRows()) === 1)
await allSwitch.click()
check('partial + switch -> all shown, switch ON', await switchOn() && (await hiddenRows()) === 0)
await allSwitch.click()
check('switch OFF -> all hidden', !(await switchOn()) && (await hiddenRows()) === (await layerPanel.locator('li').count()))
for (const button of await layerPanel.locator('li button').all()) await button.click()
check('each layer shown again -> switch ON (synced)', await switchOn() && (await hiddenRows()) === 0)
// Close button hover face.
const close = layerPanel.getByRole('button', { name: '닫기' })
await close.hover()
@@ -219,7 +220,7 @@ if (comparison.reference) {
const confirm = about.getByRole('button', { name: '확인' })
await confirm.hover()
const confirmHover = await face(confirm)
check('전체 켜기 hover = 라이선스 확인 hover (same token)', confirmHover === hoverFace, `${confirmHover} / ${hoverFace}`)
check('라이선스 확인 hover = --hm-sw-viewer-hover face', confirmHover === 'rgb(38, 102, 81) rgb(255, 255, 255)', confirmHover)
await about.locator('.hm-sw-property__title').hover()
check('modal popup opens with backdrop', (await about.getAttribute('aria-modal')) === 'true' && (await page.locator('.hm-sw-viewer-panel-backdrop').count()) === 1)
for (let index = 0; index < 4; index++) await page.keyboard.press('Tab')