slexkit 0.3.0 → 0.3.1

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 (45) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +4 -3
  3. package/README.zh-CN.md +4 -3
  4. package/dist/ai/llms-components.txt +2 -1
  5. package/dist/ai/llms-full.txt +73 -42
  6. package/dist/ai/llms-runtime.txt +11 -12
  7. package/dist/ai/llms.txt +1 -1
  8. package/dist/ai/slexkit-ai-manifest.json +77 -73
  9. package/dist/components/checkbox.js +1 -0
  10. package/dist/components/choice.css +2 -2
  11. package/dist/components/display.css +1 -1
  12. package/dist/components/index.js +56 -153
  13. package/dist/components/input.css +53 -119
  14. package/dist/components/input.js +33 -143
  15. package/dist/components/radio-group.js +1 -0
  16. package/dist/components/select.css +0 -6
  17. package/dist/components/slider.css +27 -18
  18. package/dist/components/specs.js +2 -1
  19. package/dist/components/switch.css +3 -3
  20. package/dist/components/switch.js +1 -0
  21. package/dist/components/text-input.css +21 -90
  22. package/dist/components/text.js +12 -0
  23. package/dist/components/tooling.css +0 -1
  24. package/dist/runtime.cjs +22 -5
  25. package/dist/runtime.js +22 -5
  26. package/dist/slexkit.cjs +78 -158
  27. package/dist/slexkit.css +54 -121
  28. package/dist/slexkit.js +78 -158
  29. package/dist/types/version.d.ts +2 -2
  30. package/dist/umd/slexkit.tooling.umd.js +77 -158
  31. package/dist/umd/slexkit.umd.js +78 -158
  32. package/package.json +3 -2
  33. package/skills/slexkit-host-integration/SKILL.md +1 -1
  34. package/src/components/svelte/display/Text.svelte +14 -1
  35. package/src/components/svelte/input/Checkbox.svelte +1 -1
  36. package/src/components/svelte/input/Input.svelte +0 -110
  37. package/src/components/svelte/input/RadioGroup.svelte +1 -1
  38. package/src/components/svelte/input/Switch.svelte +1 -1
  39. package/src/styles/components/choice.css +2 -2
  40. package/src/styles/components/select.css +0 -6
  41. package/src/styles/components/slider.css +27 -18
  42. package/src/styles/components/switch.css +3 -3
  43. package/src/styles/components/text-input.css +21 -90
  44. package/src/styles/display.css +1 -1
  45. package/src/styles/tooling.css +0 -1
@@ -104,11 +104,11 @@
104
104
  opacity: 0.55;
105
105
  }
106
106
 
