elcrm 1.1.38 → 1.1.39

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/dist/index.js CHANGED
@@ -1304,10 +1304,10 @@ function inspectPkgCss(pkgName, cwd) {
1304
1304
  return null;
1305
1305
  const pkg = readPkgJson(root);
1306
1306
  const exp = pkg?.exports;
1307
- const abs = (rel2) => rel2 ? join5(root, rel2.replace(/^\.\//, "")) : undefined;
1307
+ const abs = (rel) => rel ? join5(root, rel.replace(/^\.\//, "")) : undefined;
1308
1308
  const fileIfExists = (key) => {
1309
- const rel2 = exportPath(exp, key);
1310
- const a = abs(rel2);
1309
+ const rel = exportPath(exp, key);
1310
+ const a = abs(rel);
1311
1311
  return a && existsSync5(a) ? a : undefined;
1312
1312
  };
1313
1313
  return {
@@ -1330,10 +1330,10 @@ function resolveElcrmCssImport(spec, cwd) {
1330
1330
  if (!root)
1331
1331
  return null;
1332
1332
  const pkg = readPkgJson(root);
1333
- const rel2 = exportPath(pkg?.exports, `./${sub}`);
1334
- if (!rel2)
1333
+ const rel = exportPath(pkg?.exports, `./${sub}`);
1334
+ if (!rel)
1335
1335
  return null;
1336
- const abs = join5(root, rel2.replace(/^\.\//, ""));
1336
+ const abs = join5(root, rel.replace(/^\.\//, ""));
1337
1337
  return existsSync5(abs) ? abs : null;
1338
1338
  }
1339
1339
  function pruneBrokenElcrmImports(css, cwd) {
@@ -1628,6 +1628,8 @@ function checkElcrmTokenCoverage(options) {
1628
1628
  const used = new Set;
1629
1629
  for (const file of styleFiles) {
1630
1630
  const css = readFileSync7(file, "utf8");
1631
+ for (const k of parseCustomProps(css).keys())
1632
+ defined.add(k);
1631
1633
  for (const m of css.matchAll(/var\(\s*(--[a-zA-Z0-9-]+)/g)) {
1632
1634
  used.add(m[1]);
1633
1635
  }
@@ -1640,6 +1642,9 @@ function checkElcrmTokenCoverage(options) {
1640
1642
  continue;
1641
1643
  if (/^--tooltip-arrow-/.test(token))
1642
1644
  continue;
1645
+ if (token === "--alpha-color" || token === "--preview-color" || token === "--swatch") {
1646
+ continue;
1647
+ }
1643
1648
  gaps.push({ pkg: pkgName, token });
1644
1649
  }
1645
1650
  }
@@ -1757,7 +1762,7 @@ async function migrateSocketServer(cwd, dry) {
1757
1762
  pkg.dependencies["@elcrm/server"] = ver;
1758
1763
  }
1759
1764
  }
1760
- if (!dry && changes.some((c2) => c2.file === "package.json")) {
1765
+ if (!dry && changes.some((c) => c.file === "package.json")) {
1761
1766
  writePackageJson(pkg, cwd);
1762
1767
  }
1763
1768
  }
@@ -2199,25 +2204,25 @@ function sliceAttrValue(attrs, eqIndex) {
2199
2204
  let brace = 0;
2200
2205
  let quote = null;
2201
2206
  while (i < attrs.length) {
2202
- const c2 = attrs[i];
2207
+ const c = attrs[i];
2203
2208
  if (quote) {
2204
- if (c2 === "\\" && quote === '"') {
2209
+ if (c === "\\" && quote === '"') {
2205
2210
  i += 2;
2206
2211
  continue;
2207
2212
  }
2208
- if (c2 === quote)
2213
+ if (c === quote)
2209
2214
  quote = null;
2210
2215
  i++;
2211
2216
  continue;
2212
2217
  }
2213
- if (c2 === '"' || c2 === "'") {
2214
- quote = c2;
2218
+ if (c === '"' || c === "'") {
2219
+ quote = c;
2215
2220
  i++;
2216
2221
  continue;
2217
2222
  }
2218
- if (c2 === "{")
2223
+ if (c === "{")
2219
2224
  brace++;
2220
- else if (c2 === "}") {
2225
+ else if (c === "}") {
2221
2226
  brace--;
2222
2227
  if (brace === 0) {
2223
2228
  i++;
@@ -2476,8 +2481,8 @@ async function migrateFrontAll(cwd, dry) {
2476
2481
  const all = [];
2477
2482
  for (const name of FRONT_MIGRATE_ORDER) {
2478
2483
  const part = await FRONT_MIGRATIONS[name](cwd, dry);
2479
- for (const c2 of part) {
2480
- all.push({ file: c2.file, detail: `[${name}] ${c2.detail}` });
2484
+ for (const c of part) {
2485
+ all.push({ file: c.file, detail: `[${name}] ${c.detail}` });
2481
2486
  }
2482
2487
  }
2483
2488
  return all;
@@ -2591,12 +2596,12 @@ import { resolve } from "path";
2591
2596
  import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync16, statSync as statSync5 } from "fs";
2592
2597
  import { join as join8, relative as relative2 } from "path";
2593
2598
  var SKIP = new Set(["node_modules", "dist", ".git", "coverage", ".bun"]);
2594
- function isThemeTokenFile(rel2) {
2595
- const n = rel2.replace(/\\/g, "/");
2599
+ function isThemeTokenFile(rel) {
2600
+ const n = rel.replace(/\\/g, "/");
2596
2601
  return /(?:^|\/)(?:theme(?:-light|-dark)?|elcrm)\.css$/.test(n);
2597
2602
  }
2598
- function isGeneratedIcons(rel2) {
2599
- return /icons\.generated\.(tsx?|jsx?)$/.test(rel2.replace(/\\/g, "/"));
2603
+ function isGeneratedIcons(rel) {
2604
+ return /icons\.generated\.(tsx?|jsx?)$/.test(rel.replace(/\\/g, "/"));
2600
2605
  }
2601
2606
  var HARD_HEX_RE = /#[0-9a-fA-F]{3,8}\b/;
2602
2607
  var HARD_RGB_RE = /\b(?:rgb|rgba|hsl|hsla)\s*\(/i;
@@ -2739,14 +2744,14 @@ function collectFrontIssues(cwd, pkg) {
2739
2744
  }
2740
2745
  for (const file of files) {
2741
2746
  const text = readFileSync16(file, "utf8");
2742
- const rel2 = relative2(cwd, file);
2743
- const skipHardColor = isLib || isThemeTokenFile(rel2) || isGeneratedIcons(rel2);
2747
+ const rel = relative2(cwd, file);
2748
+ const skipHardColor = isLib || isThemeTokenFile(rel) || isGeneratedIcons(rel);
2744
2749
  if (DEPRECATED_SOCKET_IMPORT_RE.test(text)) {
2745
2750
  issues.push({
2746
2751
  level: "error",
2747
2752
  code: "deprecated-import",
2748
2753
  message: "\u0438\u043C\u043F\u043E\u0440\u0442 @elcrm/socket/server \u0438\u043B\u0438 socket-server",
2749
- file: rel2,
2754
+ file: rel,
2750
2755
  fixable: true
2751
2756
  });
2752
2757
  }
@@ -2758,13 +2763,13 @@ function collectFrontIssues(cwd, pkg) {
2758
2763
  level: "error",
2759
2764
  code: "themes-css",
2760
2765
  message: "import \u2026/themes.css \u0443\u0441\u0442\u0430\u0440\u0435\u043B \u2014 \u0442\u043E\u043A\u0435\u043D\u044B \u0437\u0430\u0434\u0430\u0451\u0442 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 (elcrm-theme / theme.css)",
2761
- file: rel2,
2766
+ file: rel,
2762
2767
  line
2763
2768
  });
2764
2769
  }
2765
2770
  if (LEGACY_PASSKEY_IMPORT_RE.test(text) && LEGACY_PASSKEY_SYMBOL_RE.test(text)) {
2766
- const lines2 = text.split(/\r?\n/);
2767
- lines2.forEach((line, i) => {
2771
+ const lines = text.split(/\r?\n/);
2772
+ lines.forEach((line, i) => {
2768
2773
  if (/\bcreatePasskey\b/.test(line) || /\bauthenticateWithPasskey\b/.test(line)) {
2769
2774
  if (pkg.name === "@elcrm/passkey")
2770
2775
  return;
@@ -2772,7 +2777,7 @@ function collectFrontIssues(cwd, pkg) {
2772
2777
  level: "error",
2773
2778
  code: "legacy-passkey-api",
2774
2779
  message: "createPasskey / authenticateWithPasskey \u043D\u0435 \u044D\u043A\u0441\u043F\u043E\u0440\u0442\u0438\u0440\u0443\u044E\u0442\u0441\u044F \u2014 createPasskeyClient",
2775
- file: rel2,
2780
+ file: rel,
2776
2781
  line: i + 1
2777
2782
  });
2778
2783
  }
@@ -2791,19 +2796,19 @@ function collectFrontIssues(cwd, pkg) {
2791
2796
  level: "error",
2792
2797
  code: "native-html-input",
2793
2798
  message: "\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 <input|textarea|select> \u2014 @elcrm/form (elcrm migrate native-html)",
2794
- file: rel2,
2799
+ file: rel,
2795
2800
  line: i + 1,
2796
2801
  fixable: true
2797
2802
  });
2798
2803
  }
2799
2804
  }
2800
- if (!/(^|\/)pages\/Auth\.tsx$/.test(rel2.replace(/\\/g, "/")) && /(?:^|\s)native(?:\s|$|=|\/|>)/.test(line) && !/\bnavigator\b|\bnativeEvent\b|\bcreateNative/.test(line) && !/['"`]native['"`]/.test(line) && !(/\binstallMode\b/.test(line) && /\bnative\b/.test(line))) {
2805
+ if (!/(^|\/)pages\/Auth\.tsx$/.test(rel.replace(/\\/g, "/")) && /(?:^|\s)native(?:\s|$|=|\/|>)/.test(line) && !/\bnavigator\b|\bnativeEvent\b|\bcreateNative/.test(line) && !/['"`]native['"`]/.test(line) && !(/\binstallMode\b/.test(line) && /\bnative\b/.test(line))) {
2801
2806
  if (pkg.name !== "@elcrm/form") {
2802
2807
  issues.push({
2803
2808
  level: "warn",
2804
2809
  code: "form-native-prop",
2805
2810
  message: "prop native \u0443 \u043F\u043E\u043B\u044F \u0444\u043E\u0440\u043C\u044B \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 (elcrm migrate form-native)",
2806
- file: rel2,
2811
+ file: rel,
2807
2812
  line: i + 1,
2808
2813
  fixable: true
2809
2814
  });
@@ -2814,7 +2819,7 @@ function collectFrontIssues(cwd, pkg) {
2814
2819
  level: "warn",
2815
2820
  code: "size-legacy",
2816
2821
  message: 'size="sm"|"md" \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u043E \u2014 "s"|"m" (elcrm migrate size-sml)',
2817
- file: rel2,
2822
+ file: rel,
2818
2823
  line: i + 1,
2819
2824
  fixable: true
2820
2825
  });
@@ -2824,7 +2829,7 @@ function collectFrontIssues(cwd, pkg) {
2824
2829
  level: "warn",
2825
2830
  code: "modal-add",
2826
2831
  message: "Modal.Add \u2014 Modal.Create + Open (elcrm migrate modal-create)",
2827
- file: rel2,
2832
+ file: rel,
2828
2833
  line: i + 1,
2829
2834
  fixable: true
2830
2835
  });
@@ -2834,7 +2839,7 @@ function collectFrontIssues(cwd, pkg) {
2834
2839
  level: "error",
2835
2840
  code: "field-border-shorthand",
2836
2841
  message: "field-border hover/focus/disabled \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0446\u0432\u0435\u0442 (elcrm migrate field-border)",
2837
- file: rel2,
2842
+ file: rel,
2838
2843
  line: i + 1,
2839
2844
  fixable: true
2840
2845
  });
@@ -2844,7 +2849,7 @@ function collectFrontIssues(cwd, pkg) {
2844
2849
  level: "warn",
2845
2850
  code: "form-token-legacy",
2846
2851
  message: "legacy padding/shadow-\u0442\u043E\u043A\u0435\u043D \u2192 --field-padding* / --popup-shadow (elcrm migrate form-tokens)",
2847
- file: rel2,
2852
+ file: rel,
2848
2853
  line: i + 1,
2849
2854
  fixable: true
2850
2855
  });
@@ -2854,7 +2859,7 @@ function collectFrontIssues(cwd, pkg) {
2854
2859
  level: "warn",
2855
2860
  code: "components-token-legacy",
2856
2861
  message: "legacy \u0442\u043E\u043A\u0435\u043D @elcrm/components \u2192 --shell-* (elcrm migrate components)",
2857
- file: rel2,
2862
+ file: rel,
2858
2863
  line: i + 1,
2859
2864
  fixable: true
2860
2865
  });
@@ -2864,7 +2869,7 @@ function collectFrontIssues(cwd, pkg) {
2864
2869
  level: "error",
2865
2870
  code: "css-orphan-mix",
2866
2871
  message: "\u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0438\u0439 \u0445\u0432\u043E\u0441\u0442 color-mix (in srgb\u2026) \u2014 elcrm migrate css-sanitize",
2867
- file: rel2,
2872
+ file: rel,
2868
2873
  line: i + 1,
2869
2874
  fixable: true
2870
2875
  });
@@ -2877,7 +2882,7 @@ function collectFrontIssues(cwd, pkg) {
2877
2882
  level: "warn",
2878
2883
  code: "components-stack",
2879
2884
  message: "Block/Row/Column \u0443\u0441\u0442\u0430\u0440\u0435\u043B\u0438 \u2014 Stack (elcrm migrate components)",
2880
- file: rel2,
2885
+ file: rel,
2881
2886
  line: i + 1,
2882
2887
  fixable: true
2883
2888
  });
@@ -2888,7 +2893,7 @@ function collectFrontIssues(cwd, pkg) {
2888
2893
  level: "warn",
2889
2894
  code: "components-value",
2890
2895
  message: "activeKey/defaultActiveKey \u2192 value/defaultValue (elcrm migrate components)",
2891
- file: rel2,
2896
+ file: rel,
2892
2897
  line: i + 1,
2893
2898
  fixable: true
2894
2899
  });
@@ -2900,7 +2905,7 @@ function collectFrontIssues(cwd, pkg) {
2900
2905
  level: "warn",
2901
2906
  code: "hardcoded-color",
2902
2907
  message: why,
2903
- file: rel2,
2908
+ file: rel,
2904
2909
  line: i + 1
2905
2910
  });
2906
2911
  }
@@ -3290,7 +3295,7 @@ function walkFiles2(root, dir, out) {
3290
3295
  function scanCodeFile(root, file, text) {
3291
3296
  const findings = [];
3292
3297
  const lines = text.split(/\r?\n/);
3293
- const rel2 = relative3(root, file) || file;
3298
+ const rel = relative3(root, file) || file;
3294
3299
  for (const rule of CODE_RULES) {
3295
3300
  lines.forEach((line, i) => {
3296
3301
  rule.test.lastIndex = 0;
@@ -3301,7 +3306,7 @@ function scanCodeFile(root, file, text) {
3301
3306
  findings.push({
3302
3307
  ruleId: rule.id,
3303
3308
  severity: rule.severity,
3304
- file: rel2,
3309
+ file: rel,
3305
3310
  line: i + 1,
3306
3311
  match: (m?.[0] ?? line).trim().slice(0, 120),
3307
3312
  message: rule.message,
@@ -3314,7 +3319,7 @@ function scanCodeFile(root, file, text) {
3314
3319
  }
3315
3320
  function scanPackageJson(root, file, text) {
3316
3321
  const findings = [];
3317
- const rel2 = relative3(root, file) || file;
3322
+ const rel = relative3(root, file) || file;
3318
3323
  let pkg;
3319
3324
  try {
3320
3325
  pkg = JSON.parse(text);
@@ -3327,7 +3332,7 @@ function scanPackageJson(root, file, text) {
3327
3332
  findings.push({
3328
3333
  ruleId: `pkg-${name}`,
3329
3334
  severity: "error",
3330
- file: rel2,
3335
+ file: rel,
3331
3336
  line: 1,
3332
3337
  match: `"${name}": "${deps[name]}"`,
3333
3338
  message: `\u0417\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C ${name} \u2192 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 @elcrm/server (+ subpath)`,
@@ -3340,7 +3345,7 @@ function scanPackageJson(root, file, text) {
3340
3345
  findings.push({
3341
3346
  ruleId: "pkg-need-server",
3342
3347
  severity: "error",
3343
- file: rel2,
3348
+ file: rel,
3344
3349
  line: 1,
3345
3350
  match: "missing @elcrm/server",
3346
3351
  message: "\u041D\u0443\u0436\u043D\u0430 \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C @elcrm/server",
@@ -3792,8 +3797,8 @@ async function cmdMigrate(parsed) {
3792
3797
  if (hasFlag(parsed, "list") || parsed.positionals[0] === "list") {
3793
3798
  console.log("\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0438:");
3794
3799
  console.log(" front | ui \u043F\u0430\u043A\u0435\u0442 \u0444\u0440\u043E\u043D\u0442\u043E\u0432\u044B\u0445 \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u0439:");
3795
- for (const name2 of FRONT_MIGRATE_ORDER) {
3796
- console.log(` - ${name2}`);
3800
+ for (const name of FRONT_MIGRATE_ORDER) {
3801
+ console.log(` - ${name}`);
3797
3802
  }
3798
3803
  console.log(" socket-server (\u0442\u0430\u043A\u0436\u0435 \u0432 front)");
3799
3804
  return;
@@ -3819,8 +3824,8 @@ async function cmdMigrate(parsed) {
3819
3824
  console.log("\u0418\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F.");
3820
3825
  return;
3821
3826
  }
3822
- for (const c2 of changes) {
3823
- console.log(` ${c2.file}: ${c2.detail}`);
3827
+ for (const c of changes) {
3828
+ console.log(` ${c.file}: ${c.detail}`);
3824
3829
  }
3825
3830
  console.log(dry ? `
3826
3831
  --dry: ${changes.length} \u043F\u043E\u0442\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u0445 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0439.` : `
@@ -4192,8 +4197,8 @@ function emitKitManifest(cwd) {
4192
4197
  throw new Error(`elcrm kit emit \u2014 \u043F\u0430\u043A\u0435\u0442 ${pkg.name} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F (\u043D\u0443\u0436\u0435\u043D components|form)`);
4193
4198
  }
4194
4199
  const distDir = join15(cwd, "dist");
4195
- const components = scanDistComponents(distDir, pkg.name).map((c2) => ({
4196
- ...c2,
4200
+ const components = scanDistComponents(distDir, pkg.name).map((c) => ({
4201
+ ...c,
4197
4202
  importFrom: pkg.name
4198
4203
  }));
4199
4204
  return {
@@ -4750,14 +4755,14 @@ async function cmdCss(parsed) {
4750
4755
  console.log(`${dry || checkOnly ? "[check/dry] " : ""}\u041F\u0430\u043A\u0435\u0442\u044B: ${names.join(", ")}`);
4751
4756
  try {
4752
4757
  if (checkOnly) {
4753
- const cov2 = checkElcrmTokenCoverage({ cwd, pkgNames: names });
4754
- console.log(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u0441\u0442\u0438\u043B\u0435\u0439: ${cov2.styleDir ?? "(\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)"} \xB7 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E var(): ${cov2.checked}`);
4755
- if (!cov2.gaps.length) {
4758
+ const cov = checkElcrmTokenCoverage({ cwd, pkgNames: names });
4759
+ console.log(`\u041A\u0430\u0442\u0430\u043B\u043E\u0433 \u0441\u0442\u0438\u043B\u0435\u0439: ${cov.styleDir ?? "(\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)"} \xB7 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E var(): ${cov.checked}`);
4760
+ if (!cov.gaps.length) {
4756
4761
  console.log(c.green("\u041F\u043E\u043A\u0440\u044B\u0442\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 OK."));
4757
4762
  return;
4758
4763
  }
4759
- console.error(`${c.red("\u041F\u0440\u043E\u0431\u0435\u043B\u044B:")} ${cov2.gaps.length} (\u043D\u0435\u0442 \u0432 theme*/\u043F\u0430\u043A\u0435\u0442\u0435 light|dark|tokens)`);
4760
- for (const g of cov2.gaps) {
4764
+ console.error(`${c.red("\u041F\u0440\u043E\u0431\u0435\u043B\u044B:")} ${cov.gaps.length} (\u043D\u0435\u0442 \u0432 theme*/\u043F\u0430\u043A\u0435\u0442\u0435 light|dark|tokens)`);
4765
+ for (const g of cov.gaps) {
4761
4766
  console.error(` ${c.dim(g.pkg)} ${g.token}`);
4762
4767
  }
4763
4768
  process.exit(1);
@@ -4853,7 +4858,7 @@ function scan(cwd) {
4853
4858
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
4854
4859
  for (const file of files) {
4855
4860
  const text = readFileSync27(file, "utf8");
4856
- const rel2 = relative4(cwd, file);
4861
+ const rel = relative4(cwd, file);
4857
4862
  const lines = text.split(/\r?\n/);
4858
4863
  lines.forEach((line, i) => {
4859
4864
  const n = i + 1;
@@ -4864,7 +4869,7 @@ function scan(cwd) {
4864
4869
  level: "warn",
4865
4870
  code: "size-legacy",
4866
4871
  message: 'size="sm"|"md" \u2192 "s"|"m"',
4867
- file: rel2,
4872
+ file: rel,
4868
4873
  line: n,
4869
4874
  fix: "elcrm migrate size-sml"
4870
4875
  });
@@ -4874,7 +4879,7 @@ function scan(cwd) {
4874
4879
  level: "warn",
4875
4880
  code: "modal-add",
4876
4881
  message: "Modal.Add \u2014 \u043F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0438\u0442\u0435\u043B\u0435\u043D Modal.Create + Open",
4877
- file: rel2,
4882
+ file: rel,
4878
4883
  line: n,
4879
4884
  fix: "elcrm migrate modal-create"
4880
4885
  });
@@ -4884,7 +4889,7 @@ function scan(cwd) {
4884
4889
  level: "error",
4885
4890
  code: "native-html-input",
4886
4891
  message: "\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 <input|textarea|select> \u2192 @elcrm/form",
4887
- file: rel2,
4892
+ file: rel,
4888
4893
  line: n,
4889
4894
  fix: "elcrm migrate native-html"
4890
4895
  });
@@ -4894,7 +4899,7 @@ function scan(cwd) {
4894
4899
  level: "warn",
4895
4900
  code: "form-native-prop",
4896
4901
  message: "prop native \u0443 \u043F\u043E\u043B\u044F \u0444\u043E\u0440\u043C\u044B \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435",
4897
- file: rel2,
4902
+ file: rel,
4898
4903
  line: n,
4899
4904
  fix: "elcrm migrate form-native"
4900
4905
  });
@@ -4904,7 +4909,7 @@ function scan(cwd) {
4904
4909
  level: "info",
4905
4910
  code: "modal-date-key",
4906
4911
  message: "key={Date.now()} \u0443 \u043C\u043E\u0434\u0430\u043B\u043A\u0438 \u2014 Create \u0437\u0430\u0434\u0430\u0451\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u044B\u0439 name",
4907
- file: rel2,
4912
+ file: rel,
4908
4913
  line: n
4909
4914
  });
4910
4915
  }
@@ -4913,17 +4918,17 @@ function scan(cwd) {
4913
4918
  level: "error",
4914
4919
  code: "field-border-shorthand",
4915
4920
  message: "hover/focus border \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0446\u0432\u0435\u0442, \u043D\u0435 shorthand",
4916
- file: rel2,
4921
+ file: rel,
4917
4922
  line: n,
4918
4923
  fix: "elcrm migrate field-border"
4919
4924
  });
4920
4925
  }
4921
- if (/!important/.test(line) && /drag|file-upload|DragDrop/i.test(rel2 + line)) {
4926
+ if (/!important/.test(line) && /drag|file-upload|DragDrop/i.test(rel + line)) {
4922
4927
  out.push({
4923
4928
  level: "info",
4924
4929
  code: "dragdrop-important",
4925
4930
  message: '!important \u0443 DragDrop \u2014 layout="fill"|"overlay" \u0432 @elcrm/form',
4926
- file: rel2,
4931
+ file: rel,
4927
4932
  line: n
4928
4933
  });
4929
4934
  }
@@ -4932,7 +4937,7 @@ function scan(cwd) {
4932
4937
  level: "warn",
4933
4938
  code: "react-global",
4934
4939
  message: "globalThis.React \u0445\u0430\u043A \u2014 migrate legacy-hacks / \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u0435 modal",
4935
- file: rel2,
4940
+ file: rel,
4936
4941
  line: n,
4937
4942
  fix: "elcrm migrate legacy-hacks"
4938
4943
  });
@@ -4942,7 +4947,7 @@ function scan(cwd) {
4942
4947
  level: "error",
4943
4948
  code: "form-alias",
4944
4949
  message: "\u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0439 \u0430\u043B\u0438\u0430\u0441 @elcrm/form (ProgressField/OtpField/\u2026) \u2192 \u043A\u0430\u043D\u043E\u043D",
4945
- file: rel2,
4950
+ file: rel,
4946
4951
  line: n,
4947
4952
  fix: "elcrm migrate form-aliases"
4948
4953
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elcrm",
3
- "version": "1.1.38",
3
+ "version": "1.1.39",
4
4
  "description": "CLI @elcrm/*: doctor --fix (порядок проекта), update, css, docs, cursor, kit, migrate, publish",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -107,6 +107,7 @@ import type { PricingPlan, HealthmapData, NavTreeItem } from "@elcrm/components"
107
107
 
108
108
  - `--layout-height` — высота каркаса `Layout` при `fixed` (по умолчанию `100dvh`).
109
109
  - `--layout-padding` / `--layout-margin` / `--layout-max-width` — отступы и макс. ширина корня Layout (`0` / `none` = на весь вьюпорт).
110
+ - `--layout-main-padding` — padding у `<main>` (`.slm`): сверху/снизу между шапкой и контентом (`0` по умолчанию; например `16px 0`).
110
111
  - `--header-border` — рамка Header (shorthand: все стороны и толщина). Только снизу: `none` + линия через `--header-shadow`.
111
112
  - `--header-nav-justify` — выравнивание `nav` в шапке (`flex-start` / `center` / `flex-end`).
112
113
  - `<Toolbar size="s" width="l" align="start">` — один размер для Search / `*Field` / Button / Dropdown. `width="s"` — ряд по контенту. Свой `size` у ребёнка важнее.
@@ -243,6 +244,7 @@ elcrm cursor
243
244
  | `--item-column-title-size` | geometry | `var(--shell-font-size)` |
244
245
  | `--layout-gap` | geometry | `0` |
245
246
  | `--layout-height` | geometry | `100dvh` |
247
+ | `--layout-main-padding` | geometry | `0` |
246
248
  | `--layout-main-radius` | geometry | `0` |
247
249
  | `--layout-margin` | geometry | `0` |
248
250
  | `--layout-max-width` | geometry | `none` |
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-09-02T21:16:40.858Z",
2
+ "generatedAt": "2026-09-02T21:23:46.848Z",
3
3
  "packages": [
4
4
  {
5
5
  "name": "@elcrm/alert",
@@ -28,6 +28,7 @@
28
28
  --control-height-l: 48px;
29
29
  --control-radius: 10px;
30
30
  --control-font-size: 15px;
31
+ --control-font-size-s: 0.8125rem;
31
32
  --field-height: var(--control-height);
32
33
  --field-width: 100%;
33
34
  /* единый padding капсулы (size s|m|l → через --field-height) */
@@ -69,6 +70,8 @@
69
70
  --layout-main-background: transparent;
70
71
  --layout-main-shadow: none;
71
72
  --layout-main-border: none;
73
+ /* padding <main>: сверху/снизу между шапкой и контентом */
74
+ --layout-main-padding: 0;
72
75
  /* none — на всю ширину; иначе например 1440px */
73
76
  --layout-max-width: none;
74
77
  --elcrm-z-popover: 1000;
@@ -28,6 +28,7 @@
28
28
  --control-height-l: 48px;
29
29
  --control-radius: 10px;
30
30
  --control-font-size: 15px;
31
+ --control-font-size-s: 0.8125rem;
31
32
  --field-height: var(--control-height);
32
33
  --field-width: 100%;
33
34
  /* единый padding капсулы (size s|m|l → через --field-height) */
@@ -69,6 +70,8 @@
69
70
  --layout-main-background: transparent;
70
71
  --layout-main-shadow: none;
71
72
  --layout-main-border: none;
73
+ /* padding <main>: сверху/снизу между шапкой и контентом */
74
+ --layout-main-padding: 0;
72
75
  /* none — на всю ширину; иначе например 1440px */
73
76
  --layout-max-width: none;
74
77
  --elcrm-z-popover: 1000;