@linktr.ee/arbor-mcp 0.9.0 → 0.10.0-rc.b7b2f343e.8215

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.
Files changed (2) hide show
  1. package/dist/index.js +223 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -309,6 +309,80 @@ var require_rules = __commonJS({
309
309
  ]
310
310
  }
311
311
  },
312
+ "directional-language": {
313
+ id: "directional-language",
314
+ name: "Directional language",
315
+ description: "Copy shouldn't rely on spatial directions. Element position changes across screen sizes and surfaces, and directional words are meaningless to screen-reader users.",
316
+ severity: "info",
317
+ category: "accessibility",
318
+ check: {
319
+ type: "regex",
320
+ description: "Flags spatial/directional references that don't survive layout changes across surfaces"
321
+ },
322
+ phrases: [
323
+ {
324
+ pattern: "\\b(?:(?:the|this|that|these|those)\\s+\\w+|see|shown|listed|described|mentioned|noted|explained|outlined)\\s+above\\b",
325
+ matchType: "regex",
326
+ label: "above",
327
+ reason: `"Above" is a spatial reference; element position changes across surfaces and it's meaningless to screen-reader users.`,
328
+ suggestion: 'Name the thing instead of its position, e.g. "in Settings" or "the previous step".'
329
+ },
330
+ {
331
+ pattern: "\\b(?:(?:the|this|that|these|those)\\s+\\w+|see|shown|listed|described|mentioned|noted|explained|outlined)\\s+below\\b",
332
+ matchType: "regex",
333
+ label: "below",
334
+ reason: `"Below" is a spatial reference; element position changes across surfaces and it's meaningless to screen-reader users.`,
335
+ suggestion: 'Name the thing instead of its position, e.g. "in Settings" or "the next step".'
336
+ },
337
+ {
338
+ pattern: "\\b(?:tap|press|go|see)\\s+here\\b",
339
+ matchType: "regex",
340
+ label: "directional 'here'",
341
+ reason: `Directional "here" gives weak information scent and doesn't work for screen readers.`,
342
+ suggestion: 'Use a specific action, e.g. "View pricing" or "Open settings".'
343
+ }
344
+ ]
345
+ },
346
+ "control-reference": {
347
+ id: "control-reference",
348
+ name: "Control reference",
349
+ description: `Copy shouldn't tell people to act on a control by its type ("click the button", "tap the link"). Screen-reader users don't perceive the control the same way, and the phrasing is redundant \u2014 label or hyperlink the action itself.`,
350
+ severity: "info",
351
+ category: "accessibility",
352
+ check: {
353
+ type: "regex",
354
+ description: "Flags instructions that reference a UI control by type instead of hyperlinking or labelling the action"
355
+ },
356
+ phrases: [
357
+ {
358
+ pattern: "\\b(?:clicking|click|tapping|tap|pressing|press|hitting|hit)\\s+(?:on\\s+)?(?:(?:the|this|that)\\s+)?(?:[\\w-]+\\s+){0,2}(?:button|link)\\b",
359
+ matchType: "regex",
360
+ label: "click the button",
361
+ reason: "Referencing a control by type is meaningless to screen-reader users and redundant on screen.",
362
+ suggestion: 'Hyperlink or label the action itself, e.g. make "Reset your password" the button or link text.'
363
+ }
364
+ ]
365
+ },
366
+ "blame-language": {
367
+ id: "blame-language",
368
+ name: "Blame language",
369
+ description: `Error copy shouldn't blame the Linker by calling their input "invalid". Name the specific requirement or the fix instead. ("invalid input" is handled by banned-phrases; this catches the general "invalid <thing>" shape.)`,
370
+ severity: "warning",
371
+ category: "content",
372
+ check: {
373
+ type: "regex",
374
+ description: 'Flags "invalid" used as blame ("invalid email", "invalid card"), excluding "invalid input(s)" which banned-phrases already covers'
375
+ },
376
+ phrases: [
377
+ {
378
+ pattern: "\\binvalid\\b(?!\\s+inputs?\\b)",
379
+ matchType: "regex",
380
+ label: "invalid",
381
+ reason: 'Calling input "invalid" blames the Linker and is vague.',
382
+ suggestion: 'Name the requirement or fix, e.g. "Double-check your email address" or "Email must include @".'
383
+ }
384
+ ]
385
+ },
312
386
  "word-count": {
313
387
  id: "word-count",
314
388
  name: "Word count limits",
@@ -1024,6 +1098,27 @@ var require_engine = __commonJS({
1024
1098
  return { ...phrase, _regex: null };
1025
1099
  }
1026
1100
  });
