@mp3wizard/figma-console-mcp 1.30.1 → 1.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,12 +6,12 @@
6
6
 
7
7
  // Plugin version — sent in FILE_INFO for server-side version compatibility checks.
8
8
  // The server compares this against its own version to detect stale cached plugins.
9
- var PLUGIN_VERSION = '1.30.0'; // Kept in sync with package.json by scripts/release.sh — see issue #62.
9
+ var PLUGIN_VERSION = '1.32.0'; // Kept in sync with package.json by scripts/release.sh — see issue #62.
10
10
 
11
11
  console.log('🌉 [Desktop Bridge] Plugin loaded (v' + PLUGIN_VERSION + ')');
12
12
 
13
13
  // Show minimal UI - compact status indicator
14
- figma.showUI(__html__, { width: 180, height: 50, visible: true, themeColors: true });
14
+ figma.showUI(__html__, { width: 240, height: 40, visible: true, themeColors: true });
15
15
 
16
16
  // ============================================================================
17
17
  // CONSOLE CAPTURE — Intercept console.* in the QuickJS sandbox and forward
@@ -3178,7 +3178,7 @@ figma.ui.onmessage = async (msg) => {
3178
3178
  });
3179
3179
  // Short delay to let the response message be sent before reload
3180
3180
  setTimeout(function() {
3181
- figma.showUI(__html__, { width: 180, height: 50, visible: true, themeColors: true });
3181
+ figma.showUI(__html__, { width: 240, height: 40, visible: true, themeColors: true });
3182
3182
  }, 100);
