@grampro/kit 2.6.3 → 2.7.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 (93) hide show
  1. package/.gbs/skills/gbs-components/SKILL.md +5 -4
  2. package/.gbs/skills/gbs-components/references/install.md +17 -14
  3. package/CHANGELOG.md +164 -0
  4. package/README.md +19 -6
  5. package/index.cjs +59 -59
  6. package/package.json +2 -2
  7. package/schema/passport-overlay-v1.schema.json +67 -0
  8. package/source/components/accordion/passport.json +45 -8
  9. package/source/components/accordion/passport.manual.json +46 -0
  10. package/source/components/alert/passport.json +41 -8
  11. package/source/components/alert/passport.manual.json +42 -0
  12. package/source/components/avatar/passport.json +40 -7
  13. package/source/components/avatar/passport.manual.json +43 -0
  14. package/source/components/badge/passport.json +43 -7
  15. package/source/components/badge/passport.manual.json +46 -0
  16. package/source/components/breadcrumb/passport.json +36 -7
  17. package/source/components/breadcrumb/passport.manual.json +39 -0
  18. package/source/components/button/passport.json +1 -1
  19. package/source/components/button/passport.manual.json +1 -1
  20. package/source/components/card/passport.json +45 -8
  21. package/source/components/card/passport.manual.json +47 -0
  22. package/source/components/checkbox/core/agent.ts +33 -0
  23. package/source/components/checkbox/core/index.ts +2 -0
  24. package/source/components/checkbox/passport.json +83 -10
  25. package/source/components/checkbox/passport.manual.json +82 -0
  26. package/source/components/combobox/core/agent.ts +38 -0
  27. package/source/components/combobox/core/index.ts +2 -0
  28. package/source/components/combobox/passport.json +103 -11
  29. package/source/components/combobox/passport.manual.json +101 -0
  30. package/source/components/combobox/react/props.ts +14 -0
  31. package/source/components/combobox/react/useCombobox.ts +7 -1
  32. package/source/components/data-grid/export/xlsx.ts +9 -2
  33. package/source/components/data-grid/passport.json +2 -2
  34. package/source/components/data-grid/passport.manual.json +1 -1
  35. package/source/components/date-picker/core/agent.ts +42 -0
  36. package/source/components/date-picker/core/index.ts +2 -0
  37. package/source/components/date-picker/passport.json +102 -11
  38. package/source/components/date-picker/passport.manual.json +100 -0
  39. package/source/components/date-picker/react/DatePicker.tsx +6 -0
  40. package/source/components/date-picker/react/DateRangePicker.tsx +4 -0
  41. package/source/components/date-picker/react/props.ts +17 -0
  42. package/source/components/dialog/passport.json +54 -9
  43. package/source/components/dialog/passport.manual.json +54 -0
  44. package/source/components/file-uploader/passport.json +65 -10
  45. package/source/components/file-uploader/passport.manual.json +64 -0
  46. package/source/components/input/core/agent.ts +36 -0
  47. package/source/components/input/core/index.ts +2 -0
  48. package/source/components/input/passport.json +89 -12
  49. package/source/components/input/passport.manual.json +86 -0
  50. package/source/components/menu/passport.json +64 -9
  51. package/source/components/menu/passport.manual.json +64 -0
  52. package/source/components/modal/passport.json +52 -9
  53. package/source/components/modal/passport.manual.json +52 -0
  54. package/source/components/number-input/core/agent.ts +38 -0
  55. package/source/components/number-input/core/index.ts +2 -0
  56. package/source/components/number-input/passport.json +92 -11
  57. package/source/components/number-input/passport.manual.json +90 -0
  58. package/source/components/popover/passport.json +47 -8
  59. package/source/components/popover/passport.manual.json +48 -0
  60. package/source/components/progress/passport.json +37 -8
  61. package/source/components/progress/passport.manual.json +39 -0
  62. package/source/components/radio-group/core/agent.ts +34 -0
  63. package/source/components/radio-group/core/index.ts +2 -0
  64. package/source/components/radio-group/passport.json +77 -10
  65. package/source/components/radio-group/passport.manual.json +76 -0
  66. package/source/components/shared/core/agent/field-agent.ts +101 -0
  67. package/source/components/shared/core/agent/field-executors.ts +137 -0
  68. package/source/components/shared/core/agent/field.ts +649 -0
  69. package/source/components/shared/core/agent/index.ts +30 -0
  70. package/source/components/shared/core/agent/write.ts +58 -0
  71. package/source/components/shared/react/useFieldAgent.ts +88 -0
  72. package/source/components/skeleton/passport.json +37 -6
  73. package/source/components/skeleton/passport.manual.json +39 -0
  74. package/source/components/spinner/passport.json +34 -7
  75. package/source/components/spinner/passport.manual.json +37 -0
  76. package/source/components/switch/core/agent.ts +31 -0
  77. package/source/components/switch/core/index.ts +2 -0
  78. package/source/components/switch/passport.json +68 -10
  79. package/source/components/switch/passport.manual.json +67 -0
  80. package/source/components/tabs/passport.json +50 -9
  81. package/source/components/tabs/passport.manual.json +50 -0
  82. package/source/components/textarea/core/agent.ts +34 -0
  83. package/source/components/textarea/core/index.ts +2 -0
  84. package/source/components/textarea/passport.json +83 -10
  85. package/source/components/textarea/passport.manual.json +82 -0
  86. package/source/components/toaster/passport.json +41 -8
  87. package/source/components/toaster/passport.manual.json +42 -0
  88. package/source/components/tooltip/passport.json +39 -6
  89. package/source/components/tooltip/passport.manual.json +41 -0
  90. package/tools/mcp/design.cjs +6 -1
  91. package/tools/mcp/figma.cjs +49 -9
  92. package/tools/mcp/install.cjs +6 -1
  93. package/tools/mcp/server.cjs +14 -2
