mirror of
https://github.com/ZSeven-W/openpencil.git
synced 2026-05-31 19:04:29 +07:00
fix(ai): make role part-word guard position-sensitive
Follow-up on 5e2e6f9. The ROLE_PART_WORDS guard I added rejected any
name containing a part word anywhere, which misfired on "Icon Button":
- button pattern matches
- skipContainers=true
- ROLE_PART_WORDS.test("icon button") is true (icon is a part word)
- continue → fall through to /\bicon\b/ → returns 'icon' ❌
The intent of the guard is to catch "Card Header" / "Button Label" —
structural pieces named "<role> <part>". But "Icon Button" means "a
button OF THE ICON VARIETY", not "an icon inside a button". Word order
matters: part words are only piece-markers when they appear AFTER the
role word.
Switch the loop from pattern.test to pattern.exec so we know where the
role keyword sits, then only apply ROLE_PART_WORDS against the
substring AFTER the match. "Card Header" still skips (header after
card). "Icon Button" correctly returns 'button' (no part word after
button). Symmetric cases also work out: "Button Icon" falls through
card/button, hits the icon pattern and returns 'icon', which is the
right answer for "an icon frame inside a button context".
Inline coverage extended to 30 role cases including Icon/Primary/
Submit/Text/Image/Media modifier variants on both button and card.
This commit is contained in:
parent
5e2e6f9dae
commit
d45f1a573b
1 changed files with 17 additions and 9 deletions
|
|
@ -146,17 +146,25 @@ function inferRoleFromName(node: PenNode): string | undefined {
|
|||
|
||||
// Pattern match
|
||||
for (const [pattern, role, skipContainers] of NAME_PATTERN_MAP) {
|
||||
if (pattern.test(lower)) {
|
||||
// Use exec (not test) so we know WHERE in the name the role word sits.
|
||||
// Position matters for the ROLE_PART_WORDS guard below.
|
||||
const match = pattern.exec(lower);
|
||||
if (!match) continue;
|
||||
|
||||
if (skipContainers) {
|
||||
// Skip container-like names (e.g. "Button Group", "Buttons Row")
|
||||
if (skipContainers) {
|
||||
if (CONTAINER_SUFFIXES.test(lower)) continue;
|
||||
// Also skip "Card Header", "Card Body", "Button Label", etc. — the
|
||||
// role word is modified by a structural part word, meaning the
|
||||
// node is a PIECE of the role, not the role itself.
|
||||
if (ROLE_PART_WORDS.test(lower)) continue;
|
||||
}
|
||||
return role;
|
||||
if (CONTAINER_SUFFIXES.test(lower)) continue;
|
||||
// Skip "Card Header", "Card Body", "Button Label", etc. — when the
|
||||
// part word appears AFTER the role word, the node is a PIECE of
|
||||
// the role, not the role itself. Crucially, we only look at the
|
||||
// text AFTER the match: "Icon Button" must still become 'button',
|
||||
// because there the part word ("icon") appears BEFORE the role
|
||||
// word and is just a modifier ("a button of the icon variety"),
|
||||
// not an internal piece of the button.
|
||||
const afterMatch = lower.slice(match.index + match[0].length);
|
||||
if (ROLE_PART_WORDS.test(afterMatch)) continue;
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
|
|
|||
Loading…
Reference in a new issue