3183
3183
  } catch (error) {
3184
3184
  var errorMsg = error && error.message ? error.message : String(error);
@@ -3360,6 +3360,34 @@ figma.ui.onmessage = async (msg) => {
3360
3360
  return false;
3361
3361
  }
3362
3362
 
3363
+ // Line/paragraph SPACING only affects readability when text actually renders on
3364
+ // 2+ lines (or 2+ paragraphs). Single-line labels, buttons, inputs and headings
3365
+ // gain nothing from a 1.5 line height, so flagging them is false-positive noise.
3366
+ // (WCAG 1.4.12 conformance is a code concern — whether spacing overrides break
3367
+ // layout — not something a design's spacing value can prove. These stay best-practice.)
3368
+ function textRendersMultipleLines(node, effectiveLh) {
3369
+ try {
3370
+ var chars = (typeof node.characters === 'string') ? node.characters : '';
3371
+ // \n = paragraph break; U+2028 = line break (shift-enter) within a Figma paragraph
3372
+ var LINE_SEP = String.fromCharCode(0x2028);
3373
+ if (chars.indexOf(String.fromCharCode(10)) !== -1 || chars.indexOf(LINE_SEP) !== -1) return true;
3374
+ // WIDTH_AND_HEIGHT auto-resize grows horizontally and never wraps on its own
3375
+ if (node.textAutoResize === 'WIDTH_AND_HEIGHT') return false;
3376
+ if (effectiveLh && typeof node.height === 'number' && node.height > 0) {
3377
+ return (node.height / effectiveLh) >= 1.6; // ≈ 2+ rendered lines
3378
+ }
3379
+ } catch (e) { /* mixed/slot — treat as single line */ }
3380
+ return false;
3381
+ }
3382
+
3383
+ function textHasMultipleParagraphs(node) {
3384
+ try {
3385
+ var chars = (typeof node.characters === 'string') ? node.characters : '';
3386
+ return chars.indexOf('\n') !== -1; // \n = paragraph separator in Figma text
3387
+ } catch (e) { /* ignore */ }
3388
+ return false;
3389
+ }
3390
+
3363
3391
  // ---- Rule configuration ----
3364
3392
  var allRuleIds = [
3365
3393
  'wcag-contrast', 'wcag-text-size', 'wcag-target-size', 'wcag-line-height',
@@ -3373,13 +3401,23 @@ figma.ui.onmessage = async (msg) => {
3373
3401
 
3374
3402
  var ruleGroups = {
3375
3403
  'all': allRuleIds,
3404
+ // 'wcag' = genuine WCAG conformance criteria only. Readability "best practice"
3405
+ // checks (text size, line/letter/paragraph spacing) live in 'best-practice' so a
3406
+ // conformance audit (rules: ['wcag']) is not polluted by non-normative hints.
3407
+ // In particular, WCAG 1.4.12 Text Spacing is a "support user overrides without
3408
+ // breaking" criterion — NOT a requirement to ship specific spacing values — so a
3409
+ // sub-1.5 line height is not a conformance failure (see 'best-practice' below).
3376
3410
  'wcag': [
3377
- 'wcag-contrast', 'wcag-text-size', 'wcag-target-size', 'wcag-line-height',
3411
+ 'wcag-contrast', 'wcag-target-size',
3378
3412
  'wcag-non-text-contrast', 'wcag-color-only', 'wcag-focus-indicator',
3379
- 'wcag-letter-spacing', 'wcag-paragraph-spacing', 'wcag-image-alt',
3380
- 'wcag-heading-hierarchy', 'wcag-reflow', 'wcag-reading-order',
3413
+ 'wcag-image-alt', 'wcag-heading-hierarchy', 'wcag-reflow', 'wcag-reading-order',
3381
3414
  'wcag-disabled-no-context'
3382
3415
  ],
3416
+ // Readability best practices — useful hints, NOT WCAG conformance failures.
3417
+ // Opt in with rules: ['best-practice'] (or ['all']); excluded from the default set.
3418
+ 'best-practice': [
3419
+ 'wcag-text-size', 'wcag-line-height', 'wcag-letter-spacing', 'wcag-paragraph-spacing'
3420
+ ],
3383
3421
  'design-system': ['hardcoded-color', 'no-text-style', 'default-name', 'detached-component', 'token-misuse'],
3384
3422
  'layout': ['no-autolayout', 'empty-container']
3385
3423
  };
@@ -3437,12 +3475,12 @@ figma.ui.onmessage = async (msg) => {
3437
3475
  'wcag-contrast': 'Text does not meet WCAG AA contrast ratio (4.5:1 normal, 3:1 large text ≥24px or ≥18.5px bold). Best practice: always target 4.5:1, especially in dark mode.',
3438
3476
  'wcag-text-size': 'Text size is below 12px — readability best practice. Note: WCAG 1.4.4 requires supporting 200% text-only zoom (use rem/em units), not a specific minimum size.',
3439
3477
  'wcag-target-size': 'Interactive element is smaller than 24x24px minimum target size (WCAG 2.5.8)',
3440
- 'wcag-line-height': 'Line height is below 1.5x font size — best practice for readability. Note: WCAG 1.4.12 requires that content does not break when users override spacing to 1.5x, not that designs must use 1.5x by default.',
3478
+ 'wcag-line-height': 'Multi-line body text has line height below 1.5x font size — a readability best practice (only flagged on text that wraps to 2+ lines; single-line labels, buttons and headings are exempt). NOT a WCAG failure: 1.4.12 requires content to survive a user overriding line height to 1.5x without loss of content, which is verified in code, not by the default value in the design.',
3441
3479
  'wcag-non-text-contrast': 'UI component or graphical object does not meet 3:1 contrast ratio against adjacent color. Also applies to borders and chart elements against adjacent elements (WCAG 1.4.11)',
3442
3480
  'wcag-color-only': 'Information is conveyed only through color change (e.g., error state uses red border without an error message or icon). Color can supplement but must not be the sole indicator (WCAG 1.4.1)',
3443
3481
  'wcag-focus-indicator': 'Interactive component is missing a focus/focused variant or the focus indicator is insufficient. A visible focus state is critical — without it, keyboard users cannot navigate the interface (WCAG 2.4.7)',
3444
- 'wcag-letter-spacing': 'Negative letter spacing actively harms readability. WCAG 1.4.12 requires content to support user-overridden spacing without breaking.',
3445
- 'wcag-paragraph-spacing': 'Paragraph spacing is below 2x font size — best practice. WCAG 1.4.12 requires content to support user-overridden spacing to 2x without loss of content, not that designs must use 2x by default.',
3482
+ 'wcag-letter-spacing': 'Negative letter spacing (tighter than default) harms readability — a best-practice hint, not a WCAG failure. WCAG 1.4.12 is about supporting user-overridden spacing without loss of content (verified in code), not the default tracking in the design.',
3483
+ 'wcag-paragraph-spacing': 'Multi-paragraph text has paragraph spacing below 2x font size — a readability best practice (only flagged when a text node has 2+ paragraphs). NOT a WCAG failure: 1.4.12 requires content to survive a user overriding paragraph spacing to 2x without loss of content, verified in code, not by the default value in the design.',
3446
3484
  'wcag-image-alt': 'Image or image fill has no description annotation for alternative text. All images need alt text; decorative images should be explicitly marked as decorative. Graphs and charts also need long descriptions (e.g., a data table) (WCAG 1.1.1)',
3447
3485
  'wcag-heading-hierarchy': 'Heading levels skip a level (e.g., H1 followed by H3). Use H1 through H6 sequentially without skipping levels (WCAG 1.3.1)',
3448
3486
  'wcag-reflow': 'Frame uses fixed positioning without auto-layout. Content must support 400% zoom on 1280px viewport (equivalent to 320px minimum width) without horizontal scrolling or loss of content (WCAG 1.4.10)',
@@ -3550,7 +3588,10 @@ figma.ui.onmessage = async (msg) => {
3550
3588
  // (checked after tree walk per-frame)
3551
3589
 
3552
3590
  // ---- Resolve active rules ----
3553
- var requestedRules = msg.rules || ['all'];
3591
+ // Default audit = real WCAG conformance + design-system + layout. Best-practice
3592
+ // readability hints are opt-in (rules: ['best-practice'] or ['all']) so component
3593
+ // library audits aren't flooded with non-normative spacing/size noise.
3594
+ var requestedRules = msg.rules || ['wcag', 'design-system', 'layout'];
3554
3595
  var activeRuleSet = {};
3555
3596
  for (var ri = 0; ri < requestedRules.length; ri++) {
3556
3597
  var ruleOrGroup = requestedRules[ri];
@@ -3737,7 +3778,7 @@ figma.ui.onmessage = async (msg) => {
3737
3778
  effectiveLh = fs * (lh.value / 100);
3738
3779
  }
3739
3780
  }
3740
- if (effectiveLh !== null && effectiveLh < 1.5 * fs) {
3781
+ if (effectiveLh !== null && effectiveLh < 1.5 * fs && textRendersMultipleLines(node, effectiveLh)) {
3741
3782
  if (totalFindings < maxFindings) {
3742
3783
  findings['wcag-line-height'].push({
3743
3784
  id: nodeId,
@@ -3940,7 +3981,7 @@ figma.ui.onmessage = async (msg) => {
3940
3981
  var psFs = node.fontSize;
3941
3982
  if (typeof ps === 'number' && typeof psFs === 'number' && ps > 0) {
3942
3983
  var requiredPs = 2 * psFs;
3943
- if (ps < requiredPs) {
3984
+ if (ps < requiredPs && textHasMultipleParagraphs(node)) {
3944
3985
  if (totalFindings < maxFindings) {
3945
3986
  findings['wcag-paragraph-spacing'].push({
3946
3987
  id: nodeId,