107
- .slex-switch:has(.slex-switch-input:disabled) {
107
+ .slex-switch[data-disabled="true"] {
108
108
  cursor: not-allowed;
109
109
  }
110
110
 
111
- .slex-switch:has(.slex-switch-input:disabled):hover .slex-switch-control,
112
- .slex-switch:has(.slex-switch-input:disabled):hover .slex-switch-control::after {
111
+ .slex-switch[data-disabled="true"]:hover .slex-switch-control,
112
+ .slex-switch[data-disabled="true"]:hover .slex-switch-control::after {
113
113
  box-shadow: none;
114
114
  }
@@ -89,6 +89,7 @@ function Switch($$anchor, $$props) {
89
89
  reset(span);
90
90
  template_effect(($0, $1) => {
91
91
  set_attribute(label, "data-state", get(enabled) ? "on" : "off");
92
+ set_attribute(label, "data-disabled", get(p).disabled ? "true" : undefined);
92
93
  input.disabled = !!get(p).disabled;
93
94
  set_attribute(input, "aria-label", $0);
94
95
  set_text(text_1, $1);
@@ -45,16 +45,18 @@
45
45
  border: 1px solid var(--input);
46
46
  border-radius: var(--radius);
47
47
  background: var(--background);
48
+ background-clip: padding-box;
48
49
  color: var(--foreground);
49
50
  font-family: inherit;
50
51
  font-size: 0.875rem;
51
52
  line-height: 1.5;
52
53
  outline: none;
54
+ -webkit-appearance: none;
55
+ appearance: none;
53
56
  transition: border-color 150ms ease, box-shadow 150ms ease;
54
57
  }
55
58
 
56
- .slex-input-control[data-has-unit="true"] .slex-input,
57
- .slex-input-control[data-has-controls="true"] .slex-input {
59
+ .slex-input-control[data-has-unit="true"] .slex-input {
58
60
  border-top-right-radius: 0;
59
61
  border-bottom-right-radius: 0;
60
62
  }
@@ -78,79 +80,16 @@
78
80
  transition: border-color 150ms ease, box-shadow 150ms ease;
79
81
  }
80
82
 
81
- .slex-input-control[data-has-controls="true"] .slex-input-unit {
82
- border-radius: 0;
83
- }
84
-
85
- .slex-input-controls {
86
- box-sizing: border-box;
87
- display: inline-grid;
88
- grid-template-rows: repeat(2, minmax(0, 1fr));
89
- align-items: stretch;
90
- flex: 0 0 auto;
91
- width: 1.875rem;
92
- min-width: 1.875rem;
93
- min-height: 2.5625rem;
94
- overflow: hidden;
95
- border: 1px solid var(--input);
96
- border-left: 0;
97
- border-radius: 0 var(--radius) var(--radius) 0;
98
- background: var(--background);
99
- transition: border-color 150ms ease, box-shadow 150ms ease;
100
- }
101
-
102
- .slex-input-step {
103
- box-sizing: border-box;
104
- display: inline-flex;
105
- align-items: center;
106
- justify-content: center;
107
- width: 100%;
108
- min-width: 0;
109
- min-height: 0;
110
- padding: 0;
111
- border: 0;
112
- border-top: 1px solid var(--input);
113
- border-radius: 0;
114
- background: transparent;
115
- color: var(--foreground);
116
- font: inherit;
117
- font-size: 0.75rem;
118
- font-weight: 600;
119
- line-height: 1;
120
- cursor: pointer;
121
- transition: border-color 150ms ease, background 150ms ease, box-shadow 150ms ease;
122
- }
123
-
124
- .slex-input-step:first-child {
125
- border-top: 0;
126
- }
127
-
128
- .slex-input-step:last-child {
129
- border-radius: 0;
130
- }
131
-
132
- .slex-input-step:hover:not(:disabled) {
133
- background: color-mix(in oklab, var(--muted) 52%, var(--background));
134
- }
135
-
136
- .slex-input-step:focus-visible {
137
- z-index: 1;
138
- outline: none;
139
- background: color-mix(in oklab, var(--muted) 58%, var(--background));
140
- box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ring) 34%, transparent);
141
- }
142
-
143
- .slex-input-step:disabled {
144
- opacity: 0.45;
145
- cursor: not-allowed;
146
- }
147
-
148
83
  .slex-input::placeholder {
149
84
  color: var(--muted-foreground);
150
85
  }
151
86
 
152
87
  .slex-input:focus {
153
88
  border-color: var(--ring);
89
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--ring) 18%, transparent);
90
+ }
91
+
92
+ .slex-input-control[data-has-unit="true"] .slex-input:focus {
154
93
  box-shadow: none;
155
94
  }
156
95
 
@@ -175,6 +114,10 @@
175
114
 
176
115
  .slex-input-field[data-invalid="true"] .slex-input:focus {
177
116
  border-color: var(--destructive);
117
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--destructive) 18%, transparent);
118
+ }
119
+
120
+ .slex-input-field[data-invalid="true"] .slex-input-control[data-has-unit="true"] .slex-input:focus {
178
121
  box-shadow: none;
179
122
  }
180
123
 
@@ -182,13 +125,12 @@
182
125
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--ring) 16%, transparent);
183
126
  }
