elcrm 1.1.20 → 1.1.22

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
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/index.ts
5
- import { readFileSync as readFileSync27 } from "fs";
6
- import { dirname as dirname4, join as join23 } from "path";
5
+ import { readFileSync as readFileSync28 } from "fs";
6
+ import { dirname as dirname4, join as join25 } from "path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "url";
8
8
 
9
9
  // src/parseArgs.ts
@@ -3671,11 +3671,11 @@ async function cmdMigrate(parsed) {
3671
3671
  }
3672
3672
 
3673
3673
  // src/commands/doctor.ts
3674
- import { resolve as resolve9 } from "path";
3674
+ import { resolve as resolve10 } from "path";
3675
3675
 
3676
3676
  // src/lib/kit/doctor-kit.ts
3677
- import { existsSync as existsSync14 } from "fs";
3678
- import { join as join16, resolve as resolve6 } from "path";
3677
+ import { existsSync as existsSync15 } from "fs";
3678
+ import { join as join17, resolve as resolve7 } from "path";
3679
3679
 
3680
3680
  // src/lib/projectKind.ts
3681
3681
  function detectProjectKind(cwd) {
@@ -3706,8 +3706,37 @@ function isElcrmLibPackage(cwd) {
3706
3706
  }
3707
3707
 
3708
3708
  // src/lib/kit/paths.ts
3709
- import { existsSync as existsSync12, readFileSync as readFileSync19 } from "fs";
3709
+ import { existsSync as existsSync13, readFileSync as readFileSync20 } from "fs";
3710
+ import { join as join13, resolve as resolve4 } from "path";
3711
+
3712
+ // src/lib/kit/config.ts
3713
+ import { existsSync as existsSync12, mkdirSync as mkdirSync2, readFileSync as readFileSync19, writeFileSync as writeFileSync6 } from "fs";
3714
+ import { homedir } from "os";
3710
3715
  import { join as join12, resolve as resolve3 } from "path";
3716
+ var FILE = "kit-paths.json";
3717
+ function globalConfigPath() {
3718
+ return join12(homedir(), ".elcrm", FILE);
3719
+ }
3720
+ function localConfigPath(cwd) {
3721
+ return join12(resolve3(cwd), ".elcrm", FILE);
3722
+ }
3723
+ function readJson(path) {
3724
+ if (!existsSync12(path))
3725
+ return {};
3726
+ try {
3727
+ return JSON.parse(readFileSync19(path, "utf8"));
3728
+ } catch {
3729
+ return {};
3730
+ }
3731
+ }
3732
+ function loadKitPathsConfig(cwd) {
3733
+ const global = readJson(globalConfigPath());
3734
+ const local = readJson(localConfigPath(cwd));
3735
+ return {
3736
+ apiDir: local.apiDir || global.apiDir,
3737
+ webDir: local.webDir || global.webDir
3738
+ };
3739
+ }
3711
3740
  function readName(dir) {
3712
3741
  try {
3713
3742
  const j = JSON.parse(readFileSync19(join12(dir, "package.json"), "utf8"));
@@ -3716,36 +3745,109 @@ function readName(dir) {
3716
3745
  return null;
3717
3746
  }
3718
3747
  }
3719
- function findEluiApiDir(from) {
3748
+ function validateApiDir(dir) {
3749
+ const p = resolve3(dir);
3750
+ if (!existsSync12(join12(p, "src/kit/catalog.ts")))
3751
+ return null;
3752
+ return p;
3753
+ }
3754
+ function validateWebDir(dir) {
3755
+ const p = resolve3(dir);
3756
+ if (readName(p) !== "elui-web")
3757
+ return null;
3758
+ return p;
3759
+ }
3760
+ function saveKitPathsConfig(cwd, patch, global = false) {
3761
+ const path = global ? globalConfigPath() : localConfigPath(cwd);
3762
+ const prev = readJson(path);
3763
+ const next = { ...prev, ...patch };
3764
+ if (next.apiDir) {
3765
+ const ok = validateApiDir(next.apiDir);
3766
+ if (!ok)
3767
+ throw new Error("apiDir: \u043D\u0443\u0436\u0435\u043D \u043A\u0430\u0442\u0430\u043B\u043E\u0433 elUI.api (src/kit/catalog.ts)");
3768
+ next.apiDir = ok;
3769
+ }
3770
+ if (next.webDir) {
3771
+ const ok = validateWebDir(next.webDir);
3772
+ if (!ok)
3773
+ throw new Error("webDir: \u043D\u0443\u0436\u0435\u043D \u043A\u0430\u0442\u0430\u043B\u043E\u0433 elUI.web (package name elui-web)");
3774
+ next.webDir = ok;
3775
+ }
3776
+ mkdirSync2(join12(path, ".."), { recursive: true });
3777
+ writeFileSync6(path, JSON.stringify(next, null, 2) + `
3778
+ `, "utf8");
3779
+ return next;
3780
+ }
3781
+ function persistKitPathsFromOpts(opts) {
3782
+ if (opts.dry)
3783
+ return;
3784
+ const patch = {};
3785
+ if (opts.apiDir?.trim())
3786
+ patch.apiDir = opts.apiDir.trim();
3787
+ if (opts.webDir?.trim())
3788
+ patch.webDir = opts.webDir.trim();
3789
+ if (!patch.apiDir && !patch.webDir)
3790
+ return;
3791
+ saveKitPathsConfig(opts.cwd, patch, opts.global ?? true);
3792
+ }
3793
+
3794
+ // src/lib/kit/paths.ts
3795
+ function readName2(dir) {
3796
+ try {
3797
+ const j = JSON.parse(readFileSync20(join13(dir, "package.json"), "utf8"));
3798
+ return j.name ?? null;
3799
+ } catch {
3800
+ return null;
3801
+ }
3802
+ }
3803
+ function findEluiApiDir(from, override) {
3804
+ if (override?.trim()) {
3805
+ const p = resolve4(override.trim());
3806
+ if (existsSync13(join13(p, "src/kit/catalog.ts")))
3807
+ return p;
3808
+ return null;
3809
+ }
3720
3810
  const env = process.env.ELUI_API_DIR?.trim();
3721
- if (env && existsSync12(join12(env, "src/kit/catalog.ts")))
3722
- return resolve3(env);
3723
- let dir = resolve3(from);
3811
+ if (env && existsSync13(join13(env, "src/kit/catalog.ts")))
3812
+ return resolve4(env);
3813
+ const saved = loadKitPathsConfig(from).apiDir;
3814
+ if (saved && existsSync13(join13(saved, "src/kit/catalog.ts")))
3815
+ return resolve4(saved);
3816
+ let dir = resolve4(from);
3724
3817
  for (let i = 0;i < 8; i++) {
3725
3818
  for (const name of ["elUI.api", "ui-kit.api"]) {
3726
- const candidate = join12(dir, name);
3727
- if (existsSync12(join12(candidate, "src/kit/catalog.ts")))
3819
+ const candidate = join13(dir, name);
3820
+ if (existsSync13(join13(candidate, "src/kit/catalog.ts")))
3728
3821
  return candidate;
3729
3822
  }
3730
- const parent = join12(dir, "..");
3823
+ const parent = join13(dir, "..");
3731
3824
  if (parent === dir)
3732
3825
  break;
3733
3826
  dir = parent;
3734
3827
  }
3735
3828
  return null;
3736
3829
  }
3737
- function findEluiWebDir(from) {
3830
+ function findEluiWebDir(from, override) {
3831
+ if (override?.trim()) {
3832
+ const p = resolve4(override.trim());
3833
+ if (readName2(p) === "elui-web")
3834
+ return p;
3835
+ return null;
3836
+ }
3738
3837
  const env = process.env.ELUI_WEB_DIR?.trim();
3739
- if (env && readName(env) === "elui-web")
3740
- return resolve3(env);
3741
- let dir = resolve3(from);
3838
+ if (env && readName2(env) === "elui-web")
3839
+ return resolve4(env);
3840
+ const saved = loadKitPathsConfig(from).webDir;
3841
+ if (saved && readName2(saved) === "elui-web")
3842
+ return resolve4(saved);
3843
+ let dir = resolve4(from);
3742
3844
  for (let i = 0;i < 8; i++) {
3743
3845
  for (const name of ["elUI.web", "Uikit.web"]) {
3744
- const candidate = join12(dir, name);
3745
- if (readName(candidate) === "elui-web")
3846
+ const candidate = join13(dir, name);
3847
+ if (readName2(candidate) === "elui-web")
3746
3848
  return candidate;
3747
3849
  }
3748
- dir = join12(dir, "..");
3850
+ dir = join13(dir, "..");
3749
3851
  }
3750
3852
  return null;
3751
3853
  }
@@ -3761,17 +3863,17 @@ function manifestFileName(npmName) {
3761
3863
  }
3762
3864
 
3763
3865
  // src/lib/kit/sync.ts
3764
- import { existsSync as existsSync13, mkdirSync as mkdirSync3, readFileSync as readFileSync22, writeFileSync as writeFileSync7 } from "fs";
3765
- import { join as join15, resolve as resolve5 } from "path";
3866
+ import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync23, writeFileSync as writeFileSync8 } from "fs";
3867
+ import { join as join16, resolve as resolve6 } from "path";
3766
3868
  import { spawnSync as spawnSync2 } from "child_process";
3767
3869
 
3768
3870
  // src/lib/kit/emit.ts
3769
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync21, writeFileSync as writeFileSync6 } from "fs";
3770
- import { join as join14, resolve as resolve4 } from "path";
3871
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync22, writeFileSync as writeFileSync7 } from "fs";
3872
+ import { join as join15, resolve as resolve5 } from "path";
3771
3873
 
3772
3874
  // src/lib/kit/parse-dts.ts
3773
- import { readFileSync as readFileSync20, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
3774
- import { basename, join as join13 } from "path";
3875
+ import { readFileSync as readFileSync21, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
3876
+ import { basename, join as join14 } from "path";
3775
3877
  var SKIP_SLUGS = new Set([
3776
3878
  "index",
3777
3879
  "types",
@@ -3797,16 +3899,23 @@ function isMainComponentDoc(doc) {
3797
3899
  return false;
3798
3900
  return doc.length > 72;
3799
3901
  }
3800
- function componentDoc(content, slug) {
3902
+ function componentDocs(content, slug) {
3801
3903
  const fnAnchor = "export declare function " + slug;
3802
3904
  const propsAnchor = "export type " + slug + "Props";
3803
3905
  const fnDoc = leadingDoc(content, fnAnchor);
3804
3906
  const propsDoc = leadingDoc(content, propsAnchor);
3805
- if (isMainComponentDoc(fnDoc))
3806
- return fnDoc;
3807
- if (isMainComponentDoc(propsDoc))
3808
- return propsDoc;
3809
- return fnDoc || propsDoc;
3907
+ const examplesDoc = fnDoc.includes("@example") ? fnDoc : propsDoc.includes("@example") ? propsDoc : fnDoc || propsDoc;
3908
+ let descriptionDoc = fnDoc || propsDoc;
3909
+ if (propsDoc.includes("@example")) {
3910
+ descriptionDoc = propsDoc;
3911
+ } else if (isMainComponentDoc(propsDoc) && propsDoc.length > fnDoc.length) {
3912
+ descriptionDoc = propsDoc;
3913
+ } else if (isMainComponentDoc(fnDoc)) {
3914
+ descriptionDoc = fnDoc;
3915
+ } else if (isMainComponentDoc(propsDoc)) {
3916
+ descriptionDoc = propsDoc;
3917
+ }
3918
+ return { descriptionDoc, examplesDoc };
3810
3919
  }
3811
3920
  function leadingDoc(content, anchor) {
3812
3921
  const idx = content.indexOf(anchor);
@@ -3874,15 +3983,15 @@ function parseComponentDts(content, slug) {
3874
3983
  if (!new RegExp("export declare function " + slug + "\\b").test(content)) {
3875
3984
  return null;
3876
3985
  }
3877
- const doc = componentDoc(content, slug);
3986
+ const { descriptionDoc, examplesDoc } = componentDocs(content, slug);
3878
3987
  const propsMatch = content.match(new RegExp("export type " + slug + "Props\\s*=\\s*([\\s\\S]*?);\\s*(?:export|$)"));
3879
3988
  const propsBlock = propsMatch?.[1] ?? "";
3880
3989
  return {
3881
3990
  slug,
3882
3991
  title: slug,
3883
- description: descriptionFromDoc(doc) || slug,
3992
+ description: descriptionFromDoc(descriptionDoc) || slug,
3884
3993
  importFrom: "@elcrm/components",
3885
- examples: examplesFromDoc(doc),
3994
+ examples: examplesFromDoc(examplesDoc),
3886
3995
  props: propsFromPropsBlock(propsBlock)
3887
3996
  };
3888
3997
  }
@@ -3894,10 +4003,10 @@ function scanDistComponents(distDir, importFrom) {
3894
4003
  const slug = basename(name, ".d.ts");
3895
4004
  if (SKIP_SLUGS.has(slug) || slug.includes("."))
3896
4005
  continue;
3897
- const path = join13(distDir, name);
4006
+ const path = join14(distDir, name);
3898
4007
  if (!statSync8(path).isFile())
3899
4008
  continue;
3900
- const content = readFileSync20(path, "utf8");
4009
+ const content = readFileSync21(path, "utf8");
3901
4010
  const parsed = parseComponentDts(content, slug);
3902
4011
  if (!parsed)
3903
4012
  continue;
@@ -3910,7 +4019,7 @@ function scanDistComponents(distDir, importFrom) {
3910
4019
 
3911
4020
  // src/lib/kit/emit.ts
3912
4021
  function readPkg(cwd) {
3913
- const j = JSON.parse(readFileSync21(join14(cwd, "package.json"), "utf8"));
4022
+ const j = JSON.parse(readFileSync22(join15(cwd, "package.json"), "utf8"));
3914
4023
  if (!j.name?.startsWith("@elcrm/")) {
3915
4024
  throw new Error("elcrm kit emit \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 npm-\u043F\u0430\u043A\u0435\u0442\u0435 @elcrm/*");
3916
4025
  }
@@ -3922,7 +4031,7 @@ function emitKitManifest(cwd) {
3922
4031
  if (!pkgSlug) {
3923
4032
  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)`);
3924
4033
  }
3925
- const distDir = join14(cwd, "dist");
4034
+ const distDir = join15(cwd, "dist");
3926
4035
  const components = scanDistComponents(distDir, pkg.name).map((c2) => ({
3927
4036
  ...c2,
3928
4037
  importFrom: pkg.name
@@ -3939,8 +4048,8 @@ function emitKitManifest(cwd) {
3939
4048
  function writeKitManifest(cwd, manifest, dry = false) {
3940
4049
  const written = [];
3941
4050
  const targets = [
3942
- join14(cwd, ".elcrm", "kit-manifest.json"),
3943
- join14(cwd, "dist", "kit-manifest.json")
4051
+ join15(cwd, ".elcrm", "kit-manifest.json"),
4052
+ join15(cwd, "dist", "kit-manifest.json")
3944
4053
  ];
3945
4054
  const body = `${JSON.stringify(manifest, null, 2)}
3946
4055
  `;
@@ -3949,20 +4058,20 @@ function writeKitManifest(cwd, manifest, dry = false) {
3949
4058
  written.push(path);
3950
4059
  continue;
3951
4060
  }
3952
- mkdirSync2(join14(path, ".."), { recursive: true });
3953
- writeFileSync6(path, body, "utf8");
4061
+ mkdirSync3(join15(path, ".."), { recursive: true });
4062
+ writeFileSync7(path, body, "utf8");
3954
4063
  written.push(path);
3955
4064
  }
3956
4065
  return written;
3957
4066
  }
3958
4067
  function readKitManifest(path) {
3959
- return JSON.parse(readFileSync21(resolve4(path), "utf8"));
4068
+ return JSON.parse(readFileSync22(resolve5(path), "utf8"));
3960
4069
  }
3961
4070
 
3962
4071
  // src/lib/kit/sync.ts
3963
4072
  function patchPkgVersions(apiDir, npmName, version, dry) {
3964
- const path = join15(apiDir, "src/kit/pkg-versions.ts");
3965
- let src = readFileSync22(path, "utf8");
4073
+ const path = join16(apiDir, "src/kit/pkg-versions.ts");
4074
+ let src = readFileSync23(path, "utf8");
3966
4075
  const re = new RegExp(`("${npmName.replace("/", "\\/")}"\\s*:\\s*")[^"]+(")`);
3967
4076
  if (!re.test(src)) {
3968
4077
  console.warn(c.yellow(` ! FALLBACK_DEPS: \u043D\u0435\u0442 ${npmName} \u0432 pkg-versions.ts`));
@@ -3972,17 +4081,17 @@ function patchPkgVersions(apiDir, npmName, version, dry) {
3972
4081
  if (next === src)
3973
4082
  return;
3974
4083
  if (!dry)
3975
- writeFileSync7(path, next, "utf8");
4084
+ writeFileSync8(path, next, "utf8");
3976
4085
  console.log(` ${dry ? "~" : c.green("+")} pkg-versions \u2192 ${npmName} ${version}`);
3977
4086
  }
3978
4087
  function patchWebDep(webDir, npmName, version, dry) {
3979
- const path = join15(webDir, "package.json");
3980
- const j = JSON.parse(readFileSync22(path, "utf8"));
4088
+ const path = join16(webDir, "package.json");
4089
+ const j = JSON.parse(readFileSync23(path, "utf8"));
3981
4090
  if (!j.dependencies?.[npmName])
3982
4091
  return;
3983
4092
  j.dependencies[npmName] = version;
3984
4093
  if (!dry)
3985
- writeFileSync7(path, `${JSON.stringify(j, null, 2)}
4094
+ writeFileSync8(path, `${JSON.stringify(j, null, 2)}
3986
4095
  `, "utf8");
3987
4096
  console.log(` ${dry ? "~" : c.green("+")} elUI.web \u2192 ${npmName}@${version}`);
3988
4097
  }
@@ -4003,31 +4112,37 @@ function runReseed(apiDir, webDir, dry) {
4003
4112
  throw new Error("kit:reseed \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439");
4004
4113
  }
4005
4114
  async function syncKitManifest(opts) {
4006
- const cwd = resolve5(opts.cwd);
4115
+ const cwd = resolve6(opts.cwd);
4007
4116
  const dry = opts.dry ?? false;
4008
4117
  const reseed = opts.reseed !== false;
4118
+ persistKitPathsFromOpts({
4119
+ cwd,
4120
+ apiDir: opts.apiDir,
4121
+ webDir: opts.webDir,
4122
+ dry
4123
+ });
4009
4124
  let manifest;
4010
4125
  if (opts.manifestPath) {
4011
4126
  manifest = readKitManifest(opts.manifestPath);
4012
- } else if (existsSync13(join15(cwd, ".elcrm", "kit-manifest.json"))) {
4013
- manifest = readKitManifest(join15(cwd, ".elcrm", "kit-manifest.json"));
4127
+ } else if (existsSync14(join16(cwd, ".elcrm", "kit-manifest.json"))) {
4128
+ manifest = readKitManifest(join16(cwd, ".elcrm", "kit-manifest.json"));
4014
4129
  } else {
4015
4130
  manifest = emitKitManifest(cwd);
4016
4131
  }
4017
- const apiDir = opts.apiDir ? resolve5(opts.apiDir) : findEluiApiDir(cwd);
4132
+ const apiDir = findEluiApiDir(cwd, opts.apiDir);
4018
4133
  if (!apiDir) {
4019
- throw new Error("\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D elUI.api (\u0437\u0430\u0434\u0430\u0439\u0442\u0435 --api=\u2026 \u0438\u043B\u0438 ELUI_API_DIR). Manifest \u043C\u043E\u0436\u043D\u043E \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C: elcrm kit emit");
4134
+ throw new Error("\u041D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D elUI.api (elcrm kit config --api=\u2026 --global \u0438\u043B\u0438 ELUI_API_DIR). Manifest: elcrm kit emit");
4020
4135
  }
4021
- const outDir = join15(apiDir, "src/kit/manifests");
4022
- const outFile = join15(outDir, manifestFileName(manifest.package));
4136
+ const outDir = join16(apiDir, "src/kit/manifests");
4137
+ const outFile = join16(outDir, manifestFileName(manifest.package));
4023
4138
  if (!dry) {
4024
- mkdirSync3(outDir, { recursive: true });
4025
- writeFileSync7(outFile, `${JSON.stringify(manifest, null, 2)}
4139
+ mkdirSync4(outDir, { recursive: true });
4140
+ writeFileSync8(outFile, `${JSON.stringify(manifest, null, 2)}
4026
4141
  `, "utf8");
4027
4142
  }
4028
4143
  console.log(`${dry ? "[dry] " : ""}sync \u2192 ${outFile} (${manifest.components.length} \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432, ${manifest.version})`);
4029
4144
  patchPkgVersions(apiDir, manifest.package, manifest.version, dry);
4030
- const webDir = opts.webDir ? resolve5(opts.webDir) : opts.patchWeb !== false ? findEluiWebDir(cwd) : null;
4145
+ const webDir = opts.patchWeb === false ? null : findEluiWebDir(cwd, opts.webDir);
4031
4146
  if (webDir)
4032
4147
  patchWebDep(webDir, manifest.package, manifest.version, dry);
4033
4148
  if (reseed)
@@ -4042,14 +4157,14 @@ async function emitAndSync(opts) {
4042
4157
 
4043
4158
  // src/lib/kit/doctor-kit.ts
4044
4159
  async function doctorKitSync(opts) {
4045
- const cwd = resolve6(opts.cwd);
4160
+ const cwd = resolve7(opts.cwd);
4046
4161
  const dry = opts.dry ?? false;
4047
4162
  if (!isElcrmLibPackage(cwd))
4048
4163
  return false;
4049
4164
  const pkg = readPkgAt(cwd);
4050
4165
  if (!pkg?.name || !kitPkgSlug(pkg.name))
4051
4166
  return false;
4052
- if (!existsSync14(join16(cwd, "dist"))) {
4167
+ if (!existsSync15(join17(cwd, "dist"))) {
4053
4168
  console.log(c.dim(`
4054
4169
  kit: \u043F\u0440\u043E\u043F\u0443\u0441\u043A \u2014 \u043D\u0435\u0442 dist/ (\u0441\u043D\u0430\u0447\u0430\u043B\u0430 npm run build)`));
4055
4170
  return false;
@@ -4061,13 +4176,13 @@ ${dry ? "[dry] " : ""}kit: sync \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442
4061
4176
  return true;
4062
4177
  } catch (err) {
4063
4178
  console.warn(c.yellow("kit: " + (err instanceof Error ? err.message : String(err))));
4064
- console.warn(c.dim(" \u0417\u0430\u0434\u0430\u0439\u0442\u0435 ELUI_API_DIR=\u2026 \u0438\u043B\u0438 elcrm kit pull --api=\u2026/elUI.api"));
4179
+ console.warn(c.dim(" \u0417\u0430\u0434\u0430\u0439\u0442\u0435 elcrm kit config --api=\u2026 --web=\u2026 --global"));
4065
4180
  return false;
4066
4181
  }
4067
4182
  }
4068
4183
 
4069
4184
  // src/commands/docs.ts
4070
- import { join as join18, resolve as resolve7 } from "path";
4185
+ import { join as join19, resolve as resolve8 } from "path";
4071
4186
 
4072
4187
  // src/lib/elcrmDocsCatalog.ts
4073
4188
  var ALL_ELCRM_DOCS = [
@@ -4101,33 +4216,33 @@ function docsForProject(kind, lib) {
4101
4216
 
4102
4217
  // src/lib/scaffoldFiles.ts
4103
4218
  import {
4104
- existsSync as existsSync15,
4105
- mkdirSync as mkdirSync4,
4219
+ existsSync as existsSync16,
4220
+ mkdirSync as mkdirSync5,
4106
4221
  readdirSync as readdirSync9,
4107
- readFileSync as readFileSync23,
4108
- writeFileSync as writeFileSync8
4222
+ readFileSync as readFileSync24,
4223
+ writeFileSync as writeFileSync9
4109
4224
  } from "fs";
4110
- import { dirname as dirname3, join as join17 } from "path";
4225
+ import { dirname as dirname3, join as join18 } from "path";
4111
4226
  function writeScaffoldFile(dest, body, opts) {
4112
4227
  if (opts.dry)
4113
4228
  return "dry";
4114
- if (!opts.force && existsSync15(dest))
4229
+ if (!opts.force && existsSync16(dest))
4115
4230
  return "skip";
4116
- mkdirSync4(dirname3(dest), { recursive: true });
4117
- writeFileSync8(dest, body, "utf8");
4231
+ mkdirSync5(dirname3(dest), { recursive: true });
4232
+ writeFileSync9(dest, body, "utf8");
4118
4233
  return "write";
4119
4234
  }
4120
4235
  function readBundled(relPath) {
4121
- const full = join17(packageRoot(), "templates", relPath);
4122
- if (!existsSync15(full)) {
4236
+ const full = join18(packageRoot(), "templates", relPath);
4237
+ if (!existsSync16(full)) {
4123
4238
  throw new Error(`\u0428\u0430\u0431\u043B\u043E\u043D \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D: ${relPath}`);
4124
4239
  }
4125
- return readFileSync23(full, "utf8");
4240
+ return readFileSync24(full, "utf8");
4126
4241
  }
4127
4242
 
4128
4243
  // src/commands/docs.ts
4129
4244
  async function cmdDocs(parsed) {
4130
- const cwd = resolve7(parsed.options.dir || process.cwd());
4245
+ const cwd = resolve8(parsed.options.dir || process.cwd());
4131
4246
  const dry = hasFlag(parsed, "dry");
4132
4247
  const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
4133
4248
  const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
@@ -4135,8 +4250,8 @@ async function cmdDocs(parsed) {
4135
4250
  const names = docsForProject(kind, lib);
4136
4251
  console.log(`${dry ? "[dry] " : ""}elcrm docs \u2192 ${cwd}/docs (${kind}${lib ? ", lib" : ""})`);
4137
4252
  for (const name of names) {
4138
- const body = readBundled(join18("elcrm-docs", name));
4139
- const dest = join18(cwd, "docs", name);
4253
+ const body = readBundled(join19("elcrm-docs", name));
4254
+ const dest = join19(cwd, "docs", name);
4140
4255
  const action = writeScaffoldFile(dest, body, { dry, force });
4141
4256
  const mark = action === "write" ? c.green("+") : action === "skip" ? c.gray("=") : c.cyan("~");
4142
4257
  console.log(` ${mark} docs/${name}`);
@@ -4148,7 +4263,7 @@ async function cmdDocs(parsed) {
4148
4263
  }
4149
4264
 
4150
4265
  // src/commands/cursor.ts
4151
- import { join as join19, resolve as resolve8 } from "path";
4266
+ import { join as join20, resolve as resolve9 } from "path";
4152
4267
  var ALWAYS = ["elcrm.mdc", "elcrm-packages.mdc", "elcrm-jsdoc.mdc"];
4153
4268
  var UI = [
4154
4269
  "elcrm-ui.mdc",
@@ -4164,7 +4279,7 @@ function writeOne(dest, body, dry, force, label) {
4164
4279
  console.log(` ${mark} ${label}`);
4165
4280
  }
4166
4281
  async function cmdCursor(parsed) {
4167
- const cwd = resolve8(parsed.options.dir || process.cwd());
4282
+ const cwd = resolve9(parsed.options.dir || process.cwd());
4168
4283
  const dry = hasFlag(parsed, "dry");
4169
4284
  const force = hasFlag(parsed, "force") || !hasFlag(parsed, "keep");
4170
4285
  const lib = hasFlag(parsed, "lib") || isElcrmLibPackage(cwd);
@@ -4177,24 +4292,24 @@ async function cmdCursor(parsed) {
4177
4292
  ];
4178
4293
  console.log(`${dry ? "[dry] " : ""}elcrm cursor \u2192 ${cwd}/.cursor (${kind}${lib ? ", lib" : ""})`);
4179
4294
  for (const name of ruleNames) {
4180
- let body = readBundled(join19("elcrm-cursor", "rules", name));
4295
+ let body = readBundled(join20("elcrm-cursor", "rules", name));
4181
4296
  if (kind === "server" && (name === "elcrm-server.mdc" || name === "elcrm-rpc.mdc")) {
4182
4297
  body = body.replace("alwaysApply: false", "alwaysApply: true").replace(/^globs:.*\n/m, "");
4183
4298
  }
4184
- writeOne(join19(cwd, ".cursor", "rules", name), body, dry, force, `.cursor/rules/${name}`);
4299
+ writeOne(join20(cwd, ".cursor", "rules", name), body, dry, force, `.cursor/rules/${name}`);
4185
4300
  }
4186
4301
  const docs = docsForProject(kind, lib);
4187
4302
  for (const name of docs) {
4188
- const body = readBundled(join19("elcrm-docs", name));
4189
- writeOne(join19(cwd, ".cursor", "docs", name), body, dry, force, `.cursor/docs/${name}`);
4303
+ const body = readBundled(join20("elcrm-docs", name));
4304
+ writeOne(join20(cwd, ".cursor", "docs", name), body, dry, force, `.cursor/docs/${name}`);
4190
4305
  }
4191
- writeOne(join19(cwd, "AGENTS.md"), readBundled(join19("elcrm-cursor", "AGENTS.md")), dry, force, "AGENTS.md");
4306
+ writeOne(join20(cwd, "AGENTS.md"), readBundled(join20("elcrm-cursor", "AGENTS.md")), dry, force, "AGENTS.md");
4192
4307
  console.log(c.gray("\u0410\u0433\u0435\u043D\u0442: AGENTS.md + rules (\u0443\u0437\u043A\u0438\u0435 glob) + .cursor/docs. \u0421\u0434\u0430\u0447\u0430: elcrm test."));
4193
4308
  }
4194
4309
 
4195
4310
  // src/commands/doctor.ts
4196
4311
  async function cmdDoctor(parsed) {
4197
- const cwd = resolve9(parsed.options.dir || process.cwd());
4312
+ const cwd = resolve10(parsed.options.dir || process.cwd());
4198
4313
  const dry = hasFlag(parsed, "dry");
4199
4314
  const doDocs = hasFlag(parsed, "docs");
4200
4315
  const doTest = hasFlag(parsed, "test");
@@ -4271,7 +4386,7 @@ doctor: \u0433\u043E\u0442\u043E\u0432\u043E (${n} \u043F\u0440\u0430\u0432\u043
4271
4386
  }
4272
4387
 
4273
4388
  // src/commands/build.ts
4274
- import { existsSync as existsSync16 } from "fs";
4389
+ import { existsSync as existsSync17 } from "fs";
4275
4390
 
4276
4391
  // src/lib/nextVersion.ts
4277
4392
  async function nextVersion() {
@@ -4294,9 +4409,9 @@ async function nextVersion() {
4294
4409
 
4295
4410
  // src/commands/build.ts
4296
4411
  function resolveEntrypoint() {
4297
- if (existsSync16("./src/index.js"))
4412
+ if (existsSync17("./src/index.js"))
4298
4413
  return "./src/index.js";
4299
- if (existsSync16("./src/index.ts"))
4414
+ if (existsSync17("./src/index.ts"))
4300
4415
  return "./src/index.ts";
4301
4416
  return "./src/index.js";
4302
4417
  }
@@ -4374,22 +4489,22 @@ async function cmdUuid() {
4374
4489
  }
4375
4490
 
4376
4491
  // src/commands/postbuild.ts
4377
- import { resolve as resolve10 } from "path";
4492
+ import { resolve as resolve11 } from "path";
4378
4493
 
4379
4494
  // src/vite/postbuild.ts
4380
4495
  import {
4381
- existsSync as existsSync17,
4496
+ existsSync as existsSync18,
4382
4497
  readdirSync as readdirSync11,
4383
- readFileSync as readFileSync25,
4498
+ readFileSync as readFileSync26,
4384
4499
  rmSync,
4385
4500
  statSync as statSync9,
4386
- writeFileSync as writeFileSync10
4501
+ writeFileSync as writeFileSync11
4387
4502
  } from "fs";
4388
- import { join as join21 } from "path";
4503
+ import { join as join22 } from "path";
4389
4504
 
4390
4505
  // src/vite/concat-css.ts
4391
- import { readdirSync as readdirSync10, readFileSync as readFileSync24, writeFileSync as writeFileSync9 } from "fs";
4392
- import { join as join20 } from "path";
4506
+ import { readdirSync as readdirSync10, readFileSync as readFileSync25, writeFileSync as writeFileSync10 } from "fs";
4507
+ import { join as join21 } from "path";
4393
4508
  var SKIP_CSS = new Set([
4394
4509
  "index.css",
4395
4510
  "tokens.css",
@@ -4405,10 +4520,10 @@ function concatDistCss(options) {
4405
4520
  ...options.skip ?? []
4406
4521
  ]);
4407
4522
  const files = readdirSync10(options.distDir).filter((f) => f.endsWith(".css") && !skip.has(f)).sort();
4408
- const css = files.map((f) => readFileSync24(join20(options.distDir, f), "utf8")).join(`
4523
+ const css = files.map((f) => readFileSync25(join21(options.distDir, f), "utf8")).join(`
4409
4524
  `);
4410
- const outPath = join20(options.distDir, outFile);
4411
- writeFileSync9(outPath, css);
4525
+ const outPath = join21(options.distDir, outFile);
4526
+ writeFileSync10(outPath, css);
4412
4527
  console.log(`[concat-css] ${files.length} \u0444\u0430\u0439\u043B\u043E\u0432 \u2192 ${outFile}`);
4413
4528
  return outPath;
4414
4529
  }
@@ -4426,18 +4541,18 @@ function postbuildLib(options) {
4426
4541
  outFile: options.cssOutFile ?? "index.css"
4427
4542
  });
4428
4543
  for (const name of readdirSync11(dist)) {
4429
- const dir = join21(dist, name);
4544
+ const dir = join22(dist, name);
4430
4545
  if (!statSync9(dir).isDirectory())
4431
4546
  continue;
4432
4547
  if (keep.has(name))
4433
4548
  continue;
4434
- const impl = join21(dir, `${name}.d.ts`);
4435
- const nestedIndex = join21(dir, "index.d.ts");
4436
- const flat = join21(dist, `${name}.d.ts`);
4437
- const src = existsSync17(impl) ? impl : existsSync17(nestedIndex) ? nestedIndex : null;
4549
+ const impl = join22(dir, `${name}.d.ts`);
4550
+ const nestedIndex = join22(dir, "index.d.ts");
4551
+ const flat = join22(dist, `${name}.d.ts`);
4552
+ const src = existsSync18(impl) ? impl : existsSync18(nestedIndex) ? nestedIndex : null;
4438
4553
  if (src) {
4439
- const text = readFileSync25(src, "utf8").replace(/from ['"]\.\.\/([^'"]+)['"]/g, `from './$1'`);
4440
- writeFileSync10(flat, text);
4554
+ const text = readFileSync26(src, "utf8").replace(/from ['"]\.\.\/([^'"]+)['"]/g, `from './$1'`);
4555
+ writeFileSync11(flat, text);
4441
4556
  console.log(`[postbuild] types \u2192 ${name}.d.ts`);
4442
4557
  }
4443
4558
  rmSync(dir, { recursive: true, force: true });
@@ -4448,7 +4563,7 @@ function postbuildLib(options) {
4448
4563
  // src/commands/postbuild.ts
4449
4564
  async function cmdPostbuild(parsed) {
4450
4565
  const dir = parsed.options.dir || "./dist";
4451
- const distDir = resolve10(process.cwd(), dir);
4566
+ const distDir = resolve11(process.cwd(), dir);
4452
4567
  postbuildLib({ distDir });
4453
4568
  console.log(`[postbuild] \u0433\u043E\u0442\u043E\u0432\u043E: ${distDir}`);
4454
4569
  }
@@ -4516,8 +4631,8 @@ css-sanitize: \u043F\u043E\u0447\u0438\u043D\u0435\u043D\u043E ${cleaned.length}
4516
4631
  }
4517
4632
 
4518
4633
  // src/commands/audit.ts
4519
- import { existsSync as existsSync18, readFileSync as readFileSync26 } from "fs";
4520
- import { join as join22, relative as relative4 } from "path";
4634
+ import { existsSync as existsSync19, readFileSync as readFileSync27 } from "fs";
4635
+ import { join as join23, relative as relative4 } from "path";
4521
4636
  function scan(cwd) {
4522
4637
  const out = [];
4523
4638
  const pkg = readPackageJson(cwd);
@@ -4548,7 +4663,7 @@ function scan(cwd) {
4548
4663
  }
4549
4664
  const files = projectCodeRoots(cwd).flatMap((r) => walkCodeFiles(r));
4550
4665
  for (const file of files) {
4551
- const text = readFileSync26(file, "utf8");
4666
+ const text = readFileSync27(file, "utf8");
4552
4667
  const rel2 = relative4(cwd, file);
4553
4668
  const lines = text.split(/\r?\n/);
4554
4669
  lines.forEach((line, i) => {
@@ -4650,7 +4765,7 @@ function scan(cwd) {
4650
4765
  return false;
4651
4766
  if (/theme(-light|-dark)?\.css$/.test(f))
4652
4767
  return false;
4653
- const t = readFileSync26(f, "utf8");
4768
+ const t = readFileSync27(f, "utf8");
4654
4769
  return t.includes("--field-border:") && t.includes("--button-secondary-bg:") && t.includes("transparent");
4655
4770
  });
4656
4771
  if (toolbarMarkers.length > 1) {
@@ -4661,8 +4776,8 @@ function scan(cwd) {
4661
4776
  fix: "\u043E\u0431\u0449\u0438\u0439 \u043A\u043B\u0430\u0441\u0441 + theme tokens"
4662
4777
  });
4663
4778
  }
4664
- const styleDir = ["src/style", "src/styles", "style"].map((d) => join22(cwd, d)).find(existsSync18);
4665
- if (styleDir && !existsSync18(join22(styleDir, "elcrm.css"))) {
4779
+ const styleDir = ["src/style", "src/styles", "style"].map((d) => join23(cwd, d)).find(existsSync19);
4780
+ if (styleDir && !existsSync19(join23(styleDir, "elcrm.css"))) {
4666
4781
  out.push({
4667
4782
  level: "info",
4668
4783
  code: "elcrm-css",
@@ -4670,8 +4785,8 @@ function scan(cwd) {
4670
4785
  fix: "elcrm css"
4671
4786
  });
4672
4787
  }
4673
- if (styleDir && existsSync18(join22(styleDir, "elcrm.css"))) {
4674
- const elcrmCss = readFileSync26(join22(styleDir, "elcrm.css"), "utf8");
4788
+ if (styleDir && existsSync19(join23(styleDir, "elcrm.css"))) {
4789
+ const elcrmCss = readFileSync27(join23(styleDir, "elcrm.css"), "utf8");
4675
4790
  for (const m of elcrmCss.matchAll(/@import\s+["'](@elcrm\/[^"']+)["']/g)) {
4676
4791
  const spec = m[1];
4677
4792
  if (!resolveElcrmCssImport(spec, cwd)) {
@@ -4679,7 +4794,7 @@ function scan(cwd) {
4679
4794
  level: "error",
4680
4795
  code: "elcrm-import-missing",
4681
4796
  message: `\u0431\u0438\u0442\u044B\u0439 @import "${spec}" \u2014 \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442 \u0432 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u043E\u043C \u043F\u0430\u043A\u0435\u0442\u0435`,
4682
- file: relative4(cwd, join22(styleDir, "elcrm.css")),
4797
+ file: relative4(cwd, join23(styleDir, "elcrm.css")),
4683
4798
  fix: "elcrm css"
4684
4799
  });
4685
4800
  }
@@ -4691,8 +4806,8 @@ function scan(cwd) {
4691
4806
  "theme-light.css",
4692
4807
  "theme-dark.css",
4693
4808
  "theme.css"
4694
- ].map((f) => join22(styleDir, f));
4695
- const themeText = themeFiles.filter(existsSync18).map((f) => readFileSync26(f, "utf8")).join(`
4809
+ ].map((f) => join23(styleDir, f));
4810
+ const themeText = themeFiles.filter(existsSync19).map((f) => readFileSync27(f, "utf8")).join(`
4696
4811
  `);
4697
4812
  if (themeText && !/--popup-shadow\s*:/.test(themeText) && /--date-popup-shadow\s*:|--select-background\s*:/.test(themeText)) {
4698
4813
  out.push({
@@ -4714,7 +4829,7 @@ function scan(cwd) {
4714
4829
  for (const file of files) {
4715
4830
  if (!file.endsWith(".css"))
4716
4831
  continue;
4717
- const text = readFileSync26(file, "utf8");
4832
+ const text = readFileSync27(file, "utf8");
4718
4833
  const relPath = relative4(cwd, file);
4719
4834
  const lines = text.split(/\r?\n/);
4720
4835
  let prevNonEmpty = "";
@@ -4804,7 +4919,7 @@ ${c.cyan("\u2192")} elcrm migrate front${hasFlag(parsed, "dry") ? " --dry" : ""}
4804
4919
  }
4805
4920
 
4806
4921
  // src/commands/kit.ts
4807
- import { resolve as resolve11 } from "path";
4922
+ import { resolve as resolve12, join as join24 } from "path";
4808
4923
  function showKitHelp() {
4809
4924
  console.log(`elcrm kit \u2014 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\u0446\u0438\u044F elUI (UIKit)
4810
4925
 
@@ -4819,7 +4934,13 @@ function showKitHelp() {
4819
4934
  pull [--api=\u2026] [--no-reseed] [--dry]
4820
4935
  emit + sync (\u0443\u0434\u043E\u0431\u043D\u043E \u0432 postbuild)
4821
4936
 
4822
- \u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435: ELUI_API_DIR, ELUI_WEB_DIR
4937
+ config [--api=\u2026] [--web=\u2026] [--global] [--local] [--show]
4938
+ \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043F\u0443\u0442\u0438 elUI (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E ~/.elcrm/kit-paths.json)
4939
+
4940
+ \u041F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 (\u043E\u043F\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E): ELUI_API_DIR, ELUI_WEB_DIR
4941
+
4942
+ \u041E\u0434\u0438\u043D \u0440\u0430\u0437:
4943
+ elcrm kit config --api=/path/to/elUI.api --web=/path/to/elUI.web --global
4823
4944
 
4824
4945
  \u041F\u0440\u0438\u043C\u0435\u0440 (components):
4825
4946
  npm run build && elcrm kit pull
@@ -4830,7 +4951,7 @@ function showKitHelp() {
4830
4951
  }
4831
4952
  async function cmdKit(parsed) {
4832
4953
  const sub = parsed.positionals[0] || "help";
4833
- const cwd = resolve11(parsed.options.dir || process.cwd());
4954
+ const cwd = resolve12(parsed.options.dir || process.cwd());
4834
4955
  const dry = hasFlag(parsed, "dry");
4835
4956
  const noReseed = hasFlag(parsed, "no-reseed");
4836
4957
  if (sub === "help" || sub === "--help") {
@@ -4860,6 +4981,37 @@ async function cmdKit(parsed) {
4860
4981
  });
4861
4982
  return;
4862
4983
  }
4984
+ if (sub === "config") {
4985
+ const global = hasFlag(parsed, "global") || !hasFlag(parsed, "local");
4986
+ const show = hasFlag(parsed, "show") || !parsed.options.api && !parsed.options.web;
4987
+ if (show && !parsed.options.api && !parsed.options.web) {
4988
+ const saved = loadKitPathsConfig(cwd);
4989
+ const api = findEluiApiDir(cwd, parsed.options.api) ?? "(\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)";
4990
+ const web = findEluiWebDir(cwd, parsed.options.web) ?? "(\u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D)";
4991
+ console.log("elcrm kit config:");
4992
+ console.log(" \u0444\u0430\u0439\u043B:", global ? "~/.elcrm/kit-paths.json" : ".elcrm/kit-paths.json");
4993
+ console.log(" apiDir (\u0444\u0430\u0439\u043B):", saved.apiDir ?? "\u2014");
4994
+ console.log(" webDir (\u0444\u0430\u0439\u043B):", saved.webDir ?? "\u2014");
4995
+ console.log(" apiDir (resolve):", api);
4996
+ console.log(" webDir (resolve):", web);
4997
+ if (!saved.apiDir) {
4998
+ console.log(c.dim(`
4999
+ \u0417\u0430\u0434\u0430\u0442\u044C: elcrm kit config --api=\u2026 --web=\u2026 --global`));
5000
+ }
5001
+ return;
5002
+ }
5003
+ const next = saveKitPathsConfig(cwd, {
5004
+ ...parsed.options.api ? { apiDir: parsed.options.api } : {},
5005
+ ...parsed.options.web ? { webDir: parsed.options.web } : {}
5006
+ }, global);
5007
+ const where = global ? "~/.elcrm/kit-paths.json" : join24(cwd, ".elcrm/kit-paths.json");
5008
+ console.log(c.green("kit config \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2192 " + where));
5009
+ if (next.apiDir)
5010
+ console.log(" apiDir:", next.apiDir);
5011
+ if (next.webDir)
5012
+ console.log(" webDir:", next.webDir);
5013
+ return;
5014
+ }
4863
5015
  if (sub === "pull") {
4864
5016
  if (!isElcrmLibPackage(cwd)) {
4865
5017
  console.error(c.red("kit pull \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0432 npm-\u043F\u0430\u043A\u0435\u0442\u0435 @elcrm/components | @elcrm/form"));
@@ -4867,6 +5019,8 @@ async function cmdKit(parsed) {
4867
5019
  }
4868
5020
  await emitAndSync({
4869
5021
  cwd,
5022
+ apiDir: parsed.options.api,
5023
+ webDir: parsed.options.web,
4870
5024
  dry,
4871
5025
  reseed: !noReseed
4872
5026
  });
@@ -4881,11 +5035,11 @@ async function cmdKit(parsed) {
4881
5035
  function cliVersion() {
4882
5036
  const here = dirname4(fileURLToPath2(import.meta.url));
4883
5037
  for (const p of [
4884
- join23(here, "..", "package.json"),
4885
- join23(here, "package.json")
5038
+ join25(here, "..", "package.json"),
5039
+ join25(here, "package.json")
4886
5040
  ]) {
4887
5041
  try {
4888
- const v = JSON.parse(readFileSync27(p, "utf8")).version;
5042
+ const v = JSON.parse(readFileSync28(p, "utf8")).version;
4889
5043
  if (typeof v === "string" && v)
4890
5044
  return v;
4891
5045
  } catch {}
@@ -4921,7 +5075,8 @@ function showHelp() {
4921
5075
  .cursor/rules + .cursor/docs + AGENTS.md
4922
5076
  --lib: \u0447\u0435\u043A\u043B\u0438\u0441\u0442 \u0430\u0432\u0442\u043E\u0440\u0430 @elcrm/*
4923
5077
 
4924
- kit <emit|sync|pull> [--api=\u2026] [--web=\u2026] [--no-reseed] [--dry]
5078
+ kit <emit|sync|pull|config> [--api=\u2026] [--web=\u2026] [--no-reseed] [--dry]
5079
+ config \u2014 \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043F\u0443\u0442\u0438 \u0432 ~/.elcrm/kit-paths.json (--global)
4925
5080
  emit \u2014 manifest \u0438\u0437 dist/*.d.ts (@elcrm/components|form)
4926
5081
  sync \u2014 manifest \u2192 elUI.api/src/kit/manifests + reseed
4927
5082
  pull \u2014 emit + sync (postbuild \u043F\u0430\u043A\u0435\u0442\u0430)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elcrm",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
4
4
  "description": "CLI @elcrm/*: doctor --fix (порядок проекта), update, css, docs, cursor, kit, migrate",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@ Shell UI: оболочка, шапка, секции, списки, Dropdown / R
14
14
  import {
15
15
  Layout, Header, Footer, Brand, Avatar, AvatarName, AvatarGroup, Badge,
16
16
  Menu, Dropdown, Section, NavSections, TabSections, Breadcrumb, Stack,
17
- Card, Stat, ActionGroup, TextGroup, ItemColumn, List, Item, PageHead, PageShell, ChatList, ChatMessages, ChatSplit, Sidebar, Popover,
17
+ Card, Stat, ActionGroup, Toolbar, TextGroup, ItemColumn, List, Item, PageHead, PageShell, ChatList, ChatMessages, ChatSplit, Sidebar, Popover,
18
18
  Healthmap, PanelInfo,
19
19
  Loading, EmptyState, RadioGroup, PricingGroup, PricingTable,
20
20
  createLazyResolver,
@@ -37,7 +37,8 @@ import type { PricingPlan, HealthmapData } from "@elcrm/components";
37
37
  - **Stat** — метрика: `label` + `value`, `tone` как у Badge, `labelMuted`. Не собирать из `span` в приложении.
38
38
  - **Healthmap** — теплокарта дней (`data.days`). Данные грузит приложение; слоты `brandName` / `brandSub` / `unit` / `hint`. Токены `--healthmap-cell`, `--healthmap-gap`, `--healthmap-l0`…`l4`.
39
39
  - **PanelInfo** — выезд справа (шапка / body / footer). На узком экране — оверлей. Слоты: `children`, `footer`, `headerExtra`, `close`. Токены `--panel-info-width`, `--panel-info-width-min`, `--panel-info-overlay-max`, `--panel-info-backdrop`.
40
- - **ActionGroup** — тулбар: `label` + `items` (узлы) и/или `children`. Кнопки — `@elcrm/button`, не нативный `<button>`.
40
+ - **ActionGroup** — ряд иконок в карточке / строке: `label` + `items` и/или `children`. Кнопки — `@elcrm/button`.
41
+ - **Toolbar** — фильтры над списком. `size` (`s`/`m`/`l`, по умолчанию `s`) наследуют вложенные `*Field`, `Search`, `Button`, `Dropdown` (свой `size` важнее). `align`: `start` / `center` / `end` / `between`. `width`: `l` (100%, по умолчанию) или `s` (по контенту, не растягивать). Токены `--toolbar-gap`. Не `ActionGroup`.
41
42
  - **TextGroup** — две строки: `title` + `description` (без аватара; с аватаром — AvatarName).
42
43
  - **Item** — строка списка. `as` button/a/li, `active`. `variant="card"` — рамка и muted-фон (идеи / тикеты / inbox). Не дублировать `.desk-row` в приложении.
43
44
  - **ItemColumn** — внутри Item: `icon` слева, столбик `title` / `description` / `footer` или `meta`, `extra` — бейджи справа сверху, `aside` — колонка справа. `unread` — жирный title. Title: `--item-column-title-size` / `--item-column-title-line` (в console = `--button-size-s` / `--button-line-height`).
@@ -45,7 +46,7 @@ import type { PricingPlan, HealthmapData } from "@elcrm/components";
45
46
  ## Меню
46
47
 
47
48
  - **Menu** — inline-навигация в шапке. `items`: `value` + `label` + `badge` (`0` не показываем, иначе `Badge` size s). `variant="line"` — вкладки с чертой снизу (не ломать pill селекторами страницы).
48
- - **Dropdown** — выпадающее меню по `items`/`groups` (portal). `placement="auto"|"top"|"bottom"` — куда открывать панель (футер Sidebar — `top`).
49
+ - **Dropdown** — выпадающее меню по `items`/`groups` (portal). `placement="auto"|"top"|"bottom"` — куда открывать панель (футер Sidebar — `top`). `size` — высота триггера; в `Toolbar` берётся сам.
49
50
  - **Popover** — свой попап в portal (якорь + `children`). z-index: `acquireOverlayZIndex` (тот же стек, что у form). Не `@elcrm/overlay` `Menu`. `placement` как у Dropdown: позиция по **реальной** высоте панели, не по `maxHeight` 420.
50
51
 
51
52
  ## Avatar
@@ -65,6 +66,7 @@ import type { PricingPlan, HealthmapData } from "@elcrm/components";
65
66
  - `--layout-padding` / `--layout-margin` / `--layout-max-width` — отступы и макс. ширина корня Layout (`0` / `none` = на весь вьюпорт).
66
67
  - `--header-border` — рамка Header (shorthand: все стороны и толщина). Только снизу: `none` + линия через `--header-shadow`.
67
68
  - `--header-nav-justify` — выравнивание `nav` в шапке (`flex-start` / `center` / `flex-end`).
69
+ - `<Toolbar size="s" width="l" align="start">` — один размер для Search / `*Field` / Button / Dropdown. `width="s"` — ряд по контенту. Свой `size` у ребёнка важнее.
68
70
  - Узкие токены (`--chat-*`, `--sidebar-*`, `--item-column-*`) по умолчанию = `--shell-*`. Переопределяй только отличие.
69
71
  - Задать `--shell-color` / `--shell-padding` / `--shell-radius` один раз — большинство компонентов подхватят.
70
72
  - `--popup-shadow` общий с `@elcrm/form`.
@@ -23,7 +23,8 @@ const form = useForm({ login: "", password: "" });
23
23
 
24
24
  Канонические поля: `StringField`, `PasswordField`, `TextareaField`, `NumberField`, `PercentField`, `MoneyField`, `MaskField`, `PhoneField`, `EmailField`, `UrlField`, `DateField`, `TimeField`, `SelectField`, `OptionsField`, `ModalField`, `RangeField`, `CheckField`, `RadioField`, `TagsField`, `RatingField`, `CodeField`, `FileField`, `DragDropField`, `RichTextField`, `ColorField`, `HiddenField`, `DisplayField`, `SearchField`, `TabsField`.
25
25
 
26
- `disabled`, `size` (`"s"` | `"m"` | `"l"`) — у всех видимых `*Field` (кроме `HiddenField`).
26
+ `disabled`, `size` (`"s"` | `"m"` | `"l"`) — у всех видимых `*Field` (кроме `HiddenField`).
27
+ В ряду фильтров — `<Toolbar size="s">` из `@elcrm/components`: вложенные поля/Search/Button берут этот `size`.
27
28
 
28
29
  `PasswordField`: `generate` — кнопка случайного пароля. `true` → `Az09#` (A–Z, a–z, 0–9, символы). `generate="A"` только заглавные, `"59"` цифры 5–9, `"#"` только символы. Длина — `maxLength` или 12.
29
30
 
@@ -134,6 +134,7 @@
134
134
  --item-column-title-line: 1.3;
135
135
  --stat-value-size: 1.75rem;
136
136
  --action-group-gap: var(--shell-gap-sm);
137
+ --toolbar-gap: var(--shell-gap-sm);
137
138
  --sidebar-width: 260px;
138
139
  --sidebar-pad-x: var(--shell-gap);
139
140
  --sidebar-pad-y: var(--shell-gap-lg);
@@ -145,6 +145,7 @@
145
145
  --item-column-title-line: 1.3;
146
146
  --stat-value-size: 1.75rem;
147
147
  --action-group-gap: var(--shell-gap-sm);
148
+ --toolbar-gap: var(--shell-gap-sm);
148
149
  --sidebar-width: 248px;
149
150
  --sidebar-pad-x: var(--shell-gap);
150
151
  --sidebar-pad-y: var(--shell-gap-lg);