@rfdtech/components 1.21.0 → 2.0.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.
Files changed (263) hide show
  1. package/README.md +244 -74
  2. package/dist/adapters/context-adapter.d.ts +18 -0
  3. package/dist/adapters/context-adapter.d.ts.map +1 -0
  4. package/dist/adapters/next-adapter.d.ts +23 -0
  5. package/dist/adapters/next-adapter.d.ts.map +1 -0
  6. package/dist/adapters/react-router-adapter.d.ts +9 -0
  7. package/dist/adapters/react-router-adapter.d.ts.map +1 -0
  8. package/dist/adapters/registry.d.ts +19 -0
  9. package/dist/adapters/registry.d.ts.map +1 -0
  10. package/dist/components/app-header/AppHeader.d.ts +3 -2
  11. package/dist/components/app-header/AppHeader.d.ts.map +1 -1
  12. package/dist/components/app-header/AppHeaderNotificationItem.d.ts +3 -0
  13. package/dist/components/app-header/AppHeaderNotificationItem.d.ts.map +1 -0
  14. package/dist/components/app-header/AppHeaderProfile.d.ts +5 -0
  15. package/dist/components/app-header/AppHeaderProfile.d.ts.map +1 -1
  16. package/dist/components/app-header/index.d.ts +4 -2
  17. package/dist/components/app-header/index.d.ts.map +1 -1
  18. package/dist/components/app-layout/AppLayout.d.ts +6 -0
  19. package/dist/components/app-layout/AppLayout.d.ts.map +1 -1
  20. package/dist/components/app-layout/AppLayoutInner.d.ts +1 -0
  21. package/dist/components/app-layout/AppLayoutInner.d.ts.map +1 -1
  22. package/dist/components/app-switcher/AppIconTile.d.ts +10 -0
  23. package/dist/components/app-switcher/AppIconTile.d.ts.map +1 -0
  24. package/dist/components/app-switcher/AppSwitcher.d.ts +1 -1
  25. package/dist/components/app-switcher/AppSwitcher.d.ts.map +1 -1
  26. package/dist/components/app-switcher/AppSwitcherItem.d.ts.map +1 -1
  27. package/dist/components/app-switcher/IconPatterns.d.ts +8 -0
  28. package/dist/components/app-switcher/IconPatterns.d.ts.map +1 -0
  29. package/dist/components/app-switcher/SystemAppIcon.d.ts.map +1 -1
  30. package/dist/components/app-switcher/index.d.ts +2 -0
  31. package/dist/components/app-switcher/index.d.ts.map +1 -1
  32. package/dist/components/avatar/Avatar.d.ts.map +1 -1
  33. package/dist/components/bulk-import-modal/BulkImportModal.d.ts.map +1 -1
  34. package/dist/components/bulk-import-modal/steps/ValidateDataStep.d.ts.map +1 -1
  35. package/dist/components/button/Button.d.ts +1 -1
  36. package/dist/components/button/Button.d.ts.map +1 -1
  37. package/dist/components/button/Spinner.d.ts.map +1 -1
  38. package/dist/components/combobox/Combobox.d.ts +4 -0
  39. package/dist/components/combobox/Combobox.d.ts.map +1 -0
  40. package/dist/components/combobox/Combobox.test.d.ts +2 -0
  41. package/dist/components/combobox/Combobox.test.d.ts.map +1 -0
  42. package/dist/components/combobox/index.d.ts +3 -0
  43. package/dist/components/combobox/index.d.ts.map +1 -0
  44. package/dist/components/country-selector/CountrySelector.d.ts +7 -0
  45. package/dist/components/country-selector/CountrySelector.d.ts.map +1 -1
  46. package/dist/components/country-selector/index.d.ts +1 -0
  47. package/dist/components/country-selector/index.d.ts.map +1 -1
  48. package/dist/components/date-range-selector/DateRangeSelector.d.ts +2 -2
  49. package/dist/components/date-range-selector/DateRangeSelector.d.ts.map +1 -1
  50. package/dist/components/date-range-selector/index.d.ts +1 -1
  51. package/dist/components/date-range-selector/index.d.ts.map +1 -1
  52. package/dist/components/date-selector/DateSelector.d.ts.map +1 -1
  53. package/dist/components/dropdown/Dropdown.d.ts +1 -1
  54. package/dist/components/dropdown/Dropdown.d.ts.map +1 -1
  55. package/dist/components/export-button/ExportButton.d.ts +3 -0
  56. package/dist/components/export-button/ExportButton.d.ts.map +1 -0
  57. package/dist/components/export-button/ExportButton.test.d.ts +2 -0
  58. package/dist/components/export-button/ExportButton.test.d.ts.map +1 -0
  59. package/dist/components/export-button/index.d.ts +3 -0
  60. package/dist/components/export-button/index.d.ts.map +1 -0
  61. package/dist/components/launchpad/Launchpad.d.ts +4 -0
  62. package/dist/components/launchpad/Launchpad.d.ts.map +1 -0
  63. package/dist/components/launchpad/Launchpad.test.d.ts +2 -0
  64. package/dist/components/launchpad/Launchpad.test.d.ts.map +1 -0
  65. package/dist/components/launchpad/LaunchpadGridIcon.d.ts +2 -0
  66. package/dist/components/launchpad/LaunchpadGridIcon.d.ts.map +1 -0
  67. package/dist/components/launchpad/LaunchpadIconTile.d.ts +10 -0
  68. package/dist/components/launchpad/LaunchpadIconTile.d.ts.map +1 -0
  69. package/dist/components/launchpad/LaunchpadItem.d.ts +8 -0
  70. package/dist/components/launchpad/LaunchpadItem.d.ts.map +1 -0
  71. package/dist/components/launchpad/LaunchpadOverlays.d.ts +3 -0
  72. package/dist/components/launchpad/LaunchpadOverlays.d.ts.map +1 -0
  73. package/dist/components/launchpad/SystemLaunchpadIcon.d.ts +5 -0
  74. package/dist/components/launchpad/SystemLaunchpadIcon.d.ts.map +1 -0
  75. package/dist/components/launchpad/hooks/useLaunchpad.d.ts +3 -0
  76. package/dist/components/launchpad/hooks/useLaunchpad.d.ts.map +1 -0
  77. package/dist/components/launchpad/index.d.ts +10 -0
  78. package/dist/components/launchpad/index.d.ts.map +1 -0
  79. package/dist/components/metric-card/MetricCard.d.ts.map +1 -1
  80. package/dist/components/metric-card/index.d.ts +1 -1
  81. package/dist/components/metric-card/index.d.ts.map +1 -1
  82. package/dist/components/network-operator/NetworkOperator.d.ts +4 -0
  83. package/dist/components/network-operator/NetworkOperator.d.ts.map +1 -1
  84. package/dist/components/network-operator/index.d.ts +1 -0
  85. package/dist/components/network-operator/index.d.ts.map +1 -1
  86. package/dist/components/notice/Notice.d.ts +4 -0
  87. package/dist/components/notice/Notice.d.ts.map +1 -0
  88. package/dist/components/notice/Notice.test.d.ts +2 -0
  89. package/dist/components/notice/Notice.test.d.ts.map +1 -0
  90. package/dist/components/notice/index.d.ts +3 -0
  91. package/dist/components/notice/index.d.ts.map +1 -0
  92. package/dist/components/phone-number-input/PhoneNumberInput.d.ts.map +1 -1
  93. package/dist/components/popup/Popup.d.ts +15 -0
  94. package/dist/components/popup/Popup.d.ts.map +1 -0
  95. package/dist/components/popup/Popup.test.d.ts +2 -0
  96. package/dist/components/popup/Popup.test.d.ts.map +1 -0
  97. package/dist/components/popup/index.d.ts +3 -0
  98. package/dist/components/popup/index.d.ts.map +1 -0
  99. package/dist/components/profile-popover/ProfilePopover.d.ts +4 -0
  100. package/dist/components/profile-popover/ProfilePopover.d.ts.map +1 -0
  101. package/dist/components/profile-popover/ProfilePopover.test.d.ts +2 -0
  102. package/dist/components/profile-popover/ProfilePopover.test.d.ts.map +1 -0
  103. package/dist/components/profile-popover/index.d.ts +3 -0
  104. package/dist/components/profile-popover/index.d.ts.map +1 -0
  105. package/dist/components/role-select/RoleSelect.d.ts +4 -0
  106. package/dist/components/role-select/RoleSelect.d.ts.map +1 -0
  107. package/dist/components/role-select/RoleSelect.test.d.ts +2 -0
  108. package/dist/components/role-select/RoleSelect.test.d.ts.map +1 -0
  109. package/dist/components/role-select/index.d.ts +3 -0
  110. package/dist/components/role-select/index.d.ts.map +1 -0
  111. package/dist/components/section-header/SectionHeader.d.ts +7 -0
  112. package/dist/components/section-header/SectionHeader.d.ts.map +1 -0
  113. package/dist/components/section-header/SectionHeader.test.d.ts +2 -0
  114. package/dist/components/section-header/SectionHeader.test.d.ts.map +1 -0
  115. package/dist/components/section-header/index.d.ts +3 -0
  116. package/dist/components/section-header/index.d.ts.map +1 -0
  117. package/dist/components/sidebar/Sidebar.d.ts +3 -3
  118. package/dist/components/sidebar/Sidebar.d.ts.map +1 -1
  119. package/dist/components/sidebar/SidebarContext.d.ts +6 -0
  120. package/dist/components/sidebar/SidebarContext.d.ts.map +1 -1
  121. package/dist/components/sidebar/index.d.ts +1 -1
  122. package/dist/components/sidebar/index.d.ts.map +1 -1
  123. package/dist/components/stepper/Stepper.d.ts +12 -0
  124. package/dist/components/stepper/Stepper.d.ts.map +1 -0
  125. package/dist/components/stepper/Stepper.test.d.ts +2 -0
  126. package/dist/components/stepper/Stepper.test.d.ts.map +1 -0
  127. package/dist/components/stepper/StepperContext.d.ts +4 -0
  128. package/dist/components/stepper/StepperContext.d.ts.map +1 -0
  129. package/dist/components/stepper/index.d.ts +3 -0
  130. package/dist/components/stepper/index.d.ts.map +1 -0
  131. package/dist/components/switch/Switch.d.ts +4 -0
  132. package/dist/components/switch/Switch.d.ts.map +1 -0
  133. package/dist/components/switch/Switch.test.d.ts +2 -0
  134. package/dist/components/switch/Switch.test.d.ts.map +1 -0
  135. package/dist/components/switch/index.d.ts +3 -0
  136. package/dist/components/switch/index.d.ts.map +1 -0
  137. package/dist/components/table/Table.d.ts +3 -6
  138. package/dist/components/table/Table.d.ts.map +1 -1
  139. package/dist/components/table/TableHeader.d.ts +3 -10
  140. package/dist/components/table/TableHeader.d.ts.map +1 -1
  141. package/dist/components/table/TablePagination.d.ts.map +1 -1
  142. package/dist/components/table/index.d.ts +1 -1
  143. package/dist/components/table/index.d.ts.map +1 -1
  144. package/dist/components/tabs/Tabs.d.ts.map +1 -1
  145. package/dist/components/tabs/hooks/useTabsLineIndicator.d.ts.map +1 -1
  146. package/dist/components/theme/ThemeProvider.d.ts.map +1 -1
  147. package/dist/components/theme/cletTheme.d.ts +11 -0
  148. package/dist/components/theme/cletTheme.d.ts.map +1 -0
  149. package/dist/components/theme/cletTheme.test-d.d.ts +2 -0
  150. package/dist/components/theme/cletTheme.test-d.d.ts.map +1 -0
  151. package/dist/components/theme/cletTheme.test.d.ts +2 -0
  152. package/dist/components/theme/cletTheme.test.d.ts.map +1 -0
  153. package/dist/components/theme/index.d.ts +3 -1
  154. package/dist/components/theme/index.d.ts.map +1 -1
  155. package/dist/components/timeline/Timeline.d.ts +8 -0
  156. package/dist/components/timeline/Timeline.d.ts.map +1 -0
  157. package/dist/components/timeline/Timeline.test.d.ts +2 -0
  158. package/dist/components/timeline/Timeline.test.d.ts.map +1 -0
  159. package/dist/components/timeline/index.d.ts +3 -0
  160. package/dist/components/timeline/index.d.ts.map +1 -0
  161. package/dist/components/upload-field/UploadField.d.ts +5 -0
  162. package/dist/components/upload-field/UploadField.d.ts.map +1 -1
  163. package/dist/components/upload-field/index.d.ts +2 -2
  164. package/dist/components/upload-field/index.d.ts.map +1 -1
  165. package/dist/contexts/router-adapter-context.d.ts +27 -0
  166. package/dist/contexts/router-adapter-context.d.ts.map +1 -0
  167. package/dist/generated/components.theme.d.ts +313 -0
  168. package/dist/generated/components.theme.d.ts.map +1 -0
  169. package/dist/hooks/index.d.ts +1 -0
  170. package/dist/hooks/index.d.ts.map +1 -1
  171. package/dist/hooks/useHasMounted.d.ts +10 -0
  172. package/dist/hooks/useHasMounted.d.ts.map +1 -0
  173. package/dist/hooks/useTableFilter.d.ts.map +1 -1
  174. package/dist/hooks/useTablePagination.d.ts.map +1 -1
  175. package/dist/hooks/useTableState.d.ts +1 -1
  176. package/dist/hooks/useTableState.d.ts.map +1 -1
  177. package/dist/index.cjs +85 -55
  178. package/dist/index.css +1 -1
  179. package/dist/index.d.ts +15 -1
  180. package/dist/index.d.ts.map +1 -1
  181. package/dist/index.js +19423 -31378
  182. package/dist/mcp/cli.js +75 -0
  183. package/dist/mcp/docs.js +122 -0
  184. package/dist/mcp/generated/components.json +3593 -0
  185. package/dist/mcp/generated/examples.json +1233 -0
  186. package/dist/mcp/generated/index.meta.json +252 -0
  187. package/dist/mcp/generated/rules.json +629 -0
  188. package/dist/mcp/generated/search.json +1618 -0
  189. package/dist/mcp/generated/tokens.json +1022 -0
  190. package/dist/mcp/index.js +337 -0
  191. package/dist/mcp/indexer.js +370 -0
  192. package/dist/mcp/installers/claude.js +37 -0
  193. package/dist/mcp/installers/codex.js +24 -0
  194. package/dist/mcp/installers/cursor.js +33 -0
  195. package/dist/mcp/installers/doctor.js +36 -0
  196. package/dist/mcp/installers/opencode.js +36 -0
  197. package/dist/mcp/installers/setup.js +34 -0
  198. package/dist/mcp/installers/util.js +41 -0
  199. package/dist/mcp/paths.js +63 -0
  200. package/dist/mcp/skill/SKILL.md +36 -0
  201. package/dist/mcp/skill/image-to-components.md +49 -0
  202. package/dist/next-index.d.ts +44 -0
  203. package/dist/next-index.d.ts.map +1 -0
  204. package/dist/next.cjs +282 -0
  205. package/dist/next.css +1 -0
  206. package/dist/next.d.ts +29 -0
  207. package/dist/next.js +37774 -0
  208. package/dist/types/app-header.d.ts +70 -10
  209. package/dist/types/app-header.d.ts.map +1 -1
  210. package/dist/types/app-switcher.d.ts +10 -1
  211. package/dist/types/app-switcher.d.ts.map +1 -1
  212. package/dist/types/avatar.d.ts +9 -0
  213. package/dist/types/avatar.d.ts.map +1 -1
  214. package/dist/types/combobox.d.ts +43 -0
  215. package/dist/types/combobox.d.ts.map +1 -0
  216. package/dist/types/country-selector.d.ts +4 -0
  217. package/dist/types/country-selector.d.ts.map +1 -1
  218. package/dist/types/date-range-selector.d.ts +11 -0
  219. package/dist/types/date-range-selector.d.ts.map +1 -1
  220. package/dist/types/dropdown.d.ts +8 -0
  221. package/dist/types/dropdown.d.ts.map +1 -1
  222. package/dist/types/export-button.d.ts +24 -0
  223. package/dist/types/export-button.d.ts.map +1 -0
  224. package/dist/types/launchpad.d.ts +79 -0
  225. package/dist/types/launchpad.d.ts.map +1 -0
  226. package/dist/types/metric-card.d.ts +7 -0
  227. package/dist/types/metric-card.d.ts.map +1 -1
  228. package/dist/types/network-operator.d.ts +8 -0
  229. package/dist/types/network-operator.d.ts.map +1 -1
  230. package/dist/types/notice.d.ts +24 -0
  231. package/dist/types/notice.d.ts.map +1 -0
  232. package/dist/types/popup.d.ts +43 -0
  233. package/dist/types/popup.d.ts.map +1 -0
  234. package/dist/types/profile-popover.d.ts +97 -0
  235. package/dist/types/profile-popover.d.ts.map +1 -0
  236. package/dist/types/role-select.d.ts +28 -0
  237. package/dist/types/role-select.d.ts.map +1 -0
  238. package/dist/types/router-adapter.d.ts +46 -0
  239. package/dist/types/router-adapter.d.ts.map +1 -0
  240. package/dist/types/section-header.d.ts +26 -0
  241. package/dist/types/section-header.d.ts.map +1 -0
  242. package/dist/types/sidebar.d.ts +16 -0
  243. package/dist/types/sidebar.d.ts.map +1 -1
  244. package/dist/types/stepper.d.ts +45 -0
  245. package/dist/types/stepper.d.ts.map +1 -0
  246. package/dist/types/switch.d.ts +24 -0
  247. package/dist/types/switch.d.ts.map +1 -0
  248. package/dist/types/table.d.ts +90 -1
  249. package/dist/types/table.d.ts.map +1 -1
  250. package/dist/types/tabs.d.ts +1 -1
  251. package/dist/types/tabs.d.ts.map +1 -1
  252. package/dist/types/theme.d.ts +28 -8
  253. package/dist/types/theme.d.ts.map +1 -1
  254. package/dist/types/timeline.d.ts +52 -0
  255. package/dist/types/timeline.d.ts.map +1 -0
  256. package/dist/types/upload-field.d.ts +23 -0
  257. package/dist/types/upload-field.d.ts.map +1 -1
  258. package/dist/utils/export.d.ts +15 -0
  259. package/dist/utils/export.d.ts.map +1 -0
  260. package/dist/utils/stringToColor.d.ts +25 -0
  261. package/dist/utils/stringToColor.d.ts.map +1 -1
  262. package/package.json +43 -7
  263. package/scripts/postinstall.mjs +40 -0
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { loadIndex, searchComponents } from "./docs.js";
3
+ import { runDoctor } from "./installers/doctor.js";
4
+ import { runSetup } from "./installers/setup.js";
5
+ import { buildIndex } from "./indexer.js";
6
+ import { GENERATED_DIR, hasRepoSource } from "./paths.js";
7
+ const USAGE = `rfdui — @rfdtech/components AI tooling
8
+
9
+ Usage:
10
+ rfdui setup Detect installed AI tools (Claude Code, Cursor, Codex, OpenCode) and wire up
11
+ the MCP server + a shared skill doc for each.
12
+ rfdui doctor Report whether the index, MCP, and each AI tool are configured correctly.
13
+ rfdui index Rebuild the generated docs index (only works inside the repo checkout).
14
+ rfdui mcp Run the MCP server directly over stdio.
15
+ rfdui search <query> Lexical search over components from the terminal.
16
+ rfdui update Rebuild the index (if possible) and re-run setup.
17
+ `;
18
+ async function main() {
19
+ const [, , cmd, ...rest] = process.argv;
20
+ switch (cmd) {
21
+ case "index": {
22
+ if (!hasRepoSource) {
23
+ console.error("`rfdui index` only works inside the gsl-components repo checkout (no source found).");
24
+ process.exitCode = 1;
25
+ return;
26
+ }
27
+ const result = await buildIndex(GENERATED_DIR);
28
+ console.log(`Index built: ${JSON.stringify(result, null, 2)}`);
29
+ return;
30
+ }
31
+ case "search": {
32
+ const query = rest.join(" ").trim();
33
+ if (!query) {
34
+ console.error("Usage: rfdui search <query>");
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ const idx = await loadIndex();
39
+ const results = searchComponents(idx, query);
40
+ if (results.length === 0) {
41
+ console.log(`No components matched "${query}".`);
42
+ return;
43
+ }
44
+ for (const r of results) {
45
+ console.log(`${r.slug.padEnd(24)} ${r.name}${r.description ? ` — ${r.description}` : ""}`);
46
+ }
47
+ return;
48
+ }
49
+ case "doctor":
50
+ await runDoctor();
51
+ return;
52
+ case "setup":
53
+ await runSetup();
54
+ return;
55
+ case "update":
56
+ if (hasRepoSource) {
57
+ const result = await buildIndex(GENERATED_DIR);
58
+ console.log(`Index rebuilt: ${JSON.stringify(result)}`);
59
+ }
60
+ await runSetup();
61
+ return;
62
+ case "mcp":
63
+ // Delegate to the server entry point (same process, same stdio streams).
64
+ await import("./index.js");
65
+ return;
66
+ default:
67
+ console.log(USAGE);
68
+ if (cmd && cmd !== "help" && cmd !== "--help")
69
+ process.exitCode = 1;
70
+ }
71
+ }
72
+ main().catch((err) => {
73
+ console.error(err);
74
+ process.exitCode = 1;
75
+ });
@@ -0,0 +1,122 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { GENERATED_DIR } from "./paths.js";
4
+ async function readJson(name) {
5
+ const raw = await readFile(path.join(GENERATED_DIR, name), "utf8");
6
+ return JSON.parse(raw);
7
+ }
8
+ export async function loadIndex() {
9
+ const [components, search, rules, tokens, examples] = await Promise.all([
10
+ readJson("components.json"),
11
+ readJson("search.json"),
12
+ readJson("rules.json"),
13
+ readJson("tokens.json"),
14
+ readJson("examples.json"),
15
+ ]);
16
+ const rulesById = {};
17
+ for (const category of Object.values(rules)) {
18
+ for (const rule of category)
19
+ rulesById[rule.id] = rule;
20
+ }
21
+ return { components, search, rules, rulesById, tokens, examples };
22
+ }
23
+ export function listSlugs(idx) {
24
+ return Object.keys(idx.components).sort();
25
+ }
26
+ export function getComponent(idx, slug) {
27
+ return idx.components[slug.trim().toLowerCase()] ?? null;
28
+ }
29
+ /**
30
+ * Lexical ranking over name/slug/keywords/description/headings. No embeddings —
31
+ * this is a local, offline MCP server; ranking is transparent term overlap, not
32
+ * semantic similarity. Good enough for "table pagination" -> Table, "wizard" ->
33
+ * Stepper (via keywords), etc.
34
+ */
35
+ export function searchComponents(idx, query, limit = 8) {
36
+ const terms = query.toLowerCase().trim().split(/\s+/).filter(Boolean);
37
+ if (terms.length === 0)
38
+ return [];
39
+ const scored = idx.search.map((entry) => {
40
+ const slug = entry.slug.toLowerCase();
41
+ const name = (entry.name ?? "").toLowerCase();
42
+ const keywords = (entry.keywords ?? []).map((k) => k.toLowerCase());
43
+ const description = (entry.description ?? "").toLowerCase();
44
+ const headings = (entry.headings ?? []).map((h) => h.toLowerCase());
45
+ let score = 0;
46
+ for (const term of terms) {
47
+ if (slug === term)
48
+ score += 10;
49
+ if (slug.includes(term))
50
+ score += 5;
51
+ if (name.includes(term))
52
+ score += 4;
53
+ if (keywords.some((k) => k.includes(term)))
54
+ score += 3;
55
+ if (description.includes(term))
56
+ score += 2;
57
+ if (headings.some((h) => h.includes(term)))
58
+ score += 1;
59
+ }
60
+ return { entry, score };
61
+ });
62
+ return scored
63
+ .filter((r) => r.score > 0)
64
+ .sort((a, b) => b.score - a.score)
65
+ .slice(0, limit)
66
+ .map((r) => ({
67
+ slug: r.entry.slug,
68
+ name: r.entry.name,
69
+ category: r.entry.category,
70
+ description: r.entry.description,
71
+ score: r.score,
72
+ }));
73
+ }
74
+ export function searchDocs(idx, query, limit = 8) {
75
+ const terms = query.toLowerCase().trim().split(/\s+/).filter(Boolean);
76
+ if (terms.length === 0)
77
+ return [];
78
+ const results = Object.values(idx.components).map((entry) => {
79
+ const body = entry.docs.toLowerCase();
80
+ const matchedHeadings = entry.headings.filter((h) => terms.some((t) => h.toLowerCase().includes(t)));
81
+ let score = 0;
82
+ for (const term of terms) {
83
+ if (body.includes(term))
84
+ score += 2;
85
+ }
86
+ score += matchedHeadings.length;
87
+ return { slug: entry.meta.slug, name: entry.meta.title, matchedHeadings, score };
88
+ });
89
+ return results
90
+ .filter((r) => r.score > 0)
91
+ .sort((a, b) => b.score - a.score)
92
+ .slice(0, limit);
93
+ }
94
+ /** `topic` may be a rules category (e.g. "forms") or a component slug (e.g. "dialog"). */
95
+ export function getRules(idx, topic) {
96
+ if (!topic)
97
+ return idx.rules;
98
+ const key = topic.trim().toLowerCase();
99
+ if (idx.rules[key])
100
+ return { [key]: idx.rules[key] };
101
+ const matched = [];
102
+ for (const rules of Object.values(idx.rules)) {
103
+ matched.push(...rules.filter((r) => r.components.includes(key)));
104
+ }
105
+ return matched.length ? { [key]: matched } : {};
106
+ }
107
+ export function searchRules(idx, query, limit = 10) {
108
+ const terms = query.toLowerCase().trim().split(/\s+/).filter(Boolean);
109
+ if (terms.length === 0)
110
+ return [];
111
+ const all = Object.values(idx.rules).flat();
112
+ const scored = all.map((rule) => {
113
+ const hay = `${rule.title} ${rule.body} ${rule.components.join(" ")}`.toLowerCase();
114
+ const score = terms.reduce((n, t) => n + (hay.includes(t) ? 1 : 0), 0);
115
+ return { rule, score };
116
+ });
117
+ return scored
118
+ .filter((r) => r.score > 0)
119
+ .sort((a, b) => b.score - a.score)
120
+ .slice(0, limit)
121
+ .map((r) => r.rule);
122
+ }