184
127
 
185
- .slex-input-control:focus-within .slex-input,
186
- .slex-input-control:focus-within .slex-input-unit,
187
- .slex-input-control:focus-within .slex-input-controls {
188
- border-color: var(--ring);
128
+ .slex-input-control:not([data-has-unit]):focus-within {
129
+ box-shadow: none;
189
130
  }
190
131
 
191
- .slex-input-control:focus-within .slex-input-step {
132
+ .slex-input-control:focus-within .slex-input,
133
+ .slex-input-control:focus-within .slex-input-unit {
192
134
  border-color: var(--ring);
193
135
  }
194
136
 
@@ -197,22 +139,16 @@
197
139
  color: color-mix(in oklab, var(--destructive) 84%, var(--muted-foreground));
198
140
  }
199
141
 
200
- .slex-input-field[data-invalid="true"] .slex-input-step {
201
- border-color: color-mix(in oklab, var(--destructive) 72%, var(--input));
202
- }
203
-
204
- .slex-input-field[data-invalid="true"] .slex-input-controls {
205
- border-color: color-mix(in oklab, var(--destructive) 72%, var(--input));
206
- }
207
-
208
142
  .slex-input-field[data-invalid="true"] .slex-input-control:focus-within {
209
143
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--destructive) 18%, transparent);
210
144
  }
211
145
 
146
+ .slex-input-field[data-invalid="true"] .slex-input-control:not([data-has-unit]):focus-within {
147
+ box-shadow: none;
148
+ }
149
+
212
150
  .slex-input-field[data-invalid="true"] .slex-input-control:focus-within .slex-input,
213
- .slex-input-field[data-invalid="true"] .slex-input-control:focus-within .slex-input-unit,
214
- .slex-input-field[data-invalid="true"] .slex-input-control:focus-within .slex-input-controls,
215
- .slex-input-field[data-invalid="true"] .slex-input-control:focus-within .slex-input-step {
151
+ .slex-input-field[data-invalid="true"] .slex-input-control:focus-within .slex-input-unit {
216
152
  border-color: var(--destructive);
217
153
  }
218
154
 
@@ -226,11 +162,6 @@
226
162
  cursor: not-allowed;
227
163
  }
228
164
 
