stimeo-ui 0.2.1 → 0.3.0
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.
- package/CHANGELOG.md +104 -0
- package/dist/cable/index.d.ts +16 -16
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.js +10 -0
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +97 -10
- package/dist/controllers/breadcrumb_controller.js +225 -13
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +11 -1
- package/dist/controllers/calendar_controller.js +89 -22
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +16 -1
- package/dist/controllers/carousel_controller.js +47 -6
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.js +2 -2
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +12 -1
- package/dist/controllers/color_picker_controller.js +46 -7
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +14 -1
- package/dist/controllers/combobox_controller.js +162 -23
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +2 -0
- package/dist/controllers/command_palette_controller.js +194 -17
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +7 -6
- package/dist/controllers/context_menu_controller.js +32 -10
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/count_up_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +19 -1
- package/dist/controllers/data_grid_controller.js +82 -4
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +1 -0
- package/dist/controllers/date_range_picker_controller.js +27 -3
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +1 -2
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +1 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/focus_controller.d.ts +1 -1
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +1 -1
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +1 -1
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +36 -11
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +31 -10
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +9 -2
- package/dist/controllers/listbox_controller.js +257 -53
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/local_time_controller.js +2 -2
- package/dist/controllers/local_time_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +29 -11
- package/dist/controllers/menu_controller.js +104 -17
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +91 -11
- package/dist/controllers/menubar_controller.js +415 -63
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +31 -5
- package/dist/controllers/multi_select_controller.js +312 -29
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +65 -18
- package/dist/controllers/navigation_menu_controller.js +154 -27
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.d.ts +4 -5
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +1 -1
- package/dist/controllers/number_input_controller.js +7 -0
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/otp_controller.js +18 -1
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +3 -2
- package/dist/controllers/overflow_indicator_controller.js +81 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +78 -19
- package/dist/controllers/overflow_menu_controller.js +381 -57
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +36 -5
- package/dist/controllers/pagination_controller.js +163 -32
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +6 -6
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.d.ts +2 -3
- package/dist/controllers/pointer_drag_controller.js +9 -1
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +1 -2
- package/dist/controllers/popover_controller.js +2 -2
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +4 -4
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.js +22 -3
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +14 -4
- package/dist/controllers/range_slider_controller.js +32 -6
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.js +16 -2
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.js +63 -19
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/reading_progress_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +3 -1
- package/dist/controllers/resizable_controller.js +65 -1
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/roving_controller.js +17 -2
- package/dist/controllers/roving_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +86 -12
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_restore_controller.js +1 -1
- package/dist/controllers/scroll_restore_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.d.ts +6 -3
- package/dist/controllers/scroll_visibility_controller.js +33 -3
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +64 -15
- package/dist/controllers/scrollspy_controller.js +346 -73
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +20 -5
- package/dist/controllers/separator_controller.js +9 -0
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +1 -1
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +1 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +11 -1
- package/dist/controllers/slider_controller.js +32 -6
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.d.ts +3 -1
- package/dist/controllers/sortable_controller.js +34 -3
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +1 -1
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +5 -6
- package/dist/controllers/stick_to_bottom_controller.js +2 -1
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/sticky_observer_controller.js +32 -11
- package/dist/controllers/sticky_observer_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +2 -2
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.js +1 -0
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +11 -3
- package/dist/controllers/tabs_controller.js +26 -3
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.js +22 -2
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +1 -1
- package/dist/controllers/theme_controller.js +22 -3
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +20 -1
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +4 -5
- package/dist/controllers/toast_controller.js +4 -5
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.js +23 -2
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.d.ts +53 -13
- package/dist/controllers/toolbar_controller.js +230 -31
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +1 -1
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +87 -6
- package/dist/controllers/tree_view_controller.js +467 -51
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.js +3514 -689
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +375 -39
- package/dist/inspector/cli.js +228 -14
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +419 -48
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +24 -24
- package/dist/inspector/manifest.json +1040 -125
- package/package.json +5 -6
|
@@ -269,12 +269,17 @@ var DIAGNOSTIC_CODES = [
|
|
|
269
269
|
"unknown-action-method",
|
|
270
270
|
"orphan-target",
|
|
271
271
|
"missing-required-target",
|
|
272
|
+
"missing-conditional-target",
|
|
272
273
|
"missing-aria",
|
|
273
274
|
"invalid-aria-value",
|
|
274
275
|
"keyboard-inaccessible",
|
|
275
276
|
"unresolved-idref",
|
|
276
277
|
"managed-aria",
|
|
277
278
|
"composition-mismatch",
|
|
279
|
+
"missing-companion",
|
|
280
|
+
"undeclared-target",
|
|
281
|
+
"cardinality-violation",
|
|
282
|
+
"forbidden-aria",
|
|
278
283
|
"unknown-ignore-code"
|
|
279
284
|
];
|
|
280
285
|
|
|
@@ -292,6 +297,14 @@ function checkSource(source, manifest) {
|
|
|
292
297
|
if (valueStart === void 0 || valueEnd === void 0) return false;
|
|
293
298
|
return erbRanges.some(([start, end]) => start < valueEnd && end > valueStart);
|
|
294
299
|
};
|
|
300
|
+
const conditionHolds = (scope, identifier, when) => {
|
|
301
|
+
if (!when) return true;
|
|
302
|
+
const hostAttr = scope.attrs.find(
|
|
303
|
+
(a) => a.name === `data-${identifier}-${dasherize(when.value)}-value`
|
|
304
|
+
);
|
|
305
|
+
if (hostAttr && isDynamicValue(hostAttr)) return null;
|
|
306
|
+
return when.equals.includes(hostAttr ? hostAttr.value.trim() : when.default);
|
|
307
|
+
};
|
|
295
308
|
const suppressed = (node, code) => {
|
|
296
309
|
if (code === "unknown-ignore-code") return false;
|
|
297
310
|
let current = node;
|
|
@@ -339,7 +352,40 @@ function checkSource(source, manifest) {
|
|
|
339
352
|
if (found) found.push(el);
|
|
340
353
|
else byName.set(target, [el]);
|
|
341
354
|
};
|
|
355
|
+
const contentHolds = (scope, identifier, element, condition) => {
|
|
356
|
+
if (!condition) return true;
|
|
357
|
+
const nodes = targetNodes.get(scope)?.get(identifier)?.get(condition.target) ?? [];
|
|
358
|
+
let held = 0;
|
|
359
|
+
for (const node of nodes) if (isWithin(node, element)) held += 1;
|
|
360
|
+
if (condition.min !== void 0 && held < condition.min) return false;
|
|
361
|
+
if (condition.max !== void 0 && held > condition.max) return false;
|
|
362
|
+
return true;
|
|
363
|
+
};
|
|
364
|
+
const elementHolds = (element, condition) => !condition?.exceptTags.includes(element.tag);
|
|
365
|
+
const roleTally = /* @__PURE__ */ new Map();
|
|
366
|
+
const focusableRoleTally = /* @__PURE__ */ new Map();
|
|
367
|
+
const documentHolds = (condition) => {
|
|
368
|
+
if (condition === void 0) return false;
|
|
369
|
+
const tally = condition.focusable ? focusableRoleTally : roleTally;
|
|
370
|
+
return (tally.get(condition.role) ?? 0) >= condition.atLeast;
|
|
371
|
+
};
|
|
372
|
+
const declarationAttrs = /* @__PURE__ */ new Set();
|
|
373
|
+
for (const controller of Object.values(known)) {
|
|
374
|
+
for (const rule of controller.targetDeclarations) declarationAttrs.add(rule.attr);
|
|
375
|
+
}
|
|
376
|
+
const declarationCandidates = [];
|
|
342
377
|
walk(tree, (node) => {
|
|
378
|
+
if (declarationAttrs.size > 0 && node.attrs.some((a) => declarationAttrs.has(a.name))) {
|
|
379
|
+
declarationCandidates.push(node);
|
|
380
|
+
}
|
|
381
|
+
const roleAttr = node.attrs.find((a) => a.name === "role");
|
|
382
|
+
if (roleAttr && !isDynamicValue(roleAttr)) {
|
|
383
|
+
const role = roleAttr.value.trim();
|
|
384
|
+
roleTally.set(role, (roleTally.get(role) ?? 0) + 1);
|
|
385
|
+
if (isFocusable(node, "tab")) {
|
|
386
|
+
focusableRoleTally.set(role, (focusableRoleTally.get(role) ?? 0) + 1);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
343
389
|
for (const attr of node.attrs) {
|
|
344
390
|
if (attr.name === IGNORE_ATTR) {
|
|
345
391
|
for (const token of attr.value.split(/\s+/).filter((t) => t.length > 0)) {
|
|
@@ -418,7 +464,7 @@ function checkSource(source, manifest) {
|
|
|
418
464
|
);
|
|
419
465
|
continue;
|
|
420
466
|
}
|
|
421
|
-
const targetName = attr.value.trim();
|
|
467
|
+
const targetName = isDynamicValue(attr) ? "" : attr.value.trim();
|
|
422
468
|
if (targetName.length > 0 && !controller.targets.includes(targetName)) {
|
|
423
469
|
const best = nearestName(targetName, controller.targets);
|
|
424
470
|
report(
|
|
@@ -522,13 +568,36 @@ function checkSource(source, manifest) {
|
|
|
522
568
|
}
|
|
523
569
|
}
|
|
524
570
|
}
|
|
571
|
+
for (const { node, identifier } of scopes) {
|
|
572
|
+
const controller = known[identifier];
|
|
573
|
+
if (!controller) continue;
|
|
574
|
+
const present = presence.get(node)?.get(identifier) ?? /* @__PURE__ */ new Set();
|
|
575
|
+
for (const rule of controller.conditionalTargets) {
|
|
576
|
+
if (!present.has(rule.whenPresent)) continue;
|
|
577
|
+
const missing = rule.require.filter((target) => !present.has(target));
|
|
578
|
+
if (missing.length === 0) continue;
|
|
579
|
+
report(
|
|
580
|
+
node,
|
|
581
|
+
"missing-conditional-target",
|
|
582
|
+
"error",
|
|
583
|
+
`"${identifier}" has a "${rule.whenPresent}" target, which also requires ${list(missing)}.`,
|
|
584
|
+
node,
|
|
585
|
+
rule.suggestion
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
525
589
|
for (const { node, identifier } of scopes) {
|
|
526
590
|
const controller = known[identifier];
|
|
527
591
|
if (!controller) continue;
|
|
528
592
|
for (const req of controller.a11y) {
|
|
593
|
+
if (conditionHolds(node, identifier, req.when) !== true) continue;
|
|
529
594
|
const elements = req.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(req.target) ?? [];
|
|
595
|
+
if (req.whenDocument && !documentHolds(req.whenDocument)) continue;
|
|
596
|
+
const severity = documentHolds(req.escalateWhen) ? "error" : req.severity ?? "error";
|
|
530
597
|
for (const el of elements) {
|
|
531
|
-
|
|
598
|
+
if (!contentHolds(node, identifier, el, req.whenContains)) continue;
|
|
599
|
+
if (!elementHolds(el, req.whenElement)) continue;
|
|
600
|
+
checkA11y(report, el, identifier, req, severity);
|
|
532
601
|
}
|
|
533
602
|
}
|
|
534
603
|
}
|
|
@@ -575,6 +644,32 @@ function checkSource(source, manifest) {
|
|
|
575
644
|
}
|
|
576
645
|
}
|
|
577
646
|
}
|
|
647
|
+
for (const { node, identifier } of scopes) {
|
|
648
|
+
const controller = known[identifier];
|
|
649
|
+
if (!controller) continue;
|
|
650
|
+
for (const rule of controller.forbiddenAria) {
|
|
651
|
+
const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
|
|
652
|
+
const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
|
|
653
|
+
for (const el of elements) {
|
|
654
|
+
if (!contentHolds(node, identifier, el, rule.whenContains)) continue;
|
|
655
|
+
for (const name of rule.attrs) {
|
|
656
|
+
const attr = el.attrs.find((a) => a.name === name);
|
|
657
|
+
if (!attr) continue;
|
|
658
|
+
if (rule.values && (isDynamicValue(attr) || !rule.values.includes(attr.value.trim()))) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
report(
|
|
662
|
+
el,
|
|
663
|
+
"forbidden-aria",
|
|
664
|
+
"warning",
|
|
665
|
+
`${name} on the ${where} of "${identifier}" contradicts the markup around it.`,
|
|
666
|
+
attr,
|
|
667
|
+
rule.suggestion
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
578
673
|
for (const { node, identifier } of scopes) {
|
|
579
674
|
const controller = known[identifier];
|
|
580
675
|
if (!controller) continue;
|
|
@@ -583,14 +678,7 @@ function checkSource(source, manifest) {
|
|
|
583
678
|
const elements = rule.target === "" ? [node] : targeted ?? (rule.fallbackToScope ? [node] : []);
|
|
584
679
|
const where = rule.target === "" || !targeted ? "scope element" : `"${rule.target}" target`;
|
|
585
680
|
const when = rule.when;
|
|
586
|
-
if (when)
|
|
587
|
-
const hostAttr = node.attrs.find(
|
|
588
|
-
(a) => a.name === `data-${identifier}-${dasherize(when.value)}-value`
|
|
589
|
-
);
|
|
590
|
-
if (hostAttr && isDynamicValue(hostAttr)) continue;
|
|
591
|
-
const hostEffective = hostAttr ? hostAttr.value.trim() : when.default;
|
|
592
|
-
if (!when.equals.includes(hostEffective)) continue;
|
|
593
|
-
}
|
|
681
|
+
if (conditionHolds(node, identifier, when) !== true) continue;
|
|
594
682
|
const hostContext = when ? ` (${when.value} ${list(when.equals)})` : "";
|
|
595
683
|
for (const el of elements) {
|
|
596
684
|
const declared = el.attrs.find((a) => a.name === "data-controller");
|
|
@@ -626,6 +714,113 @@ function checkSource(source, manifest) {
|
|
|
626
714
|
}
|
|
627
715
|
}
|
|
628
716
|
}
|
|
717
|
+
for (const { node, identifier } of scopes) {
|
|
718
|
+
const controller = known[identifier];
|
|
719
|
+
if (!controller) continue;
|
|
720
|
+
for (const rule of controller.companions) {
|
|
721
|
+
const elements = rule.target === "" ? [node] : targetNodes.get(node)?.get(identifier)?.get(rule.target) ?? [];
|
|
722
|
+
const where = rule.target === "" ? "scope element" : `"${rule.target}" target`;
|
|
723
|
+
for (const el of elements) {
|
|
724
|
+
const declared = el.attrs.find((a) => a.name === "data-controller");
|
|
725
|
+
if (declared && isDynamicValue(declared)) continue;
|
|
726
|
+
if (declared && controllerIdentifiers(declared.value).includes(rule.controller)) continue;
|
|
727
|
+
report(
|
|
728
|
+
el,
|
|
729
|
+
"missing-companion",
|
|
730
|
+
"error",
|
|
731
|
+
`The ${where} of "${identifier}" must also declare "${rule.controller}" in data-controller.`,
|
|
732
|
+
declared ?? el,
|
|
733
|
+
rule.suggestion,
|
|
734
|
+
// Appending to an existing list is unambiguous; creating the
|
|
735
|
+
// attribute from nothing is an insertion the anchor cannot express.
|
|
736
|
+
declared ? valueFix(
|
|
737
|
+
declared,
|
|
738
|
+
`${declared.value.trim()} ${rule.controller}`.trim(),
|
|
739
|
+
`Add "${rule.controller}" to data-controller`
|
|
740
|
+
) : void 0
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
for (const { node, identifier } of scopes) {
|
|
746
|
+
const controller = known[identifier];
|
|
747
|
+
if (!controller) continue;
|
|
748
|
+
if (controller.targetDeclarations.length === 0) continue;
|
|
749
|
+
const targetAttrName = `data-${identifier}-target`;
|
|
750
|
+
for (const rule of controller.targetDeclarations) {
|
|
751
|
+
for (const el of declarationCandidates) {
|
|
752
|
+
const marker = el.attrs.find((a) => a.name === rule.attr);
|
|
753
|
+
if (!marker || isDynamicValue(marker)) continue;
|
|
754
|
+
const value = marker.value.trim();
|
|
755
|
+
if (value.length === 0) continue;
|
|
756
|
+
if (rule.values && !rule.values.includes(value)) continue;
|
|
757
|
+
if (findOwner(el, identifier) !== node) continue;
|
|
758
|
+
const declaredAs = el.attrs.find((a) => a.name === targetAttrName);
|
|
759
|
+
if (declaredAs && !isDynamicValue(declaredAs) && declaredAs.value.trim() === rule.target) {
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (declaredAs && isDynamicValue(declaredAs)) continue;
|
|
763
|
+
report(
|
|
764
|
+
el,
|
|
765
|
+
"undeclared-target",
|
|
766
|
+
"error",
|
|
767
|
+
`${rule.attr}="${value}" inside "${identifier}" is not declared as its "${rule.target}" target, so the controller never manages it.`,
|
|
768
|
+
marker,
|
|
769
|
+
rule.suggestion
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
for (const { node, identifier } of scopes) {
|
|
775
|
+
const controller = known[identifier];
|
|
776
|
+
if (!controller) continue;
|
|
777
|
+
for (const rule of controller.cardinality) {
|
|
778
|
+
if (conditionHolds(node, identifier, rule.when) !== true) continue;
|
|
779
|
+
const byName = targetNodes.get(node)?.get(identifier);
|
|
780
|
+
const counted = byName?.get(rule.target) ?? [];
|
|
781
|
+
const containers = rule.within === "" ? [node] : byName?.get(rule.within) ?? [];
|
|
782
|
+
const what = describeCounted(rule.target, rule.attr, rule.values);
|
|
783
|
+
const where = rule.within === "" ? "controller scope" : `"${rule.within}" target`;
|
|
784
|
+
for (const container of containers) {
|
|
785
|
+
const matched = [];
|
|
786
|
+
for (const el of counted) {
|
|
787
|
+
if (!isWithin(el, container)) continue;
|
|
788
|
+
if (rule.attr === void 0) {
|
|
789
|
+
matched.push({ el, at: el });
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
const attr = el.attrs.find((a) => a.name === rule.attr);
|
|
793
|
+
if (!attr) continue;
|
|
794
|
+
if (rule.values && (isDynamicValue(attr) || !rule.values.includes(attr.value.trim()))) {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
matched.push({ el, at: attr });
|
|
798
|
+
}
|
|
799
|
+
if (rule.max !== void 0 && matched.length > rule.max) {
|
|
800
|
+
for (const { el, at } of matched.slice(rule.max)) {
|
|
801
|
+
report(
|
|
802
|
+
el,
|
|
803
|
+
"cardinality-violation",
|
|
804
|
+
"error",
|
|
805
|
+
`"${identifier}" allows at most ${rule.max} ${what} per ${where}, but found ${matched.length}.`,
|
|
806
|
+
at,
|
|
807
|
+
rule.suggestion
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (rule.min !== void 0 && matched.length < rule.min) {
|
|
812
|
+
report(
|
|
813
|
+
container,
|
|
814
|
+
"cardinality-violation",
|
|
815
|
+
"error",
|
|
816
|
+
`"${identifier}" requires at least ${rule.min} ${what} per ${where}, but found ${matched.length}.`,
|
|
817
|
+
container,
|
|
818
|
+
rule.suggestion
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
629
824
|
const ids = /* @__PURE__ */ new Set();
|
|
630
825
|
walk(tree, (node) => {
|
|
631
826
|
const id = node.attrs.find((a) => a.name === "id");
|
|
@@ -663,7 +858,7 @@ function checkSource(source, manifest) {
|
|
|
663
858
|
diagnostics.sort((a, b) => a.line - b.line || a.column - b.column);
|
|
664
859
|
return diagnostics;
|
|
665
860
|
}
|
|
666
|
-
function checkA11y(report, el, identifier, req) {
|
|
861
|
+
function checkA11y(report, el, identifier, req, severity) {
|
|
667
862
|
const groups = [
|
|
668
863
|
{ attrs: req.attrs, values: req.values },
|
|
669
864
|
...req.or ?? []
|
|
@@ -692,11 +887,12 @@ function checkA11y(report, el, identifier, req) {
|
|
|
692
887
|
}
|
|
693
888
|
}
|
|
694
889
|
if (!anyPresent) {
|
|
890
|
+
const verb = severity === "warning" ? "recommends" : "requires";
|
|
695
891
|
report(
|
|
696
892
|
el,
|
|
697
893
|
"missing-aria",
|
|
698
|
-
|
|
699
|
-
`"${identifier}"
|
|
894
|
+
severity,
|
|
895
|
+
`"${identifier}" ${verb} ${describeAttrs([...allowedByAttr.keys()])} on its ${where}.`,
|
|
700
896
|
el,
|
|
701
897
|
req.suggestion
|
|
702
898
|
);
|
|
@@ -715,6 +911,19 @@ function checkA11y(report, el, identifier, req) {
|
|
|
715
911
|
);
|
|
716
912
|
}
|
|
717
913
|
}
|
|
914
|
+
function isWithin(node, container) {
|
|
915
|
+
let current = node;
|
|
916
|
+
while (current) {
|
|
917
|
+
if (current === container) return true;
|
|
918
|
+
current = current.parent;
|
|
919
|
+
}
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
function describeCounted(target, attr, values) {
|
|
923
|
+
if (attr === void 0) return `"${target}" targets`;
|
|
924
|
+
const constrained = values && values.length > 0 ? ` set to ${list(values)}` : "";
|
|
925
|
+
return `"${target}" targets with ${attr}${constrained}`;
|
|
926
|
+
}
|
|
718
927
|
var NATIVE_FOCUSABLE_TAGS = /* @__PURE__ */ new Set(["button", "select", "textarea", "summary"]);
|
|
719
928
|
function isNativelyFocusable(el) {
|
|
720
929
|
if (NATIVE_FOCUSABLE_TAGS.has(el.tag)) return true;
|
|
@@ -868,10 +1077,15 @@ var CONTROLLER_ARRAY_FIELDS = [
|
|
|
868
1077
|
"actions",
|
|
869
1078
|
"events",
|
|
870
1079
|
"requiredTargets",
|
|
1080
|
+
"conditionalTargets",
|
|
871
1081
|
"a11y",
|
|
872
1082
|
"keyboard",
|
|
873
1083
|
"managedAria",
|
|
874
|
-
"compositions"
|
|
1084
|
+
"compositions",
|
|
1085
|
+
"companions",
|
|
1086
|
+
"targetDeclarations",
|
|
1087
|
+
"cardinality",
|
|
1088
|
+
"forbiddenAria"
|
|
875
1089
|
];
|
|
876
1090
|
function isCompatibleManifest(value) {
|
|
877
1091
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -1297,6 +1511,41 @@ var EXAMPLE_RESULT_SCHEMA = {
|
|
|
1297
1511
|
},
|
|
1298
1512
|
required: ["id", "file", "source", "guidance"]
|
|
1299
1513
|
};
|
|
1514
|
+
var CONTENT_CONDITION_SCHEMA = {
|
|
1515
|
+
type: "object",
|
|
1516
|
+
description: "Condition on the element's own contents arming the rule; absent means it does not depend on them. Evaluated per element, unlike the scope-level `when`.",
|
|
1517
|
+
properties: {
|
|
1518
|
+
target: { type: "string", description: "Target counted inside the element." },
|
|
1519
|
+
min: { type: "integer", description: "Arms the rule when the count is at least this." },
|
|
1520
|
+
max: { type: "integer", description: "Arms the rule when the count is at most this." }
|
|
1521
|
+
},
|
|
1522
|
+
required: ["target"]
|
|
1523
|
+
};
|
|
1524
|
+
var ELEMENT_CONDITION_SCHEMA = {
|
|
1525
|
+
type: "object",
|
|
1526
|
+
description: "Tags the rule is disarmed on because they name the role natively (a <table>'s <caption>, a <fieldset>'s <legend>, an <input>'s <label for>); absent means the rule applies to every spelling.",
|
|
1527
|
+
properties: {
|
|
1528
|
+
exceptTags: {
|
|
1529
|
+
type: "array",
|
|
1530
|
+
items: { type: "string" },
|
|
1531
|
+
description: "Lowercase tag names whose native naming path disarms the rule."
|
|
1532
|
+
}
|
|
1533
|
+
},
|
|
1534
|
+
required: ["exceptTags"]
|
|
1535
|
+
};
|
|
1536
|
+
var DOCUMENT_CONDITION_SCHEMA = {
|
|
1537
|
+
type: "object",
|
|
1538
|
+
description: "Condition on the whole file: how many elements in it carry a role. Backs ARIA's conditional name levels (a toolbar's name becomes required on the second toolbar). Counts per file, which under-approximates a page built from partials.",
|
|
1539
|
+
properties: {
|
|
1540
|
+
role: { type: "string", description: "Role counted across the file." },
|
|
1541
|
+
atLeast: { type: "integer", description: "Holds at this many or more." },
|
|
1542
|
+
focusable: {
|
|
1543
|
+
type: "boolean",
|
|
1544
|
+
description: "Counts only Tab-reachable carriers of the role; absent counts all. ARIA qualifies a separator's condition by focusability (a decorative hr cannot be landed on) but a toolbar's not at all."
|
|
1545
|
+
}
|
|
1546
|
+
},
|
|
1547
|
+
required: ["role", "atLeast"]
|
|
1548
|
+
};
|
|
1300
1549
|
var CONTROLLER_CONTRACT_SCHEMA = {
|
|
1301
1550
|
type: "object",
|
|
1302
1551
|
properties: {
|
|
@@ -1309,6 +1558,19 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
1309
1558
|
...STRING_ARRAY,
|
|
1310
1559
|
description: "Targets that must be present at least once inside the controller scope."
|
|
1311
1560
|
},
|
|
1561
|
+
conditionalTargets: {
|
|
1562
|
+
type: "array",
|
|
1563
|
+
description: "Targets that become required only once another optional target appears \u2014 an opt-in feature that is incomplete without its whole set. Omitting one half passes every other check and silently does nothing.",
|
|
1564
|
+
items: {
|
|
1565
|
+
type: "object",
|
|
1566
|
+
properties: {
|
|
1567
|
+
whenPresent: { type: "string", description: "The optional target that turns it on." },
|
|
1568
|
+
require: { ...STRING_ARRAY, description: "Targets required once it appears." },
|
|
1569
|
+
suggestion: { type: "string" }
|
|
1570
|
+
},
|
|
1571
|
+
required: ["whenPresent", "require", "suggestion"]
|
|
1572
|
+
}
|
|
1573
|
+
},
|
|
1312
1574
|
a11y: {
|
|
1313
1575
|
type: "array",
|
|
1314
1576
|
description: "Author-supplied ARIA the controller relies on but never sets itself.",
|
|
@@ -1330,6 +1592,25 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
1330
1592
|
required: ["attrs"]
|
|
1331
1593
|
}
|
|
1332
1594
|
},
|
|
1595
|
+
when: {
|
|
1596
|
+
type: "object",
|
|
1597
|
+
description: "Own-value condition arming the requirement; absent means unconditional. Outside the named configuration the ARIA is not optional but wrong.",
|
|
1598
|
+
properties: {
|
|
1599
|
+
value: { type: "string" },
|
|
1600
|
+
equals: STRING_ARRAY,
|
|
1601
|
+
default: { type: "string" }
|
|
1602
|
+
},
|
|
1603
|
+
required: ["value", "equals", "default"]
|
|
1604
|
+
},
|
|
1605
|
+
whenContains: CONTENT_CONDITION_SCHEMA,
|
|
1606
|
+
whenElement: ELEMENT_CONDITION_SCHEMA,
|
|
1607
|
+
whenDocument: DOCUMENT_CONDITION_SCHEMA,
|
|
1608
|
+
severity: {
|
|
1609
|
+
type: "string",
|
|
1610
|
+
enum: ["error", "warning"],
|
|
1611
|
+
description: 'Level of an unmet requirement; absent means "error". ARIA recommends some names rather than requiring them, and those report as warnings.'
|
|
1612
|
+
},
|
|
1613
|
+
escalateWhen: DOCUMENT_CONDITION_SCHEMA,
|
|
1333
1614
|
suggestion: { type: "string" }
|
|
1334
1615
|
},
|
|
1335
1616
|
required: ["target", "attrs", "suggestion"]
|
|
@@ -1404,6 +1685,84 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
1404
1685
|
},
|
|
1405
1686
|
required: ["target", "coController", "require", "suggestion"]
|
|
1406
1687
|
}
|
|
1688
|
+
},
|
|
1689
|
+
companions: {
|
|
1690
|
+
type: "array",
|
|
1691
|
+
description: "Controllers the named target must ALSO declare. Unlike compositions (which only check an already co-located companion), these report the companion being missing.",
|
|
1692
|
+
items: {
|
|
1693
|
+
type: "object",
|
|
1694
|
+
properties: {
|
|
1695
|
+
target: {
|
|
1696
|
+
type: "string",
|
|
1697
|
+
description: 'Target that must declare it; "" means the data-controller element.'
|
|
1698
|
+
},
|
|
1699
|
+
controller: { type: "string", description: "Required companion identifier." },
|
|
1700
|
+
suggestion: { type: "string" }
|
|
1701
|
+
},
|
|
1702
|
+
required: ["target", "controller", "suggestion"]
|
|
1703
|
+
}
|
|
1704
|
+
},
|
|
1705
|
+
targetDeclarations: {
|
|
1706
|
+
type: "array",
|
|
1707
|
+
description: "Reverse direction: elements in scope carrying the attribute must be declared as the named target, or the controller never manages them.",
|
|
1708
|
+
items: {
|
|
1709
|
+
type: "object",
|
|
1710
|
+
properties: {
|
|
1711
|
+
attr: { type: "string", description: 'Marking attribute, e.g. "role".' },
|
|
1712
|
+
values: { ...STRING_ARRAY, description: "Marking values; absent = any value." },
|
|
1713
|
+
target: {
|
|
1714
|
+
type: "string",
|
|
1715
|
+
description: "Target the matched element must be declared as."
|
|
1716
|
+
},
|
|
1717
|
+
suggestion: { type: "string" }
|
|
1718
|
+
},
|
|
1719
|
+
required: ["attr", "target", "suggestion"]
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
cardinality: {
|
|
1723
|
+
type: "array",
|
|
1724
|
+
description: "Set-level count bounds: how many of the named target may or must exist per scope or per container target, optionally narrowed to elements carrying an attribute.",
|
|
1725
|
+
items: {
|
|
1726
|
+
type: "object",
|
|
1727
|
+
properties: {
|
|
1728
|
+
within: {
|
|
1729
|
+
type: "string",
|
|
1730
|
+
description: 'Container target bounding the count; "" means the whole scope.'
|
|
1731
|
+
},
|
|
1732
|
+
target: { type: "string", description: "Target name being counted." },
|
|
1733
|
+
attr: { type: "string", description: "Count only elements carrying this attribute." },
|
|
1734
|
+
values: { ...STRING_ARRAY, description: "Restrict the attribute to these values." },
|
|
1735
|
+
min: { type: "integer", description: "Smallest permitted count (inclusive)." },
|
|
1736
|
+
max: { type: "integer", description: "Largest permitted count (inclusive)." },
|
|
1737
|
+
when: {
|
|
1738
|
+
type: "object",
|
|
1739
|
+
description: "Own-value condition arming the bound; absent means unconditional.",
|
|
1740
|
+
properties: {
|
|
1741
|
+
value: { type: "string" },
|
|
1742
|
+
equals: STRING_ARRAY,
|
|
1743
|
+
default: { type: "string" }
|
|
1744
|
+
},
|
|
1745
|
+
required: ["value", "equals", "default"]
|
|
1746
|
+
},
|
|
1747
|
+
suggestion: { type: "string" }
|
|
1748
|
+
},
|
|
1749
|
+
required: ["within", "target", "suggestion"]
|
|
1750
|
+
}
|
|
1751
|
+
},
|
|
1752
|
+
forbiddenAria: {
|
|
1753
|
+
type: "array",
|
|
1754
|
+
description: "ARIA the author owns that the surrounding markup contradicts (e.g. a menu still declaring itself busy once its items are present). Reported as a warning \u2014 unlike managedAria, the attribute is the author's and is required in the other configuration.",
|
|
1755
|
+
items: {
|
|
1756
|
+
type: "object",
|
|
1757
|
+
properties: {
|
|
1758
|
+
target: { type: "string" },
|
|
1759
|
+
attrs: STRING_ARRAY,
|
|
1760
|
+
values: { ...STRING_ARRAY, description: "Forbidden values; absent = any value." },
|
|
1761
|
+
whenContains: CONTENT_CONDITION_SCHEMA,
|
|
1762
|
+
suggestion: { type: "string" }
|
|
1763
|
+
},
|
|
1764
|
+
required: ["target", "attrs", "suggestion"]
|
|
1765
|
+
}
|
|
1407
1766
|
}
|
|
1408
1767
|
},
|
|
1409
1768
|
required: [
|
|
@@ -1413,10 +1772,15 @@ var CONTROLLER_CONTRACT_SCHEMA = {
|
|
|
1413
1772
|
"actions",
|
|
1414
1773
|
"events",
|
|
1415
1774
|
"requiredTargets",
|
|
1775
|
+
"conditionalTargets",
|
|
1416
1776
|
"a11y",
|
|
1417
1777
|
"keyboard",
|
|
1418
1778
|
"managedAria",
|
|
1419
|
-
"compositions"
|
|
1779
|
+
"compositions",
|
|
1780
|
+
"companions",
|
|
1781
|
+
"targetDeclarations",
|
|
1782
|
+
"cardinality",
|
|
1783
|
+
"forbiddenAria"
|
|
1420
1784
|
]
|
|
1421
1785
|
};
|
|
1422
1786
|
var InvalidToolArgsError = class extends Error {
|
|
@@ -1429,7 +1793,7 @@ var TOOL_DESCRIPTORS = [
|
|
|
1429
1793
|
{
|
|
1430
1794
|
name: "stimeo_check",
|
|
1431
1795
|
title: "Check Stimeo UI markup",
|
|
1432
|
-
description: "Statically check HTML or ERB source that uses Stimeo UI (stimeo--*) controllers. Returns a CheckReport with diagnostics \u2014 unknown controllers/targets/values/action methods, missing required targets, missing or invalid author-supplied ARIA, keyboard-focusability prerequisites, and misaligned cross-controller composition values \u2014 each with a fix suggestion when available. Call this
|
|
1796
|
+
description: "Statically check HTML or ERB source that uses Stimeo UI (stimeo--*) controllers \u2014 the headless, accessible Stimulus UI component library for Rails. Returns a CheckReport with diagnostics \u2014 unknown controllers/targets/values/action methods, missing required targets, missing or invalid author-supplied ARIA, keyboard-focusability prerequisites, and misaligned cross-controller composition values \u2014 each with a fix suggestion when available. Call this to validate any Rails view, ERB template, or HTML you write or edit with these components, before finalizing it; ok=true means no errors.",
|
|
1433
1797
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
1434
1798
|
inputSchema: {
|
|
1435
1799
|
type: "object",
|
|
@@ -1451,7 +1815,7 @@ var TOOL_DESCRIPTORS = [
|
|
|
1451
1815
|
{
|
|
1452
1816
|
name: "stimeo_catalog",
|
|
1453
1817
|
title: "List Stimeo UI controllers",
|
|
1454
|
-
description: "List every Stimeo UI controller with its targets, values, actions, and events.
|
|
1818
|
+
description: "List every Stimeo UI controller with its targets, values, actions, and events. Stimeo UI is a headless, accessible (WAI-ARIA APG) Stimulus UI component library for Rails: dialog/modal, drawer, dropdown menu, tabs, accordion, combobox, listbox, tooltip, popover, toast, carousel, data grid, date picker, tree view, and many more. Start here to find which stimeo--* controller implements a requested component and the data-* API it accepts before writing markup.",
|
|
1455
1819
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
1456
1820
|
inputSchema: {
|
|
1457
1821
|
type: "object",
|
|
@@ -1463,7 +1827,7 @@ var TOOL_DESCRIPTORS = [
|
|
|
1463
1827
|
{
|
|
1464
1828
|
name: "stimeo_controller",
|
|
1465
1829
|
title: "Get one controller's usage contract",
|
|
1466
|
-
description: 'Fetch the full usage contract of one Stimeo UI controller by identifier (e.g. "stimeo--tabs"): targets, values, actions, events, required targets, and the accessibility contract \u2014 author-supplied ARIA requirements, keyboard-focusability prerequisites, the ARIA attributes the controller manages at runtime (which the author must NOT hardcode), and cross-controller composition value alignments.',
|
|
1830
|
+
description: 'Fetch the full usage contract of one Stimeo UI controller by identifier (e.g. "stimeo--tabs"): targets, values, actions, events, required targets, and the accessibility contract \u2014 author-supplied ARIA requirements, keyboard-focusability prerequisites, the ARIA attributes the controller manages at runtime (which the author must NOT hardcode), and cross-controller composition value alignments. Use it after stimeo_catalog to write correct, accessible Rails ERB or HTML markup for that Stimulus component.',
|
|
1467
1831
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
1468
1832
|
inputSchema: {
|
|
1469
1833
|
type: "object",
|
|
@@ -1481,7 +1845,7 @@ var TOOL_DESCRIPTORS = [
|
|
|
1481
1845
|
{
|
|
1482
1846
|
name: "stimeo_example",
|
|
1483
1847
|
title: "Get one controller's verified example markup",
|
|
1484
|
-
description: 'Fetch verified example markup for one Stimeo UI controller by identifier (e.g. "stimeo--menu"). The example is the official catalog demo \u2014 it passes stimeo_check against the bundled manifest, so use it as the reference for correct structure, required targets, and author-supplied ARIA. class attributes in it are demo styling placeholders to replace; the stimeo--* data attributes and role/aria-* attributes are the contract to keep.',
|
|
1848
|
+
description: 'Fetch verified example markup for one Stimeo UI controller by identifier (e.g. "stimeo--menu"). The example is the official catalog demo \u2014 it passes stimeo_check against the bundled manifest, so use it as the reference for correct structure, required targets, and author-supplied ARIA. class attributes in it are demo styling placeholders to replace; the stimeo--* data attributes and role/aria-* attributes are the contract to keep. Copy it as the starting point when adding that accessible Stimulus component to a Rails ERB view.',
|
|
1485
1849
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
1486
1850
|
inputSchema: {
|
|
1487
1851
|
type: "object",
|
|
@@ -1693,7 +2057,13 @@ var McpSession = class {
|
|
|
1693
2057
|
title: "Stimeo UI Inspector",
|
|
1694
2058
|
version: this.#context.manifest.packageVersion
|
|
1695
2059
|
},
|
|
1696
|
-
|
|
2060
|
+
// The server's own account of itself. Per the MCP schema this is a hint
|
|
2061
|
+
// a client MAY put in front of the model, which makes it the earliest
|
|
2062
|
+
// point this server can be routed to — clients that defer tool
|
|
2063
|
+
// definitions still tend to load it alongside the tool names. Hence the
|
|
2064
|
+
// trigger condition first, in the words a request would use, and a
|
|
2065
|
+
// length that survives truncation.
|
|
2066
|
+
instructions: "Search for these tools whenever a task involves building, editing, or reviewing an accessible UI component in a Rails view \u2014 dialog/modal, drawer, dropdown menu, tabs, accordion, combobox, toast, and the rest of the catalog \u2014 or whenever HTML/ERB carrying stimeo--* attributes is in play. Stimeo UI is a headless Stimulus UI framework for Rails. Use stimeo_catalog to discover controllers, stimeo_controller for one controller's usage and accessibility contract, stimeo_example for verified reference markup, and stimeo_check to validate generated HTML/ERB before presenting it. The full manifest and every example are also exposed as resources (stimeo:// URIs), and the stimeo_build_ui / stimeo_fix_markup prompts bake this workflow into user-invokable templates."
|
|
1697
2067
|
});
|
|
1698
2068
|
}
|
|
1699
2069
|
#getPrompt(id, params) {
|
|
@@ -1807,40 +2177,41 @@ async function runMcpServer(io) {
|
|
|
1807
2177
|
|
|
1808
2178
|
// src/inspector/cli_bin.ts
|
|
1809
2179
|
var argv = process2.argv.slice(2);
|
|
2180
|
+
process2.stdout.on("error", (error) => {
|
|
2181
|
+
process2.exit(error.code === "EPIPE" ? 0 : 1);
|
|
2182
|
+
});
|
|
1810
2183
|
if (argv[0] === "mcp") {
|
|
1811
2184
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
1812
|
-
process2.
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
input: process2.stdin,
|
|
1819
|
-
write: (line) => {
|
|
1820
|
-
process2.stdout.write(`${line}
|
|
2185
|
+
process2.exitCode = runCli(["--help"]);
|
|
2186
|
+
} else {
|
|
2187
|
+
runMcpServer({
|
|
2188
|
+
input: process2.stdin,
|
|
2189
|
+
write: (line) => {
|
|
2190
|
+
process2.stdout.write(`${line}
|
|
1821
2191
|
`);
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
2192
|
+
},
|
|
2193
|
+
load: () => ({ manifest: loadManifest(), examples: loadExamplesIndex() }),
|
|
2194
|
+
logError: (message) => {
|
|
2195
|
+
process2.stderr.write(`stimeo mcp: ${message}
|
|
1826
2196
|
`);
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
2197
|
+
}
|
|
2198
|
+
}).then(
|
|
2199
|
+
// Set the exit code instead of calling process.exit so any buffered
|
|
2200
|
+
// stdout responses are flushed before the process ends.
|
|
2201
|
+
() => {
|
|
2202
|
+
process2.exitCode = 0;
|
|
2203
|
+
},
|
|
2204
|
+
(error) => {
|
|
2205
|
+
process2.stderr.write(
|
|
2206
|
+
`stimeo mcp: ${error instanceof Error ? error.message : String(error)}
|
|
1837
2207
|
`
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2208
|
+
);
|
|
2209
|
+
process2.exitCode = 1;
|
|
2210
|
+
}
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
1842
2213
|
} else {
|
|
1843
|
-
process2.
|
|
2214
|
+
process2.exitCode = runCli(argv);
|
|
1844
2215
|
}
|
|
1845
2216
|
//# sourceMappingURL=cli_bin.js.map
|
|
1846
2217
|
//# sourceMappingURL=cli_bin.js.map
|