feat(sw): refine headers, status label, popups and AI guide rules
Deploy Storybook / deploy (push) Successful in 1m10s
Deploy Storybook / deploy (push) Successful in 1m10s
- SW Header: the five field previews hide and return together in one measured step; from 760 to 1920px the groups from 레이어 to 치수스타일 share the room left before 속성보기, above 1920px they keep the 1920 widths (controls bars and < 760px unchanged) - SW Viewer Header Model / Layout: long names ellipsize in equal buttons (minmax(0, 1fr), name counted at --hm-sw-viewer-tab-name-basis) so they never change the box or the measured steps; green outer outline and its tokens removed; 378px / 42px, text-shadow and state unchanged - SWViewerStatus: the label keeps the width of its widest real state text (idle keeps its icon slot), so a state change never moves the header; with too little room it ellipsizes after the file name gives way - Popups: body inset shadow removed, title SVG drop shadow removed, viewer panel title icons use --hm-sw-viewer-panel-icon-color (#c7bda9) - Storybook: SW/Viewer › Model / Layout story removed - AI guide (docs/ai + package copies): design-only changes for every component, content limits for Submodule consumers (Header excluded), Submodule usage rules; VIEWER / DESIGN_RULES synced - viewer.browser.mjs: explicit 1920 / 1440 labels and approved 1280 icon-only expectations
This commit is contained in:
1 parent
325ec25e37
commit
db03e155dd
19 files changed
+190
-119
No files matched your search
@@ -326,6 +326,9 @@ if (comparison.reference) {
|
||||
tabsEqual: tabWidths.every(width => Math.abs(width - tabWidths[0]) <= 0.5),
|
||||
tabsFit,
|
||||
labels: [...element.querySelectorAll('.hm-sw-viewer-header__button, .hm-sw-viewer-header__tab, .hm-sw-viewer-header__switch-label')].map(item => item.textContent.trim() || item.getAttribute('aria-label')),
|
||||
// Buttons shown as icons only by the measured steps (name = aria-label / title) and labels drawn as text.
|
||||
iconOnly: [...element.querySelectorAll('.hm-sw-viewer-header__button[data-icon-only=""]')].map(item => item.getAttribute('aria-label') || item.getAttribute('title') || item.textContent.trim()).sort(),
|
||||
shownLabels: [...element.querySelectorAll('.hm-sw-viewer-header__label')].filter(item => rect(item).width > 0).map(item => item.textContent.trim()),
|
||||
}
|
||||
})
|
||||
report.push({ rowWidth: width, ...info })
|
||||
@@ -335,10 +338,16 @@ if (comparison.reference) {
|
||||
const at = width => report.find(item => item.rowWidth === width)
|
||||
check('rows render at their own width', report.every(item => item.width === item.rowWidth), JSON.stringify(report.map(item => [item.rowWidth, item.width])))
|
||||
check('no horizontal overflow, overlap or part outside the header at any row', report.every(item => item.overflow <= 0 && item.objectOverflow <= 1 && !item.overlaps && !item.outside), JSON.stringify(report.map(item => [item.rowWidth, item.overflow, item.objectOverflow, item.overlaps, item.outside])))
|
||||
// 2026-10-23: the Model / Layout field widens with the free room up to 378px (1920: 378), narrower with less room;
|
||||
// the buttons always share one width and keep name + count apart. With this Story's content 1920 / 1440 / 1280 show
|
||||
// every item (no More); narrower rows move collapseBelow items (Model / Layout included) into More by the measured steps.
|
||||
check('Model / Layout field: 378px at 1920, at most 378px and narrower with less room', at(1920).field === 378 && report.every(item => item.field === null || item.field <= 378) && at(1440).field >= at(1280).field, JSON.stringify(report.map(item => [item.rowWidth, item.field])))
|
||||
// 2026-10-23: the Model / Layout field widens with the free room up to 378px (1920: 378), narrower with less room at
|
||||
// the same step (1920 → 1440, both with every label); the buttons always share one width and keep name + count apart.
|
||||
// With this Story's content 1920 / 1440 / 1280 show every item (no More); narrower rows move collapseBelow items
|
||||
// (Model / Layout included) into More by the measured steps.
|
||||
check('Model / Layout field: 378px at 1920, at most 378px and narrower with less room (1920 → 1440)', at(1920).field === 378 && report.every(item => item.field === null || item.field <= 378) && at(1440).field <= at(1920).field, JSON.stringify(report.map(item => [item.rowWidth, item.field])))
|
||||
// 1920 / 1440 (unchanged): every button with its label, none icon only.
|
||||
check('1920 / 1440: 전체 보기 · 레이어 · 설정 · 속성 labelled, no icon-only button', [1920, 1440].every(width => !at(width).iconOnly.length && ['전체 보기', '레이어', '설정', '속성'].every(name => at(width).shownLabels.includes(name))), JSON.stringify([1920, 1440].map(width => [width, at(width).shownLabels, at(width).iconOnly])))
|
||||
// 1280 (2026-10-23, user-approved): the status label keeps the width of its widest text (SWViewerStatus), so at 1280
|
||||
// the measured icon step turns exactly the three panel toggles into icons; 전체 보기 keeps its label, nothing is in More.
|
||||
check('1280: exactly 레이어 · 설정 · 속성 icon only, 전체 보기 labelled, no More', JSON.stringify(at(1280).iconOnly) === JSON.stringify(['레이어', '설정', '속성'].sort()) && at(1280).shownLabels.includes('전체 보기') && !['레이어', '설정', '속성'].some(name => at(1280).shownLabels.includes(name)) && !at(1280).more, JSON.stringify([at(1280).shownLabels, at(1280).iconOnly, at(1280).more]))
|
||||
check('Model / Layout buttons equal, name and count never overlap', report.every(item => item.tabsEqual && item.tabsFit))
|
||||
check('1920 / 1440 / 1280: every item in the header, no More', [1920, 1440, 1280].every(width => !at(width).more && at(width).field && ['전체 보기', '2.5D', '어두운 배경', '레이어'].every(name => at(width).labels.includes(name))))
|
||||
// 950 sits at the edge of the measured steps (icons only or More, depending on the font rendering); both fit.
|
||||
|
||||
Reference in new issue
Block a user