229
- .slex-input[disabled] ~ .slex-input-controls,
230
- .slex-input[readonly] ~ .slex-input-controls {
231
- opacity: 0.6;
232
- }
233
-
234
165
  .slex-input-description {
235
166
  color: var(--muted-foreground);
236
167
  font-size: 0.75rem;
@@ -11,10 +11,12 @@ import {
11
11
  reset,
12
12
  set,
13
13
  set_class,
14
+ set_style,
14
15
  set_text,
15
16
  state,
16
17
  template_effect,
17
18
  text1 as text,
19
+ user_derived,
18
20
  user_effect
19
21
  } from "../chunks/button-cr1fhsa7.js";
20
22
 
@@ -27,11 +29,21 @@ function Text($$anchor, $$props) {
27
29
  push($$props, true);
28
30
  let p = state(proxy({}));
29
31
  user_effect(() => bindPropStore($$props.props, (next) => set(p, next, true)));
32
+ const color = user_derived(() => get(p).color == null || get(p).color === "" ? undefined : text(get(p).color));
33
+ const size = user_derived(() => cssLength(get(p).size));
34
+ function cssLength(value) {
35
+ if (value == null || value === "")
36
+ return;
37
+ const rendered = text(value);
38
+ return /^-?\d+(\.\d+)?$/.test(rendered) ? `${rendered}px` : rendered;
39
+ }
30
40
  var div = root();
41
+ let styles;
31
42
  var text_1 = child(div, true);
32
43
  reset(div);
33
44
  template_effect(($0, $1) => {
34
45
  set_class(div, 1, $0);
46
+ styles = set_style(div, "", styles, { color: get(color), "font-size": get(size) });
35
47
  set_text(text_1, $1);
36
48
  }, [
37
49
  () => `slex-text${get(p).variant ? ` slex-text--${text(get(p).variant)}` : ""}${get(p).class ? ` ${text(get(p).class)}` : ""}`,
@@ -572,7 +572,6 @@
572
572
  overflow: hidden;
573
573
  clip: rect(0 0 0 0);
574
574
  white-space: nowrap;
575
- clip-path: inset(50%);
576
575
  }
577
576
 
578
577
  .slex-playground-error {
package/dist/runtime.cjs CHANGED
@@ -761,6 +761,9 @@ function isWritableComponent(type) {
761
761
  const mode = getComponentStateMode(type);
762
762
  return mode === "value" || mode === "checked" || mode === "enabled";
763
763
  }
764
+ function isReadableComponent(type) {
765
+ return getComponentStateMode(type) === "readable";
766
+ }
764
767
  function isStatefulComponent(type) {
765
768
  return getComponentStateMode(type) !== "none";
766
769
  }
@@ -877,7 +880,10 @@ function createGProxy(g, components, componentTypes) {
877
880
  function ensureComponentState(name, type, components, componentTypes) {
878
881
  if (!components[name])
879
882
  components[name] = {};
880
- componentTypes[name] = type;
883
+ const previousType = componentTypes[name] ?? "";
884
+ if (!(isWritableComponent(previousType) && isReadableComponent(type))) {
885
+ componentTypes[name] = type;
886
+ }
881
887
  return components[name];
882
888
  }
883
889
  function syncReadableComponentProp(type, state, propName, value) {
@@ -905,6 +911,9 @@ function syncReadableComponentProp(type, state, propName, value) {
905
911
  function syncComponentProps(type, name, props, components, componentTypes) {
906
912
  if (!name || !isStatefulComponent(type))
907
913
  return;
914
+ const previousType = componentTypes[name] ?? "";
915
+ if (isWritableComponent(previousType) && isReadableComponent(type))
916
+ return;
908
917
  const state = ensureComponentState(name, type, components, componentTypes);
909
918
  if (type === "input" && typeof props.type === "string") {
910
919
  assignInputType(state, props.type);
@@ -969,7 +978,7 @@ function seedStaticComponentState(type, state, props) {
969
978
  function warnDuplicateState(ns, name, currentType, currentPath, previous) {
970
979
  console.warn(`[SlexKit][${ns}] Component state '${name}' is declared more than once at ${previous.path} and ${currentPath}; state is shared by namespace and component name.`);
971
980
  if (previous.type !== currentType) {
972
- console.warn(`[SlexKit][${ns}] Component state '${name}' is used by multiple component types (${previous.type}, ${currentType}); the latest rendered type controls write behavior.`);
981
+ console.warn(`[SlexKit][${ns}] Component state '${name}' is used by multiple component types (${previous.type}, ${currentType}); use distinct names when components should not share state.`);
973
982
  }
974
983
  }
975
984
  function dynamicStateBinding(type, props) {
@@ -981,6 +990,9 @@ function isMirroredValueControlPair(previousType, currentType) {
981
990
  return previousType === "input" && currentType === "slider" || previousType === "slider" && currentType === "input";
982
991
  }
983
992
  function shouldWarnDuplicateState(currentType, currentBinding, previous) {
993
+ if (isReadableComponent(previous.type) && isReadableComponent(currentType)) {
994
+ return false;
995
+ }
984
996
  if (currentBinding && previous.stateBinding === currentBinding && isMirroredValueControlPair(previous.type, currentType)) {
985
997
  return false;
986
998
  }
@@ -1791,7 +1803,7 @@ ${parseSource}
1791
1803
  var parseSlexKitDsl = parseSlexSource;
1792
1804
 
1793
1805
  // src/version.ts
1794
- var SLEXKIT_VERSION = "0.3.0";
1806
+ var SLEXKIT_VERSION = "0.3.1";
1795
1807
  var SLEX_PROTOCOL_VERSION = "0.1";
1796
1808
  var SLEXKIT_COMPONENTS_VERSION = SLEXKIT_VERSION;
1797
1809
  function getSlexKitInfo() {
@@ -2919,7 +2931,6 @@ var inputSpec = component({
2919
2931
  min: { type: "string | number", dynamic: true, description: "Minimum value used by numeric input controls." },
2920
2932
  max: { type: "string | number", dynamic: true, description: "Maximum value used by numeric input controls." },
2921
2933
  step: { type: "string | number", dynamic: true, description: "Step size used by numeric input controls." },
2922
- controls: { type: "boolean", default: true, dynamic: true, description: "Show decrement and increment buttons for numeric inputs." },
2923
2934
  onchange: { type: "write-expression", description: "Write expression invoked when the value changes." }
2924
2935
  },
2925
2936
  children: noChildren,
@@ -3427,6 +3438,8 @@ var textSpec = component({
3427
3438
  content: { type: "string", dynamic: true, description: "Alias for text." },
3428
3439
  label: { type: "string", dynamic: true, description: "Alias for text." },
3429
3440
  variant: { type: "string", values: ["default", "muted"], default: "default", description: "Text visual variant." },
3441
+ color: { type: "string", dynamic: true, description: "Optional CSS color for controlled previews." },
3442
+ size: { type: "string | number", dynamic: true, description: "Optional font size. Numbers are treated as px." },
3430
3443
  class: { type: "string", description: "Additional host-controlled CSS class." }
3431
3444
  },
3432
3445
  children: noChildren,
@@ -3727,7 +3740,11 @@ function isRenderableSource(value) {
3727
3740
  return Object.keys(value).some((key) => key.includes(":"));
3728
3741
  }
3729
3742
  function bareLayoutFromSource(value) {
3730
- const { slex: _slex, namespace: _namespace, g: _g, layout: _layout, ...layout } = value;
3743
+ const layout = { ...value };
3744
+ delete layout.slex;
3745
+ delete layout.namespace;
3746
+ delete layout.g;
3747
+ delete layout.layout;
3731
3748
  return layout;
3732
3749
  }
3733
3750
  function isStateOnlySource(value) {
package/dist/runtime.js CHANGED
@@ -689,6 +689,9 @@ function isWritableComponent(type) {
689
689
  const mode = getComponentStateMode(type);
690
690
  return mode === "value" || mode === "checked" || mode === "enabled";
691
691
  }
692
+ function isReadableComponent(type) {
693
+ return getComponentStateMode(type) === "readable";
694
+ }
692
695
  function isStatefulComponent(type) {
693
696
  return getComponentStateMode(type) !== "none";
694
697
  }
@@ -805,7 +808,10 @@ function createGProxy(g, components, componentTypes) {
805
808
  function ensureComponentState(name, type, components, componentTypes) {
806
809
  if (!components[name])
807
810
  components[name] = {};
808
- componentTypes[name] = type;
811
+ const previousType = componentTypes[name] ?? "";
812
+ if (!(isWritableComponent(previousType) && isReadableComponent(type))) {
813
+ componentTypes[name] = type;
814
+ }
809
815
  return components[name];
810
816
  }
811
817
  function syncReadableComponentProp(type, state, propName, value) {
@@ -833,6 +839,9 @@ function syncReadableComponentProp(type, state, propName, value) {
833
839
  function syncComponentProps(type, name, props, components, componentTypes) {
834
840
  if (!name || !isStatefulComponent(type))
835
841
  return;
842
+ const previousType = componentTypes[name] ?? "";
843
+ if (isWritableComponent(previousType) && isReadableComponent(type))
844
+ return;
836
845
  const state = ensureComponentState(name, type, components, componentTypes);
837
846
  if (type === "input" && typeof props.type === "string") {
838
847
  assignInputType(state, props.type);
@@ -897,7 +906,7 @@ function seedStaticComponentState(type, state, props) {
897
906
  function warnDuplicateState(ns, name, currentType, currentPath, previous) {
898
907
  console.warn(`[SlexKit][${ns}] Component state '${name}' is declared more than once at ${previous.path} and ${currentPath}; state is shared by namespace and component name.`);
899
908
  if (previous.type !== currentType) {
900
- console.warn(`[SlexKit][${ns}] Component state '${name}' is used by multiple component types (${previous.type}, ${currentType}); the latest rendered type controls write behavior.`);
909
+ console.warn(`[SlexKit][${ns}] Component state '${name}' is used by multiple component types (${previous.type}, ${currentType}); use distinct names when components should not share state.`);
901
910
  }
902
911
  }
903
912
  function dynamicStateBinding(type, props) {
@@ -909,6 +918,9 @@ function isMirroredValueControlPair(previousType, currentType) {
909
918
  return previousType === "input" && currentType === "slider" || previousType === "slider" && currentType === "input";
910
919
  }
911
920
  function shouldWarnDuplicateState(currentType, currentBinding, previous) {
921
+ if (isReadableComponent(previous.type) && isReadableComponent(currentType)) {
922
+ return false;
923
+ }
912
924
  if (currentBinding && previous.stateBinding === currentBinding && isMirroredValueControlPair(previous.type, currentType)) {
913
925
  return false;
914
926
  }
@@ -1719,7 +1731,7 @@ ${parseSource}
1719
1731
  var parseSlexKitDsl = parseSlexSource;
1720
1732
 
1721
1733
  // src/version.ts
1722
- var SLEXKIT_VERSION = "0.3.0";
1734
+ var SLEXKIT_VERSION = "0.3.1";
1723
1735
  var SLEX_PROTOCOL_VERSION = "0.1";
1724
1736
  var SLEXKIT_COMPONENTS_VERSION = SLEXKIT_VERSION;
1725
1737
  function getSlexKitInfo() {
@@ -2847,7 +2859,6 @@ var inputSpec = component({
2847
2859
  min: { type: "string | number", dynamic: true, description: "Minimum value used by numeric input controls." },
2848
2860
  max: { type: "string | number", dynamic: true, description: "Maximum value used by numeric input controls." },
2849
2861
  step: { type: "string | number", dynamic: true, description: "Step size used by numeric input controls." },
2850
- controls: { type: "boolean", default: true, dynamic: true, description: "Show decrement and increment buttons for numeric inputs." },
2851
2862
  onchange: { type: "write-expression", description: "Write expression invoked when the value changes." }
2852
2863
  },
2853
2864
  children: noChildren,
@@ -3355,6 +3366,8 @@ var textSpec = component({
3355
3366
  content: { type: "string", dynamic: true, description: "Alias for text." },
3356
3367
  label: { type: "string", dynamic: true, description: "Alias for text." },
3357
3368
  variant: { type: "string", values: ["default", "muted"], default: "default", description: "Text visual variant." },
3369
+ color: { type: "string", dynamic: true, description: "Optional CSS color for controlled previews." },
3370
+ size: { type: "string | number", dynamic: true, description: "Optional font size. Numbers are treated as px." },
3358
3371
  class: { type: "string", description: "Additional host-controlled CSS class." }
3359
3372
  },
3360
3373
  children: noChildren,
@@ -3655,7 +3668,11 @@ function isRenderableSource(value) {
3655
3668
  return Object.keys(value).some((key) => key.includes(":"));
3656
3669
  }
3657
3670
  function bareLayoutFromSource(value) {
3658
- const { slex: _slex, namespace: _namespace, g: _g, layout: _layout, ...layout } = value;
3671
+ const layout = { ...value };
3672
+ delete layout.slex;
3673
+ delete layout.namespace;
3674
+ delete layout.g;
3675
+ delete layout.layout;
3659
3676
  return layout;
3660
3677
  }
3661
3678
  function isStateOnlySource(value) {