@monoes/monodesign 1.2.0 → 1.2.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.
@@ -3568,6 +3568,13 @@ function checkElementImageDimensions(el, style) {
3568
3568
  }
3569
3569
 
3570
3570
  function checkElementImageDimensionsDOM(el) {
3571
+ // Only <img> can have this problem. The static engine gets this for free —
3572
+ // it registers the rule with `selector: 'img'` (engines/static-html/
3573
+ // detect-html.mjs) — but the DOM walker calls every check against every
3574
+ // element, so the tag filter has to live here. Without it this rule fired on
3575
+ // <head>, <meta>, <title> and <style>: a clean 84-line fixture reported 43
3576
+ // findings, all of them "an <img> ships without width and height".
3577
+ if (el.tagName !== 'IMG') return [];
3571
3578
  const hasWidthAttr = el.getAttribute('width') != null;
3572
3579
  const hasHeightAttr = el.getAttribute('height') != null;
3573
3580
  // Browser computed `height` always resolves to a used px value after layout,
@@ -2845,6 +2845,13 @@ function checkElementImageDimensions(el, style) {
2845
2845
  }
2846
2846
 
2847
2847
  function checkElementImageDimensionsDOM(el) {
2848
+ // Only <img> can have this problem. The static engine gets this for free —
2849
+ // it registers the rule with `selector: 'img'` (engines/static-html/
2850
+ // detect-html.mjs) — but the DOM walker calls every check against every
2851
+ // element, so the tag filter has to live here. Without it this rule fired on
2852
+ // <head>, <meta>, <title> and <style>: a clean 84-line fixture reported 43
2853
+ // findings, all of them "an <img> ships without width and height".
2854
+ if (el.tagName !== 'IMG') return [];
2848
2855
  const hasWidthAttr = el.getAttribute('width') != null;
2849
2856
  const hasHeightAttr = el.getAttribute('height') != null;
2850
2857
  // Browser computed `height` always resolves to a used px value after layout,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monodesign",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Frontend design intelligence for AI agents \u2014 design tokens, antipattern detection, and the monodesign skill",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -45,7 +45,7 @@
45
45
  "marked": "^18.0.0"
46
46
  },
47
47
  "optionalDependencies": {
48
- "@monoes/monobrowse": "^1.0.3",
48
+ "@monoes/monobrowse": "^1.0.6",
49
49
  "puppeteer": "^25.1.0"
50
50
  }
51
51
  }
@@ -1112,7 +1112,8 @@ applyLegacyDeferredAcceptsOnStartup();
1112
1112
  restorePendingEventsFromStore();
1113
1113
  manualApply.pruneStaleEvidence();
1114
1114
  const portArg = args.find(a => a.startsWith('--port='));
1115
- state.port = portArg ? parseInt(portArg.split('=')[1], 10) : await findOpenPort();
1115
+ const explicitPort = portArg ? parseInt(portArg.split('=')[1], 10) : null;
1116
+ state.port = explicitPort ?? await findOpenPort();
1116
1117
  // Annotation screenshots live in the project root so the agent's Read tool
1117
1118
  // doesn't trip a per-file permission prompt. Sessioned by token so concurrent
1118
1119
  // projects (or quick restarts) don't collide.
@@ -1123,7 +1124,43 @@ state.sessionDir = fs.mkdtempSync(path.join(annotRoot, 'session-'));
1123
1124
  const { detectScript, liveScriptParts } = loadBrowserScripts();
1124
1125
  httpServer = http.createServer(createRequestHandler({ detectScript, liveScriptParts }));
1125
1126
 
1127
+ // findOpenPort() probes a port, closes the probe socket, and only then does the
1128
+ // real server bind it. Another process scanning the same range can take the
1129
+ // port inside that gap, which surfaced as an intermittent
1130
+ // "EADDRINUSE 127.0.0.1:8405" — about one run in five of the test suite, where
1131
+ // several server-starting test files run in parallel.
1132
+ //
1133
+ // Retry on the next port when we picked it ourselves. An explicit --port is
1134
+ // never silently moved: the caller asked for that port, so a conflict there
1135
+ // must be an error they can see.
1136
+ const MAX_PORT_RETRIES = 25;
1137
+ let portRetries = 0;
1138
+
1139
+ httpServer.on('error', (err) => {
1140
+ if (err?.code !== 'EADDRINUSE') throw err;
1141
+
1142
+ if (explicitPort !== null) {
1143
+ console.error(`\nPort ${state.port} is already in use.`);
1144
+ console.error('Another live server may be running — stop it, or pass a different --port.');
1145
+ process.exit(1);
1146
+ }
1147
+
1148
+ if (portRetries >= MAX_PORT_RETRIES) {
1149
+ console.error(`\nCould not find a free port after ${MAX_PORT_RETRIES} attempts (last tried ${state.port}).`);
1150
+ process.exit(1);
1151
+ }
1152
+
1153
+ portRetries++;
1154
+ state.port++;
1155
+ httpServer.listen(state.port, '127.0.0.1');
1156
+ });
1157
+
1126
1158
  httpServer.listen(state.port, '127.0.0.1', () => {
1159
+ // Trust the address the OS actually bound over the one we asked for — after
1160
+ // a retry above, state.port and the bound port must not drift apart.
1161
+ const boundAddr = httpServer.address();
1162
+ if (boundAddr && typeof boundAddr === 'object') state.port = boundAddr.port;
1163
+
1127
1164
  writeLiveServerInfo(process.cwd(), { pid: process.pid, port: state.port, token: state.token });
1128
1165
  const url = `http://localhost:${state.port}`;
1129
1166
  console.log(`\nMonodesign live server running on ${url}`);