package/index.cjs CHANGED
@@ -28,7 +28,7 @@ const readline = require("readline");
28
28
 
29
29
  // Configuration
30
30
  const CONFIG = {
31
- components: [
31
+ legacyComponents: [
32
32
  "Card",
33
33
  "Select",
34
34
  "SideBar",
@@ -55,7 +55,7 @@ const CONFIG = {
55
55
  "UsePaginatedData",
56
56
  "UseUploader",
57
57
  ],
58
- betaComponents: [
58
+ components: [
59
59
  "DataGrid",
60
60
  "Combobox",
61
61
  "DatePicker",
@@ -84,8 +84,8 @@ const CONFIG = {
84
84
  "Badge",
85
85
  "Progress",
86
86
  ],
87
- // Beta folder names that are not simply the lowercased component name.
88
- betaFolders: {
87
+ // Folder names that are not simply the lowercased component name.
88
+ folders: {
89
89
  DataGrid: "data-grid",
90
90
  DatePicker: "date-picker",
91
91
  FileUploader: "file-uploader",
@@ -198,7 +198,7 @@ const NL = String.fromCharCode(10);
198
198
  const CLI_NAME = "@grampro/kit";
199
199
 
200
200
  /**
201
- * Every beta component imports its shared helpers from `../../shared`, so this
201
+ * Every component imports its shared helpers from `../../shared`, so this
202
202
  * folder is installed alongside whatever the user picked. It is not a component
203
203
  * and never appears in the component list.
204
204
  */
@@ -220,14 +220,14 @@ const normalizeComponent = (component, availableComponents) =>
220
220
  (available) => available.toLowerCase() === component.toLowerCase(),
221
221
  );
222
222
 
223
- const getAvailableComponents = (beta = false) =>
224
- beta ? CONFIG.betaComponents : CONFIG.components;
223
+ const getAvailableComponents = (current = false) =>
224
+ current ? CONFIG.components : CONFIG.legacyComponents;
225
225
 
226
- const getSourcePath = (beta = false) => (beta ? SOURCE_PATH : LEGACY_SOURCE_PATH);
226
+ const getSourcePath = (current = false) => (current ? SOURCE_PATH : LEGACY_SOURCE_PATH);
227
227
 
228
228
  /** The folder a component lives in, which is not always its lowercased name. */
229
- const folderFor = (component, beta = false) =>
230
- (beta && CONFIG.betaFolders[component]) || component.toLowerCase();
229
+ const folderFor = (component, current = false) =>
230
+ (current && CONFIG.folders[component]) || component.toLowerCase();
231
231
 
232
232
 
233
233
  /*
@@ -271,8 +271,8 @@ const relativeFrom = (file, dir) => {
271
271
  return rel.startsWith(".") ? rel : "./" + rel;
272
272
  };
273
273
 
274
- const writeStyleBarrel = async (destPath, beta) => {
275
- if (!beta) return null;
274
+ const writeStyleBarrel = async (destPath, current) => {
275
+ if (!current) return null;
276
276
 
277
277
  // Whatever is on disk now, not whatever this run happened to install: the
278
278
  // barrel has to stay correct across repeated and partial installs.
@@ -452,7 +452,7 @@ const installShared = async (destPath, { force = false } = {}) => {
452
452
 
453
453
  if (!fs.existsSync(src)) {
454
454
  throw new Error(
455
- `The shared folder is missing from ${SOURCE_PATH}. Beta components cannot be installed without it.`,
455
+ `The shared folder is missing from ${SOURCE_PATH}. Components cannot be installed without it.`,
456
456
  );
457
457
  }
458
458
 
@@ -914,12 +914,12 @@ const isSkillWord = (value) =>
914
914
  typeof value === "string" && value.trim().toLowerCase() === "skill";
915
915
 
916
916
  /** The files a component needs beside it, which differ between the two sets. */
917
- const copySupportFiles = async (destPath, beta, options) => {
918
- if (beta) {
917
+ const copySupportFiles = async (destPath, current, options) => {
918
+ if (current) {
919
919
  await installShared(destPath, options);
920
920
  return;
921
921
  }
922
- // The legacy components' common files. Beta components use none of them.
922
+ // The legacy components' common files. The current set uses none of them.
923
923
  if (!fs.existsSync(path.join(destPath, "utils.ts"))) {
924
924
  await copyCommonFiles(destPath);
925
925
  }
@@ -927,22 +927,22 @@ const copySupportFiles = async (destPath, beta, options) => {
927
927
 
928
928
  /* -------------------------------------------------------------- components */
929
929
 
930
- const checkComponentExists = (component, destPath, beta = false) => {
931
- const componentPath = path.join(destPath, folderFor(component, beta));
930
+ const checkComponentExists = (component, destPath, current = false) => {
931
+ const componentPath = path.join(destPath, folderFor(component, current));
932
932
  return fs.existsSync(componentPath);
933
933
  };
934
934
 
935
- const copyComponent = async (component, destPath, beta = false) => {
935
+ const copyComponent = async (component, destPath, current = false) => {
936
936
  try {
937
937
  const componentSrc = path.join(
938
- getSourcePath(beta),
939
- folderFor(component, beta),
938
+ getSourcePath(current),
939
+ folderFor(component, current),
940
940
  );
941
- const componentDest = path.join(destPath, folderFor(component, beta));
941
+ const componentDest = path.join(destPath, folderFor(component, current));
942
942
 
943
943
  if (!fs.existsSync(componentSrc)) {
944
944
  throw new Error(
945
- `Component ${component} not found in ${beta ? "beta " : ""}source directory.`,
945
+ `Component ${component} not found in ${current ? "" : "1.x "}source directory.`,
946
946
  );
947
947
  }
948
948
 
@@ -969,17 +969,17 @@ const copyComponent = async (component, destPath, beta = false) => {
969
969
  const installComponentWithDependencies = async (
970
970
  component,
971
971
  destPath,
972
- beta = false,
972
+ current = false,
973
973
  ) => {
974
974
  // Get dependencies for the component
975
975
  const dependencies = CONFIG.dependencies[component] || [];
976
976
  const componentsToInstall = new Set([component, ...dependencies]);
977
977
 
978
978
  // Check which components need to be installed
979
- const pendingInstalls = beta
979
+ const pendingInstalls = current
980
980
  ? Array.from(componentsToInstall)
981
981
  : Array.from(componentsToInstall).filter(
982
- (comp) => !checkComponentExists(comp, destPath, beta),
982
+ (comp) => !checkComponentExists(comp, destPath, current),
983
983
  );
984
984
 
985
985
  if (pendingInstalls.length === 0) {
@@ -991,7 +991,7 @@ const installComponentWithDependencies = async (
991
991
 
992
992
  // Install all pending components
993
993
  for (const comp of pendingInstalls) {
994
- await copyComponent(comp, destPath, beta);
994
+ await copyComponent(comp, destPath, current);
995
995
  }
996
996
 
997
997
  if (dependencies.length > 0) {
@@ -1001,7 +1001,7 @@ const installComponentWithDependencies = async (
1001
1001
  });
1002
1002
  }
1003
1003
 
1004
- reportStyleBarrel(await writeStyleBarrel(destPath, beta));
1004
+ reportStyleBarrel(await writeStyleBarrel(destPath, current));
1005
1005
 
1006
1006
  console.log(`\nFor documentation visit: ${CONFIG.docs}`);
1007
1007
  };
@@ -1009,7 +1009,7 @@ const installComponentWithDependencies = async (
1009
1009
  const installMultipleComponents = async (
1010
1010
  components,
1011
1011
  destPath,
1012
- beta = false,
1012
+ current = false,
1013
1013
  ) => {
1014
1014
  const allComponentsToInstall = new Set();
1015
1015
 
@@ -1021,10 +1021,10 @@ const installMultipleComponents = async (
1021
1021
  });
1022
1022
 
1023
1023
  // Filter out already installed components
1024
- const pendingInstalls = beta
1024
+ const pendingInstalls = current
1025
1025
  ? Array.from(allComponentsToInstall)
1026
1026
  : Array.from(allComponentsToInstall).filter(
1027
- (comp) => !checkComponentExists(comp, destPath, beta),
1027
+ (comp) => !checkComponentExists(comp, destPath, current),
1028
1028
  );
1029
1029
 
1030
1030
  if (pendingInstalls.length === 0) {
@@ -1038,7 +1038,7 @@ const installMultipleComponents = async (
1038
1038
 
1039
1039
  // Install all pending components
1040
1040
  for (const comp of pendingInstalls) {
1041
- await copyComponent(comp, destPath, beta);
1041
+ await copyComponent(comp, destPath, current);
1042
1042
  }
1043
1043
 
1044
1044
  // Show dependency information
@@ -1055,12 +1055,12 @@ const installMultipleComponents = async (
1055
1055
  });
1056
1056
  }
1057
1057
 
1058
- reportStyleBarrel(await writeStyleBarrel(destPath, beta));
1058
+ reportStyleBarrel(await writeStyleBarrel(destPath, current));
1059
1059
 
1060
1060
  console.log(`\nFor documentation visit: ${CONFIG.docs}`);
1061
1061
  };
1062
1062
 
1063
- const interactiveComponentSelector = async (beta = false) => {
1063
+ const interactiveComponentSelector = async (current = false) => {
1064
1064
  return new Promise((resolve) => {
1065
1065
  const rl = readline.createInterface({
1066
1066
  input: process.stdin,
@@ -1077,7 +1077,7 @@ const interactiveComponentSelector = async (beta = false) => {
1077
1077
  "Use ↑/↓ arrow keys to navigate, SPACE to select/deselect, ENTER to install\n",
1078
1078
  );
1079
1079
 
1080
- const availableComponents = getAvailableComponents(beta);
1080
+ const availableComponents = getAvailableComponents(current);
1081
1081
 
1082
1082
  availableComponents.forEach((component, index) => {
1083
1083
  const isSelected = selectedComponents.has(component);
@@ -1109,13 +1109,13 @@ const interactiveComponentSelector = async (beta = false) => {
1109
1109
  break;
1110
1110
  case "": // Down arrow
1111
1111
  currentIndex = Math.min(
1112
- getAvailableComponents(beta).length - 1,
1112
+ getAvailableComponents(current).length - 1,
1113
1113
  currentIndex + 1,
1114
1114
  );
1115
1115
  renderMenu();
1116
1116
  break;
1117
1117
  case " ": // Space bar
1118
- const component = getAvailableComponents(beta)[currentIndex];
1118
+ const component = getAvailableComponents(current)[currentIndex];
1119
1119
  if (selectedComponents.has(component)) {
1120
1120
  selectedComponents.delete(component);
1121
1121
  } else {
@@ -1158,23 +1158,23 @@ const parseMultipleComponents = (componentString) => {
1158
1158
  .filter((comp) => comp.length > 0);
1159
1159
  };
1160
1160
 
1161
- const validateComponents = (components, beta = false) => {
1162
- const availableComponents = getAvailableComponents(beta);
1161
+ const validateComponents = (components, current = false) => {
1162
+ const availableComponents = getAvailableComponents(current);
1163
1163
  const invalidComponents = components.filter(
1164
1164
  (comp) => !availableComponents.includes(comp),
1165
1165
  );
1166
1166
  if (invalidComponents.length > 0) {
1167
1167
  if (invalidComponents.some((comp) => comp.toLowerCase() === SHARED_DIR)) {
1168
1168
  console.error(
1169
- "`shared` is not a component: it is installed automatically with any beta component.",
1169
+ "`shared` is not a component: it is installed automatically with any component.",
1170
1170
  );
1171
1171
  }
1172
1172
  console.error(`Invalid components: ${invalidComponents.join(", ")}`);
1173
- console.log(`\nAvailable ${beta ? "" : "1.x "}components:`);
1173
+ console.log(`\nAvailable ${current ? "" : "1.x "}components:`);
1174
1174
  availableComponents.forEach((comp) => console.log(`- ${comp}`));
1175
- if (!beta) {
1176
- console.log("\nRedesigned beta components (install with -beta):");
1177
- CONFIG.betaComponents.forEach((comp) => console.log(`- ${comp}`));
1175
+ if (!current) {
1176
+ console.log("\nThe 2.x components, which are the default (drop --legacy):");
1177
+ CONFIG.components.forEach((comp) => console.log(`- ${comp}`));
1178
1178
  }
1179
1179
  return false;
1180
1180
  }
@@ -1306,7 +1306,7 @@ const main = async () => {
1306
1306
  * which is where the old `--beta` flag's meaning went: it used to opt *in* to
1307
1307
  * these, and now there is nothing to opt into.
1308
1308
  */
1309
- const beta = !argv.legacy;
1309
+ const current = !argv.legacy;
1310
1310
 
1311
1311
  if (argv["beta"] === true) {
1312
1312
  console.log(
@@ -1381,24 +1381,24 @@ const main = async () => {
1381
1381
 
1382
1382
  // List components if requested
1383
1383
  if (argv.list) {
1384
- const availableComponents = getAvailableComponents(beta);
1385
- console.log(`\nAvailable ${beta ? "" : "1.x "}components:`);
1384
+ const availableComponents = getAvailableComponents(current);
1385
+ console.log(`\nAvailable ${current ? "" : "1.x "}components:`);
1386
1386
  availableComponents.forEach((comp) => {
1387
1387
  const deps = CONFIG.dependencies[comp]
1388
1388
  ? ` (requires: ${CONFIG.dependencies[comp].join(", ")})`
1389
1389
  : "";
1390
1390
  console.log(`- ${comp}${deps}`);
1391
1391
  });
1392
- if (beta) {
1392
+ if (current) {
1393
1393
  console.log(
1394
- `\nEvery beta component also installs component-lib/${SHARED_DIR}, which they all import.`,
1394
+ `\nEvery component also installs component-lib/${SHARED_DIR}, which they all import.`,
1395
1395
  );
1396
1396
  console.log(
1397
1397
  `${NL}The agent skill installs separately, at the project root, for ${SKILL_TARGETS.join(", ")} and the GBS agent:${NL} npx @grampro/kit skill`,
1398
1398
  );
1399
1399
  } else {
1400
- console.log("\nRedesigned beta components (install with -beta):");
1401
- CONFIG.betaComponents.forEach((comp) => console.log(`- ${comp}`));
1400
+ console.log("\nThe 2.x components, which are the default (drop --legacy):");
1401
+ CONFIG.components.forEach((comp) => console.log(`- ${comp}`));
1402
1402
  }
1403
1403
  return;
1404
1404
  }
@@ -1406,7 +1406,7 @@ const main = async () => {
1406
1406
  // Interactive mode
1407
1407
  if (argv.interactive) {
1408
1408
  console.log("Starting interactive component selector...\n");
1409
- const selectedComponents = await interactiveComponentSelector(beta);
1409
+ const selectedComponents = await interactiveComponentSelector(current);
1410
1410
 
1411
1411
  if (selectedComponents.length === 0) {
1412
1412
  console.log("No components selected. Exiting...");
@@ -1416,10 +1416,10 @@ const main = async () => {
1416
1416
  const destPath = resolveDestPath(argv.dest);
1417
1417
  await fs.ensureDir(destPath);
1418
1418
 
1419
- await copySupportFiles(destPath, beta, { force: argv.force });
1419
+ await copySupportFiles(destPath, current, { force: argv.force });
1420
1420
 
1421
1421
  // Install selected components
1422
- await installMultipleComponents(selectedComponents, destPath, beta);
1422
+ await installMultipleComponents(selectedComponents, destPath, current);
1423
1423
  return;
1424
1424
  }
1425
1425
 
@@ -1453,27 +1453,27 @@ const main = async () => {
1453
1453
  const componentInput = argv.add;
1454
1454
  const components = parseMultipleComponents(componentInput).map(
1455
1455
  (component) =>
1456
- normalizeComponent(component, getAvailableComponents(beta)) ||
1456
+ normalizeComponent(component, getAvailableComponents(current)) ||
1457
1457
  component,
1458
1458
  );
1459
1459
 
1460
1460
  // Validate all components
1461
- if (!validateComponents(components, beta)) {
1461
+ if (!validateComponents(components, current)) {
1462
1462
  process.exit(1);
1463
1463
  }
1464
1464
 
1465
1465
  const destPath = resolveDestPath(argv.dest);
1466
1466
  await fs.ensureDir(destPath);
1467
1467
 
1468
- await copySupportFiles(destPath, beta, { force: argv.force });
1468
+ await copySupportFiles(destPath, current, { force: argv.force });
1469
1469
 
1470
1470
  // Install components
1471
1471
  if (components.length === 1) {
1472
1472
  // Single component installation (existing behavior)
1473
- await installComponentWithDependencies(components[0], destPath, beta);
1473
+ await installComponentWithDependencies(components[0], destPath, current);
1474
1474
  } else {
1475
1475
  // Multiple components installation
1476
- await installMultipleComponents(components, destPath, beta);
1476
+ await installMultipleComponents(components, destPath, current);
1477
1477
  }
1478
1478
  };
1479
1479
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grampro/kit",
3
- "version": "2.6.3",
3
+ "version": "2.7.0",
4
4
  "description": "GramproKit \u2014 an agent-native UI runtime for React. Copy-in components you own, each publishing a contract that software can read.",
5
5
  "type": "module",
6
6
  "files": [
@@ -45,7 +45,7 @@
45
45
  "author": "Anandhu Remanan",
46
46
  "license": "ISC",
47
47
  "dependencies": {
48
- "@grampro/headless-helpers": "^1.0.21",
48
+ "@grampro/headless-helpers": "^1.0.22",
49
49
  "fs-extra": "^11.0.4",
50
50
  "tailwind-merge": "^3.6.0",
51
51
  "yargs": "^17.7.2"
@@ -0,0 +1,67 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://kit.gramproindia.com/schema/passport-overlay-v1.schema.json",
4
+ "title": "GramproKit Passport Overlay v1",
5
+ "description": "The authored half of a component passport: passport.manual.json, written by the library, and passport.local.json, owned by the consuming developer. An overlay is a partial — the generator supplies passportVersion, identity, source, props, events, slots, states and coverage from the TypeScript, and merging the two produces passport.json, which passport-v1.schema.json describes. Validating an overlay against that schema reports all eight generated fields as missing and rejects the keys only an overlay has, which is why this file exists.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["component"],
9
+ "properties": {
10
+ "$schema": {
11
+ "type": "string"
12
+ },
13
+ "component": {
14
+ "type": "string",
15
+ "description": "Which component this annotates. Checked against the folder it sits in."
16
+ },
17
+ "purpose": {
18
+ "$ref": "./passport-v1.schema.json#/properties/purpose"
19
+ },
20
+ "accessibility": {
21
+ "$ref": "./passport-v1.schema.json#/properties/accessibility"
22
+ },
23
+ "composition": {
24
+ "$ref": "./passport-v1.schema.json#/properties/composition"
25
+ },
26
+ "operations": {
27
+ "$ref": "./passport-v1.schema.json#/properties/operations"
28
+ },
29
+ "intentDomains": {
30
+ "$ref": "./passport-v1.schema.json#/properties/intentDomains"
31
+ },
32
+ "safeMutations": {
33
+ "$ref": "./passport-v1.schema.json#/properties/safeMutations"
34
+ },
35
+ "examples": {
36
+ "$ref": "./passport-v1.schema.json#/properties/examples"
37
+ },
38
+ "props": {
39
+ "type": "array",
40
+ "description": "Annotations for derived props, by name: a description, a note or a deprecation. Only passport.local.json may introduce a prop that is not in the source.",
41
+ "items": {
42
+ "type": "object",
43
+ "required": ["name"],
44
+ "properties": {
45
+ "name": { "type": "string" },
46
+ "description": { "type": "string" },
47
+ "note": { "type": "string" },
48
+ "deprecated": { "type": ["string", "boolean"] }
49
+ }
50
+ }
51
+ },
52
+ "propNotes": {
53
+ "type": "object",
54
+ "description": "Shorthand for annotating derived props: a map of prop name to note. Every key must name a prop the source actually has.",
55
+ "additionalProperties": { "type": "string" }
56
+ },
57
+ "notImplemented": {
58
+ "type": "array",
59
+ "description": "Capabilities a reader might reasonably expect and this component does not have. Saying so is cheaper than an agent discovering it by writing code that cannot work.",
60
+ "items": { "type": "string" }
61
+ },
62
+ "$operationsNote": {
63
+ "type": "string",
64
+ "description": "Where the operations block is mirrored from, and what fails if the two drift."
65
+ }
66
+ }
67
+ }
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "source": {
13
- "libraryVersion": "2.6.3",
13
+ "libraryVersion": "2.7.0",
14
14
  "files": [
15
15
  "core/index.ts",
16
16
  "core/open.ts",
@@ -23,6 +23,7 @@
23
23
  ],
24
24
  "sourceHash": "sha256:c04b9c09e8661f463e2a808ecd45a7c080c236bc7b398efc1b3505a45e245f00"
25
25
  },
26
+ "purpose": "Sections that open and close, built on native <details> and <summary>, with single or multiple expansion.",
26
27
  "props": [
27
28
  {
28
29
  "name": "children",
@@ -53,7 +54,8 @@
53
54
  "default": true,
54
55
  "origin": "derived",
55
56
  "description": "Allow the open panel to be closed again. Default true.",
56
- "descriptionOrigin": "jsdoc"
57
+ "descriptionOrigin": "jsdoc",
58
+ "note": "Whether the last open section can be closed, leaving none open."
57
59
  },
58
60
  {
59
61
  "name": "defaultValue",
@@ -244,20 +246,55 @@
244
246
  }
245
247
  ],
246
248
  "accessibility": {
247
- "supplied": [],
248
- "required": []
249
+ "supplied": [
250
+ "native <details>/<summary> semantics, so it works before JavaScript loads and is findable by in-page search",
251
+ "Enter and Space toggle the summary, which is focusable by default",
252
+ "the open state is a real DOM attribute rather than a class"
253
+ ],
254
+ "required": [
255
+ "summary text that says what is inside, since a collapsed section is judged entirely by its heading",
256
+ "`multiple` where sections are independent — forcing one open at a time hides content a user was reading"
257
+ ]
258
+ },
259
+ "composition": {
260
+ "allowedChildren": [
261
+ "AccordionItem"
262
+ ],
263
+ "notes": "Items come from children or the `items` array, not both."
249
264
  },
250
- "composition": {},
251
265
  "operations": {},
252
- "intentDomains": [],
253
- "safeMutations": {},
266
+ "intentDomains": [
267
+ "ActionIntent"
268
+ ],
269
+ "safeMutations": {
270
+ "allowed": [
271
+ "variant",
272
+ "size",
273
+ "icon",
274
+ "iconPosition",
275
+ "collapsible",
276
+ "multiple"
277
+ ],
278
+ "forbidden": [
279
+ "value",
280
+ "defaultValue",
281
+ "onValueChange",
282
+ "items",
283
+ "disabled"
284
+ ],
285
+ "rationale": "Appearance is safe; which section is open is state, and `items` is the content."
286
+ },
254
287
  "examples": [],
255
288
  "coverage": {
256
289
  "propsTotal": 15,
257
290
  "propsDescribed": 12,
258
291
  "describedPct": 80,
259
292
  "authoredSections": [
260
- "accessibility"
293
+ "accessibility",
294
+ "composition",
295
+ "intentDomains",
296
+ "purpose",
297
+ "safeMutations"
261
298
  ]
262
299
  }
263
300
  }
@@ -0,0 +1,46 @@
1
+ {
2
+ "$schema": "../../../schema/passport-overlay-v1.schema.json",
3
+ "component": "Accordion",
4
+ "purpose": "Sections that open and close, built on native <details> and <summary>, with single or multiple expansion.",
5
+ "intentDomains": [
6
+ "ActionIntent"
7
+ ],
8
+ "accessibility": {
9
+ "supplied": [
10
+ "native <details>/<summary> semantics, so it works before JavaScript loads and is findable by in-page search",
11
+ "Enter and Space toggle the summary, which is focusable by default",
12
+ "the open state is a real DOM attribute rather than a class"
13
+ ],
14
+ "required": [
15
+ "summary text that says what is inside, since a collapsed section is judged entirely by its heading",
16
+ "`multiple` where sections are independent — forcing one open at a time hides content a user was reading"
17
+ ]
18
+ },
19
+ "composition": {
20
+ "allowedChildren": [
21
+ "AccordionItem"
22
+ ],
23
+ "notes": "Items come from children or the `items` array, not both."
24
+ },
25
+ "safeMutations": {
26
+ "allowed": [
27
+ "variant",
28
+ "size",
29
+ "icon",
30
+ "iconPosition",
31
+ "collapsible",
32
+ "multiple"
33
+ ],
34
+ "forbidden": [
35
+ "value",
36
+ "defaultValue",
37
+ "onValueChange",
38
+ "items",
39
+ "disabled"
40
+ ],
41
+ "rationale": "Appearance is safe; which section is open is state, and `items` is the content."
42
+ },
43
+ "propNotes": {
44
+ "collapsible": "Whether the last open section can be closed, leaving none open."
45
+ }
46
+ }
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.6.3",
11
+ "libraryVersion": "2.7.0",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/live.ts",
@@ -21,6 +21,7 @@
21
21
  ],
22
22
  "sourceHash": "sha256:1d3329cee22213d4fb81062f401b481902600b2a2b3a81edf12c6738934f6923"
23
23
  },
24
+ "purpose": "A message that stays in the page — neither interrupting like a dialog nor leaving like a toast. For conditions the user needs to keep seeing.",
24
25
  "props": [
25
26
  {
26
27
  "name": "actions",
@@ -178,7 +179,8 @@
178
179
  "default": "info",
179
180
  "origin": "derived",
180
181
  "description": "Default `info`.",
181
- "descriptionOrigin": "jsdoc"
182
+ "descriptionOrigin": "jsdoc",
183
+ "note": "Decides the role: error and warning are announced, info and success are not."
182
184
  }
183
185
  ],
184
186
  "inherits": [
@@ -231,20 +233,51 @@
231
233
  }
232
234
  ],
233
235
  "accessibility": {
234
- "supplied": [],
235
- "required": []
236
+ "supplied": [
237
+ "role=alert on the error and warning variants, so they are announced when they appear",
238
+ "the variant is carried in text and icon, not by colour alone",
239
+ "the dismiss control is a real button with an accessible name"
240
+ ],
241
+ "required": [
242
+ "a message that says what to do, not only what went wrong",
243
+ "do not use `onDismiss` for something the user must act on — a dismissible error can be dismissed unread"
244
+ ]
245
+ },
246
+ "composition": {
247
+ "mustNotContain": [
248
+ "Alert"
249
+ ],
250
+ "notes": "Actions belong in `actions`, which keeps them in the alert's landmark rather than loose in the body."
236
251
  },
237
- "composition": {},
238
252
  "operations": {},
239
- "intentDomains": [],
240
- "safeMutations": {},
253
+ "intentDomains": [
254
+ "Summarization"
255
+ ],
256
+ "safeMutations": {
257
+ "allowed": [
258
+ "size",
259
+ "icon",
260
+ "description"
261
+ ],
262
+ "forbidden": [
263
+ "variant",
264
+ "children",
265
+ "actions",
266
+ "onDismiss"
267
+ ],
268
+ "rationale": "`variant` is the severity. Downgrading an error to info changes what the page is telling the user, and it changes whether it is announced at all."
269
+ },
241
270
  "examples": [],
242
271
  "coverage": {
243
272
  "propsTotal": 8,
244
273
  "propsDescribed": 6,
245
274
  "describedPct": 75,
246
275
  "authoredSections": [
247
- "accessibility"
276
+ "accessibility",
277
+ "composition",
278
+ "intentDomains",
279
+ "purpose",
280
+ "safeMutations"
248
281
  ]
249
282
  }
250
283
  }