1101
+ var compiledDirectional = rules["directional-language"].phrases.map((phrase) => {
1102
+ try {
1103
+ return { ...phrase, _regex: new RegExp(phrase.pattern, "i") };
1104
+ } catch {
1105
+ return { ...phrase, _regex: null };
1106
+ }
1107
+ });
1108
+ var compiledControlReference = rules["control-reference"].phrases.map((phrase) => {
1109
+ try {
1110
+ return { ...phrase, _regex: new RegExp(phrase.pattern, "i") };
1111
+ } catch {
1112
+ return { ...phrase, _regex: null };
1113
+ }
1114
+ });
1115
+ var compiledBlameLanguage = rules["blame-language"].phrases.map((phrase) => {
1116
+ try {
1117
+ return { ...phrase, _regex: new RegExp(phrase.pattern, "i") };
1118
+ } catch {
1119
+ return { ...phrase, _regex: null };
1120
+ }
1121
+ });
1027
1122
  function countSyllables(word) {
1028
1123
  const w = word.toLowerCase().replace(/[^a-z]/g, "");
1029
1124
  if (w.length <= 3) return 1;
@@ -1077,6 +1172,49 @@ var require_engine = __commonJS({
1077
1172
  });
1078
1173
  }
1079
1174
  }
1175
+ function checkDirectionalLanguage(text, issues) {
1176
+ for (const phrase of compiledDirectional) {
1177
+ if (phrase._regex !== null && phrase._regex.test(text)) {
1178
+ issues.push({
1179
+ ruleId: "directional-language",
1180
+ ruleName: "Directional language",
1181
+ severity: "info",
1182
+ message: `"${phrase.label}" \u2014 ${phrase.reason}`,
1183
+ suggestion: phrase.suggestion
1184
+ });
1185
+ }
1186
+ }
1187
+ }
1188
+ function checkControlReference(text, issues) {
1189
+ for (const phrase of compiledControlReference) {
1190
+ if (phrase._regex === null) continue;
1191
+ const match = text.match(phrase._regex);
1192
+ if (match) {
1193
+ issues.push({
1194
+ ruleId: "control-reference",
1195
+ ruleName: "Control reference",
1196
+ severity: "info",
1197
+ message: `"${match[0] || phrase.label}" \u2014 ${phrase.reason}`,
1198
+ suggestion: phrase.suggestion
1199
+ });
1200
+ }
1201
+ }
1202
+ }
1203
+ function checkBlameLanguage(text, issues) {
1204
+ for (const phrase of compiledBlameLanguage) {
1205
+ if (phrase._regex === null) continue;
1206
+ const match = text.match(phrase._regex);
1207
+ if (match) {
1208
+ issues.push({
1209
+ ruleId: "blame-language",
1210
+ ruleName: "Blame language",
1211
+ severity: "warning",
1212
+ message: `"${match[0] || phrase.label}" \u2014 ${phrase.reason}`,
1213
+ suggestion: phrase.suggestion
1214
+ });
1215
+ }
1216
+ }
1217
+ }
1080
1218
  function checkGenericButtons(text, issues) {
1081
1219
  for (const entry of rules["generic-buttons"].bannedLabels) {
1082
1220
  if (entry.notes) continue;
@@ -1161,6 +1299,9 @@ var require_engine = __commonJS({
1161
1299
  if (!trimmed) return { score: 10, issues };
1162
1300
  checkSentenceCase(trimmed, issues);
1163
1301
  checkBannedPhrases(trimmed, contentType, issues);
1302
+ checkDirectionalLanguage(trimmed, issues);
1303
+ checkControlReference(trimmed, issues);
1304
+ checkBlameLanguage(trimmed, issues);
1164
1305
  if (contentType === "button") checkGenericButtons(trimmed, issues);
1165
1306
  checkPassiveVoice(trimmed, issues);
1166
1307
  if (contentType) checkWordCount(trimmed, contentType, issues);
@@ -5943,7 +6084,7 @@ var CONTENT_TYPES = [
5943
6084
  var MAX_TEXT_LENGTH = 5e3;
5944
6085
  var TOOL_NAME2 = "arbor_check_ux_writing";
5945
6086
  var TOOL_TITLE2 = "Check UX writing against Arbor voice rules";
5946
- var TOOL_DESCRIPTION2 = `Lint a UI string against Arbor's voice and UX-writing rules (sentence case, banned/generic phrases, passive voice, word count, error structure, readability) and return a 0\u201310 score plus structured issues with suggestions. Deterministic and offline \u2014 the same engine behind the Slack /ux-check command. Pass contentType (e.g. "button", "error-system") to enable context-specific checks. This judges copy quality; it does NOT rewrite the text.`;
6087
+ var TOOL_DESCRIPTION2 = `Lint a UI string against Arbor's voice and UX-writing rules (sentence case, banned/generic phrases, passive voice, word count, error structure, readability, accessibility checks like directional language and control references) and return a 0\u201310 score plus structured issues with suggestions. Deterministic and offline \u2014 the same engine behind the Slack /ux-check command. Pass contentType (e.g. "button", "error-system") to enable context-specific checks. This judges copy quality; it does NOT rewrite the text.`;
5947
6088
  var inputSchema2 = external_exports.object({
5948
6089
  text: external_exports.string().trim().min(1, "text is required and must be a non-empty string").max(MAX_TEXT_LENGTH, `text must be ${MAX_TEXT_LENGTH} characters or fewer`).describe("The UI string to lint"),
5949
6090
  contentType: external_exports.enum(CONTENT_TYPES).optional().describe("What kind of UI text this is \u2014 enables context-specific rules (omit for content-agnostic checks)")
@@ -7279,6 +7420,87 @@ var compositions_default = [
7279
7420
  name: "IndicatorBadge (custom color)",
7280
7421
  code: '<IndicatorBadge count={5} color="#7B61FF" />'
7281
7422
  },
7423
+ // ── Chip ──
7424
+ {
7425
+ group: "Chip",
7426
+ name: "Chip (filter row)",
7427
+ code: `<div className="flex flex-wrap items-center gap-2">
7428
+ <Chip selected onSelectedChange={() => {}}>Instagram</Chip>
7429
+ <Chip onSelectedChange={() => {}}>TikTok</Chip>
7430
+ <Chip onSelectedChange={() => {}}>YouTube</Chip>
7431
+ </div>`
7432
+ },
7433
+ {
7434
+ group: "Chip",
7435
+ name: "Chip (dismissible tags)",
7436
+ code: `<div className="flex flex-wrap items-center gap-2">
7437
+ <Chip removeLabel="Remove Design system" onRemove={() => {}}>Design system</Chip>
7438
+ <Chip removeLabel="Remove Infrastructure" onRemove={() => {}}>Infrastructure</Chip>
7439
+ </div>`
7440
+ },
7441
+ {
7442
+ group: "Chip",
7443
+ name: "Chip (sizes)",
7444
+ code: `<div className="flex flex-wrap items-center gap-2">
7445
+ <Chip size="md">Music</Chip>
7446
+ <Chip size="sm">Music</Chip>
7447
+ </div>`
7448
+ },
7449
+ {
7450
+ group: "Chip",
7451
+ name: "Chip (disabled)",
7452
+ code: "<Chip disabled onSelectedChange={() => {}}>Top 5</Chip>"
7453
+ },
7454
+ // ── Combobox ──
7455
+ // Imported via the @linktr.ee/arbor/components/Combobox sub-path (barrel-excluded
7456
+ // — optional @base-ui/react peer dep); surfaced in components.ts like AppIcon/OTP.
7457
+ {
7458
+ group: "Combobox",
7459
+ name: "Combobox (single)",
7460
+ code: `<Field.Root>
7461
+ <Combobox.Root items={["Design system", "Infrastructure", "Analytics", "Commerce"]}>
7462
+ <Combobox.Control>
7463
+ <Field.Label>Tag</Field.Label>
7464
+ <Combobox.Input />
7465
+ <Combobox.Trigger />
7466
+ </Combobox.Control>
7467
+ <Combobox.Content>
7468
+ <Combobox.Empty>No tags found</Combobox.Empty>
7469
+ <Combobox.List>
7470
+ {(tag) => (
7471
+ <Combobox.Item key={tag} value={tag}>
7472
+ {tag}
7473
+ </Combobox.Item>
7474
+ )}
7475
+ </Combobox.List>
7476
+ </Combobox.Content>
7477
+ </Combobox.Root>
7478
+ </Field.Root>`
7479
+ },
7480
+ {
7481
+ group: "Combobox",
7482
+ name: "Combobox (multiple with chips)",
7483
+ code: `<Field.Root>
7484
+ <Combobox.Root multiple defaultValue={["Design system"]} items={["Design system", "Infrastructure", "Analytics", "Commerce"]}>
7485
+ <Combobox.Control>
7486
+ <Combobox.Chips>
7487
+ <Combobox.Input placeholder="Search tags" aria-label="Tags" />
7488
+ </Combobox.Chips>
7489
+ <Combobox.Trigger />
7490
+ </Combobox.Control>
7491
+ <Combobox.Content>
7492
+ <Combobox.Empty>No tags found</Combobox.Empty>
7493
+ <Combobox.List>
7494
+ {(tag) => (
7495
+ <Combobox.Item key={tag} value={tag}>
7496
+ {tag}
7497
+ </Combobox.Item>
7498
+ )}
7499
+ </Combobox.List>
7500
+ </Combobox.Content>
7501
+ </Combobox.Root>
7502
+ </Field.Root>`
7503
+ },
7282
7504
  // ── Button ──
7283
7505
  {
7284
7506
  group: "Button",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/arbor-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.10.0-rc.b7b2f343e.8215",
4
4
  "description": "Model Context Protocol server exposing Arbor design system tools: Playroom snippets, Figma→code sync health, UX-writing checks, decision-log lookup, and federated component/version metadata.",
5
5
  "keywords": [
6
6
  "arbor",