elcrm 1.0.0 → 1.1.2

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 (47) hide show
  1. package/README.md +10 -0
  2. package/dist/index.js +2828 -425
  3. package/dist/vite/discover-lib.js +4 -4
  4. package/dist/vite/plugin-css-scoped.js +2 -2
  5. package/dist/vite/postbuild.d.ts +5 -0
  6. package/dist/vite/postbuild.js +59 -0
  7. package/package.json +10 -4
  8. package/templates/elcrm-cursor/AGENTS.md +13 -0
  9. package/templates/elcrm-cursor/rules/elcrm-lib.mdc +101 -0
  10. package/templates/elcrm-cursor/rules/elcrm-module.mdc +25 -0
  11. package/templates/elcrm-cursor/rules/elcrm-packages.mdc +43 -0
  12. package/templates/elcrm-cursor/rules/elcrm-rpc.mdc +23 -0
  13. package/templates/elcrm-cursor/rules/elcrm-server.mdc +30 -0
  14. package/templates/elcrm-cursor/rules/elcrm-theme.mdc +13 -0
  15. package/templates/elcrm-cursor/rules/elcrm-ui.mdc +41 -0
  16. package/templates/elcrm-cursor/rules/elcrm.mdc +43 -0
  17. package/templates/elcrm-docs/ALERT.elCRM.md +26 -0
  18. package/templates/elcrm-docs/API.elCRM.md +38 -0
  19. package/templates/elcrm-docs/BUTTON.elCRM.md +36 -0
  20. package/templates/elcrm-docs/CLI.elCRM.md +27 -0
  21. package/templates/elcrm-docs/COMPONENTS.elCRM.md +68 -0
  22. package/templates/elcrm-docs/FORM.elCRM.md +39 -0
  23. package/templates/elcrm-docs/ICONS.elCRM.md +40 -0
  24. package/templates/elcrm-docs/MODAL.elCRM.md +41 -0
  25. package/templates/elcrm-docs/NOTICE.elCRM.md +31 -0
  26. package/templates/elcrm-docs/NOTIFICATIONS.elCRM.md +39 -0
  27. package/templates/elcrm-docs/PASSKEY.elCRM.md +26 -0
  28. package/templates/elcrm-docs/ROUTER.elCRM.md +34 -0
  29. package/templates/elcrm-docs/SERVER.elCRM.md +53 -0
  30. package/templates/elcrm-docs/SOCKET.elCRM.md +34 -0
  31. package/templates/elcrm-docs/TOOLTIP.elCRM.md +25 -0
  32. package/templates/panel/README.md +2 -26
  33. package/templates/panel/api/package.json +2 -5
  34. package/templates/panel/api/src/index.ts +11 -20
  35. package/templates/panel/api/src/lib/cron.ts +10 -7
  36. package/templates/panel/api/src/lib/db.ts +3 -5
  37. package/templates/panel/api/src/lib/jwt.ts +1 -1
  38. package/templates/panel/api/src/routes/auth.ts +20 -8
  39. package/templates/panel/web/package.json +4 -4
  40. package/templates/panel/web/src/modules/Account/sections/Profile.tsx +0 -1
  41. package/templates/panel/web/src/modules/Settings/sections/General.tsx +0 -2
  42. package/templates/panel/web/src/modules/Settings/sections/Profile.tsx +0 -1
  43. package/templates/panel/web/src/pages/Auth.tsx +0 -1
  44. package/templates/panel/web/src/style/elcrm.css +4 -1
  45. package/templates/panel/web/src/style/theme-dark.css +65 -3
  46. package/templates/panel/web/src/style/theme-light.css +65 -3
  47. package/templates/panel/web/src/style/theme.css +73 -3
@@ -1,4 +1,10 @@
1
- import { createRouter, rpcErr, rpcOk } from "@elcrm/server";
1
+ import {
2
+ createRouter,
3
+ rpcErr,
4
+ rpcLogout,
5
+ rpcOk,
6
+ type Middleware,
7
+ } from "@elcrm/server";
2
8
  import { db } from "../lib/db";
3
9
  import { jwt } from "../lib/jwt";
4
10
 
@@ -32,8 +38,19 @@ function publicUser(
32
38
  };
33
39
  }
34
40
 
41
+ /**
42
+ * createServer уже разобрал ssid → params.user.
43
+ * Не вызываем jwt.init повторно — только наличие user.
44
+ * publicMethods: pass (логин без сессии).
45
+ */
46
+ const requireUser: Middleware = async ({ path, user }) => {
47
+ if (path[1] === "pass") return;
48
+ if (!user?.id) return rpcLogout();
49
+ };
50
+
35
51
  export function registerAuthRoutes() {
36
52
  createRouter("auth")
53
+ .use(requireUser)
37
54
  .use("pass", async ({ body, headers, jwt: jwtCtx }) => {
38
55
  const login = String(body?.login ?? "").trim();
39
56
  const password = String(body?.password ?? "");
@@ -49,7 +66,7 @@ export function registerAuthRoutes() {
49
66
 
50
67
  if (!data) return rpcErr("Пользователь не найден");
51
68
  if (data.active === 0) {
52
- return { status: "delete", data: "Аккаунт заблокирован" };
69
+ return rpcErr("Аккаунт заблокирован", 403);
53
70
  }
54
71
  if (!data.password || data.password !== jwt.password(password)) {
55
72
  return rpcErr("Данные не верны");
@@ -65,19 +82,14 @@ export function registerAuthRoutes() {
65
82
  return rpcOk(pub, { ssid });
66
83
  })
67
84
  .use("me", async ({ user }) => {
68
- if (!user?.id) return { status: "logout", data: null };
69
85
  const row = await db.get<UserRow>(
70
86
  `SELECT ${USER_PUBLIC_COLS} FROM users WHERE id = ?`,
71
87
  [user.id],
72
88
  );
73
- if (!row || row.active === 0) {
74
- return { status: "logout", data: null };
75
- }
89
+ if (!row || row.active === 0) return rpcLogout();
76
90
  return rpcOk(publicUser(row));
77
91
  })
78
92
  .use("profile", async ({ user, body }) => {
79
- if (!user?.id) return { status: "logout", data: null };
80
-
81
93
  const display_name = String(
82
94
  body?.display_name ?? body?.name ?? "",
83
95
  ).trim();
@@ -11,14 +11,14 @@
11
11
  "dependencies": {
12
12
  "@elcrm/alert": "0.1.1",
13
13
  "@elcrm/api": "^0.1.55",
14
- "@elcrm/button": "1.0.1",
14
+ "@elcrm/button": "1.0.2",
15
15
  "@elcrm/components": "0.0.1",
16
16
  "@elcrm/core": "0.1.2",
17
17
  "@elcrm/crypto": "0.0.1",
18
18
  "@elcrm/error-boundary": "0.0.2",
19
- "@elcrm/form": "0.0.88",
20
- "@elcrm/modal": "0.0.32",
21
- "@elcrm/notice": "0.1.1",
19
+ "@elcrm/form": "0.1.2",
20
+ "@elcrm/modal": "0.0.34",
21
+ "@elcrm/notice": "0.1.3",
22
22
  "@elcrm/notifications": "0.3.1",
23
23
  "@elcrm/passkey": "^0.0.1",
24
24
  "@elcrm/router": "0.0.58",
@@ -57,7 +57,6 @@ export default function Profile() {
57
57
  form={form}
58
58
  label="Имя"
59
59
  placeholder="Отображаемое имя"
60
- native
61
60
  />
62
61
  <EmailField
63
62
  name="email"
@@ -32,14 +32,12 @@ export default function General() {
32
32
  form={form}
33
33
  label="Имя"
34
34
  placeholder="Panel"
35
- native
36
35
  />
37
36
  <StringField
38
37
  name="description"
39
38
  form={form}
40
39
  label="Описание"
41
40
  placeholder="Краткое описание"
42
- native
43
41
  />
44
42
  </Section>
45
43
  </Section>
@@ -68,7 +68,6 @@ export default function Profile() {
68
68
  form={form}
69
69
  label="Имя"
70
70
  placeholder="Отображаемое имя"
71
- native
72
71
  />
73
72
  <EmailField
74
73
  name="email"
@@ -71,7 +71,6 @@ export default function Auth() {
71
71
  name="login"
72
72
  form={form}
73
73
  placeholder="Логин"
74
- native
75
74
  autoComplete="username"
76
75
  />
77
76
  <PasswordField
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * База пакетов @elcrm/* + обе темы panel.
3
3
  * Порядок важен: сначала CSS пакетов, затем theme-* перекрывают токены.
4
+ *
5
+ * @elcrm/button@1.0.2+ — только style.css (цвета кнопок в theme-light/dark).
4
6
  */
5
7
 
6
8
  /* структура компонентов */
7
9
  @import "@elcrm/notice/style.css";
8
10
  @import "@elcrm/alert/style.css";
9
- @import "@elcrm/button/themes.css";
10
11
  @import "@elcrm/button/style.css";
11
12
  @import "@elcrm/search/style.css";
12
13
  @import "@elcrm/modal/style.css";
@@ -17,6 +18,8 @@
17
18
  @import "@elcrm/form/dark.css";
18
19
  @import "@elcrm/alert/light.css";
19
20
  @import "@elcrm/alert/dark.css";
21
+ @import "@elcrm/notice/light.css";
22
+ @import "@elcrm/notice/dark.css";
20
23
 
21
24
  /* общие размеры/геометрия, затем цвета light/dark */
22
25
  @import "./theme.css";
@@ -19,6 +19,8 @@ body[data-theme="dark"] {
19
19
  --shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
20
20
 
21
21
  /* —— button —— */
22
+ --button-focus-ring: rgba(91, 140, 255, 0.55);
23
+ --button-border-primary: 1px solid transparent;
22
24
  --button-primary-bg: var(--accent);
23
25
  --button-primary-fg: #ffffff;
24
26
  --button-primary-hover-bg: #7aa3ff;
@@ -29,18 +31,42 @@ body[data-theme="dark"] {
29
31
  --button-secondary-fg: rgba(255, 255, 255, 0.92);
30
32
  --button-secondary-hover-bg: rgba(255, 255, 255, 0.12);
31
33
  --button-secondary-hover-fg: #ffffff;
34
+ --button-secondary-active-bg: rgba(255, 255, 255, 0.16);
35
+ --button-secondary-active-fg: #ffffff;
32
36
  --button-border-secondary: 1px solid var(--line);
37
+ --button-positive-bg: var(--success);
38
+ --button-positive-fg: #0f1216;
39
+ --button-positive-hover-bg: #52e0a0;
40
+ --button-positive-hover-fg: #0f1216;
41
+ --button-positive-active-bg: #2bb872;
42
+ --button-positive-active-fg: #0f1216;
43
+ --button-border-positive: 1px solid transparent;
44
+ --button-negative-bg: var(--danger);
45
+ --button-negative-fg: #ffffff;
46
+ --button-negative-hover-bg: #ff8585;
47
+ --button-negative-hover-fg: #ffffff;
48
+ --button-negative-active-bg: #e05555;
49
+ --button-negative-active-fg: #ffffff;
50
+ --button-border-negative: 1px solid transparent;
33
51
  --button-ghost-bg: transparent;
34
52
  --button-ghost-fg: var(--muted);
35
53
  --button-ghost-hover-bg: rgba(255, 255, 255, 0.06);
36
54
  --button-ghost-hover-fg: var(--text);
55
+ --button-ghost-active-bg: rgba(255, 255, 255, 0.1);
56
+ --button-ghost-active-fg: var(--text);
37
57
  --button-border-ghost: transparent;
58
+ --button-disabled-bg: rgba(255, 255, 255, 0.08);
59
+ --button-disabled-fg: rgba(255, 255, 255, 0.4);
60
+ --button-border-disabled: 1px solid transparent;
61
+ --button-await-bg: rgba(255, 255, 255, 0.08);
62
+ --button-await-fg: rgba(255, 255, 255, 0.4);
63
+ --button-border-await: 1px solid transparent;
38
64
 
39
65
  /* —— form / control —— */
40
66
  --field-border: 1px solid var(--line);
41
- --field-border-hover: 1px solid #3a4558;
42
- --field-border-focus: 1px solid var(--accent);
43
- --field-border-disabled: 1px solid var(--line);
67
+ --field-border-hover: #3a4558;
68
+ --field-border-focus: var(--accent);
69
+ --field-border-disabled: var(--line);
44
70
  --field-focus-ring: rgba(91, 140, 255, 0.28);
45
71
  --field-focus-shadow: 0 0 0 3px var(--field-focus-ring);
46
72
  --field-background: #0c0f14;
@@ -52,6 +78,39 @@ body[data-theme="dark"] {
52
78
  --field-placeholder: var(--muted);
53
79
  --field-label-color: var(--muted);
54
80
  --field-icon-color: var(--muted);
81
+
82
+ /* —— search (@elcrm/search) —— */
83
+ --search-border: var(--field-border);
84
+ --search-border-focus: var(--field-border-focus);
85
+ --search-background: var(--field-background);
86
+ --search-color: var(--field-color);
87
+ --search-placeholder: var(--field-placeholder);
88
+ --search-icon-color: var(--field-icon-color);
89
+ --search-focus-shadow: var(--field-focus-shadow);
90
+ --search-clear-hover-color: var(--field-color);
91
+ --search-clear-hover-bg: rgba(255, 255, 255, 0.08);
92
+ --search-badge-border: 1px solid var(--line);
93
+ --search-badge-background: rgba(255, 255, 255, 0.06);
94
+ --search-badge-color: var(--muted);
95
+ --search-list-border: 1px solid var(--line);
96
+ --search-list-background: #12161e;
97
+ --search-list-color: var(--field-color);
98
+ --search-list-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
99
+ --search-option-active-bg: rgba(91, 140, 255, 0.16);
100
+ --search-option-active-color: var(--text);
101
+ --search-option-meta-color: var(--muted);
102
+ --search-ghost-bg: transparent;
103
+ --search-ghost-bg-focus: rgba(255, 255, 255, 0.06);
104
+ --search-ghost-fg: var(--text);
105
+ --search-border-ghost: 1px solid transparent;
106
+ --search-border-ghost-focus: 1px solid var(--line);
107
+ --search-ghost-focus-shadow: none;
108
+ --search-filled-bg: rgba(255, 255, 255, 0.08);
109
+ --search-filled-bg-focus: rgba(255, 255, 255, 0.12);
110
+ --search-filled-fg: var(--text);
111
+ --search-border-filled: 1px solid transparent;
112
+ --search-border-filled-focus: 1px solid var(--line);
113
+ --search-filled-focus-shadow: none;
55
114
  --field-check-box-border: 2px solid var(--muted);
56
115
  --field-error-border: var(--danger);
57
116
  --field-error-color: #ff8e8e;
@@ -62,6 +121,9 @@ body[data-theme="dark"] {
62
121
  --select-li-hover-bg: rgba(255, 255, 255, 0.06);
63
122
  --select-li-selected-bg: rgba(91, 140, 255, 0.18);
64
123
 
124
+ /* @elcrm/form — общая тень попапов (Date / Select / Color / Time) */
125
+ --popup-shadow: 0 6px 24px 0 #00000040;
126
+
65
127
  /* —— modal / alert / notice —— */
66
128
  --modal-background-color: var(--panel-elevated);
67
129
  --modal-background-overflow: rgba(0, 0, 0, 0.65);
@@ -19,6 +19,8 @@ body[data-theme="light"] {
19
19
  --shadow: 0 8px 24px rgba(26, 31, 41, 0.1);
20
20
 
21
21
  /* —— button —— */
22
+ --button-focus-ring: rgba(59, 110, 245, 0.55);
23
+ --button-border-primary: 1px solid transparent;
22
24
  --button-primary-bg: var(--accent);
23
25
  --button-primary-fg: #ffffff;
24
26
  --button-primary-hover-bg: #2f5de0;
@@ -29,18 +31,42 @@ body[data-theme="light"] {
29
31
  --button-secondary-fg: var(--text);
30
32
  --button-secondary-hover-bg: #e2e7ef;
31
33
  --button-secondary-hover-fg: var(--text);
34
+ --button-secondary-active-bg: #d8dee8;
35
+ --button-secondary-active-fg: var(--text);
32
36
  --button-border-secondary: 1px solid var(--line);
37
+ --button-positive-bg: var(--success);
38
+ --button-positive-fg: #ffffff;
39
+ --button-positive-hover-bg: #198a55;
40
+ --button-positive-hover-fg: #ffffff;
41
+ --button-positive-active-bg: #157348;
42
+ --button-positive-active-fg: #ffffff;
43
+ --button-border-positive: 1px solid transparent;
44
+ --button-negative-bg: var(--danger);
45
+ --button-negative-fg: #ffffff;
46
+ --button-negative-hover-bg: #c03c3c;
47
+ --button-negative-hover-fg: #ffffff;
48
+ --button-negative-active-bg: #a83434;
49
+ --button-negative-active-fg: #ffffff;
50
+ --button-border-negative: 1px solid transparent;
33
51
  --button-ghost-bg: transparent;
34
52
  --button-ghost-fg: var(--muted);
35
53
  --button-ghost-hover-bg: rgba(26, 31, 41, 0.06);
36
54
  --button-ghost-hover-fg: var(--text);
55
+ --button-ghost-active-bg: rgba(26, 31, 41, 0.1);
56
+ --button-ghost-active-fg: var(--text);
37
57
  --button-border-ghost: transparent;
58
+ --button-disabled-bg: #e6e6e6;
59
+ --button-disabled-fg: #6b6b6b;
60
+ --button-border-disabled: 1px solid transparent;
61
+ --button-await-bg: #e6e6e6;
62
+ --button-await-fg: #6b6b6b;
63
+ --button-border-await: 1px solid transparent;
38
64
 
39
65
  /* —— form / control —— */
40
66
  --field-border: 1px solid var(--line);
41
- --field-border-hover: 1px solid #b8c0ce;
42
- --field-border-focus: 1px solid var(--accent);
43
- --field-border-disabled: 1px solid var(--line);
67
+ --field-border-hover: #b8c0ce;
68
+ --field-border-focus: var(--accent);
69
+ --field-border-disabled: var(--line);
44
70
  --field-focus-ring: rgba(59, 110, 245, 0.22);
45
71
  --field-focus-shadow: 0 0 0 3px var(--field-focus-ring);
46
72
  --field-background: #ffffff;
@@ -52,6 +78,39 @@ body[data-theme="light"] {
52
78
  --field-placeholder: var(--muted);
53
79
  --field-label-color: var(--muted);
54
80
  --field-icon-color: var(--muted);
81
+
82
+ /* —— search (@elcrm/search) —— */
83
+ --search-border: var(--field-border);
84
+ --search-border-focus: var(--field-border-focus);
85
+ --search-background: var(--field-background);
86
+ --search-color: var(--field-color);
87
+ --search-placeholder: var(--field-placeholder);
88
+ --search-icon-color: var(--field-icon-color);
89
+ --search-focus-shadow: var(--field-focus-shadow);
90
+ --search-clear-hover-color: var(--field-color);
91
+ --search-clear-hover-bg: rgba(26, 31, 41, 0.08);
92
+ --search-badge-border: 1px solid var(--line);
93
+ --search-badge-background: #eef1f6;
94
+ --search-badge-color: var(--muted);
95
+ --search-list-border: 1px solid var(--line);
96
+ --search-list-background: var(--field-background);
97
+ --search-list-color: var(--field-color);
98
+ --search-list-shadow: 0 8px 24px rgba(15, 18, 22, 0.12);
99
+ --search-option-active-bg: rgba(59, 110, 245, 0.1);
100
+ --search-option-active-color: var(--text);
101
+ --search-option-meta-color: var(--muted);
102
+ --search-ghost-bg: transparent;
103
+ --search-ghost-bg-focus: rgba(26, 31, 41, 0.04);
104
+ --search-ghost-fg: var(--text);
105
+ --search-border-ghost: 1px solid transparent;
106
+ --search-border-ghost-focus: 1px solid var(--line);
107
+ --search-ghost-focus-shadow: none;
108
+ --search-filled-bg: #eef1f6;
109
+ --search-filled-bg-focus: #e2e7ef;
110
+ --search-filled-fg: var(--text);
111
+ --search-border-filled: 1px solid transparent;
112
+ --search-border-filled-focus: 1px solid var(--line);
113
+ --search-filled-focus-shadow: none;
55
114
  --field-check-box-border: 2px solid var(--muted);
56
115
  --field-error-border: var(--danger);
57
116
  --field-error-color: var(--danger);
@@ -62,6 +121,9 @@ body[data-theme="light"] {
62
121
  --select-li-hover-bg: rgba(26, 31, 41, 0.05);
63
122
  --select-li-selected-bg: rgba(59, 110, 245, 0.12);
64
123
 
124
+ /* @elcrm/form — общая тень попапов (Date / Select / Color / Time) */
125
+ --popup-shadow: 0 6px 24px 0 #00000040;
126
+
65
127
  /* —— modal / alert / notice —— */
66
128
  --modal-background-color: #ffffff;
67
129
  --modal-background-overflow: rgba(26, 31, 41, 0.4);
@@ -3,8 +3,23 @@
3
3
  * Любой цвет, color-mix, var(--text|--accent|…) — только в theme-light / theme-dark.
4
4
  */
5
5
  :root {
6
- /* —— button —— */
6
+ /* —— button (@elcrm/button: геометрия без цвета) —— */
7
+ --button-height: var(--control-height-m, var(--control-height));
8
+ --button-height-s: var(--control-height-s, 32px);
9
+ --button-height-m: var(--control-height-m, var(--control-height));
10
+ --button-height-l: var(--control-height-l, 48px);
11
+ --button-padding: 0.65em 1em;
12
+ --button-padding-s: 0 8px;
13
+ --button-gap: 0.5em;
7
14
  --button-radius: 10px;
15
+ --button-border: 1px solid transparent;
16
+ --button-max-width: 100%;
17
+ --button-min-width-m: 200px;
18
+ --button-transition: 0.18s ease;
19
+ --button-focus-ring-offset: 2px;
20
+ --button-size-s: 14px;
21
+ --button-size-m: 16px;
22
+ --button-size-l: 18px;
8
23
  --button-shadow: none;
9
24
  --button-shadow-hover: none;
10
25
  --button-shadow-active: none;
@@ -12,18 +27,73 @@
12
27
  --button-primary-hover-shadow: none;
13
28
  --button-primary-active-shadow: none;
14
29
  --button-secondary-shadow: none;
30
+ --button-secondary-hover-shadow: none;
31
+ --button-secondary-active-shadow: none;
32
+ --button-positive-shadow: none;
33
+ --button-positive-hover-shadow: none;
34
+ --button-positive-active-shadow: none;
35
+ --button-negative-shadow: none;
36
+ --button-negative-hover-shadow: none;
37
+ --button-negative-active-shadow: none;
38
+ --button-ghost-shadow: none;
39
+ --button-ghost-hover-shadow: none;
40
+ --button-ghost-active-shadow: none;
15
41
 
16
42
  /* —— form / control —— */
17
43
  --control-height: 44px;
44
+ --control-height-s: 32px;
45
+ --control-height-m: var(--control-height);
46
+ --control-height-l: 48px;
18
47
  --control-radius: 10px;
19
- --control-side-padding: 14px;
20
48
  --control-font-size: 15px;
21
49
  --field-height: var(--control-height);
22
50
  --field-width: 100%;
23
- --field-padding: 10px 12px;
51
+ /* единый padding капсулы (size s|m|l → через --field-height) */
52
+ --field-padding-block: calc(var(--field-height) * 0.18);
53
+ --field-padding-inline: calc(var(--field-height) * 0.32);
54
+ --field-padding: var(--field-padding-block) var(--field-padding-inline);
24
55
  --field-radius: var(--control-radius);
25
56
  --field-label-size: 13px;
26
57
 
58
+ /* —— search (@elcrm/search: геометрия без цвета) —— */
59
+ --search-height-s: var(--control-height-s);
60
+ --search-height-m: var(--control-height-m);
61
+ --search-height-l: var(--control-height-l);
62
+ --search-size-s: 13px;
63
+ --search-size-m: var(--control-font-size);
64
+ --search-size-l: 17px;
65
+ --search-padding-x: 10px;
66
+ --search-padding-x-s: 8px;
67
+ --search-padding-x-l: 12px;
68
+ --search-gap: 8px;
69
+ --search-gap-s: 6px;
70
+ --search-gap-l: 10px;
71
+ --search-radius: var(--field-radius);
72
+ --search-min-width-m: 200px;
73
+ --search-minimal-expand-min: 180px;
74
+ --search-icon-size: 15px;
75
+ --search-icon-stroke: 1.5;
76
+ --search-icon-opacity: 0.85;
77
+ --search-clear-size: 22px;
78
+ --search-clear-radius: 6px;
79
+ --search-transition: 0.22s ease;
80
+ --search-disabled-opacity: 0.7;
81
+ --search-spinner-duration: 0.7s;
82
+ --search-after-gap: 4px;
83
+ --search-badge-font-size: 11px;
84
+ --search-badge-padding: 2px 5px;
85
+ --search-badge-radius: 6px;
86
+ --search-list-z: 40;
87
+ --search-list-offset: 6px;
88
+ --search-list-padding: 6px;
89
+ --search-list-max-height: 240px;
90
+ --search-list-radius: 10px;
91
+ --search-option-padding: 8px 10px;
92
+ --search-option-radius: 8px;
93
+ --search-option-meta-size: 12px;
94
+ --search-border-ghost: 1px solid transparent;
95
+ --search-border-filled: 1px solid transparent;
96
+
27
97
  /* —— modal —— */
28
98
  --modal-radius: 12px;
29
99