slexkit 0.3.0 → 0.3.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 (48) hide show
  1. package/CHANGELOG.md +30 -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 +85 -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/base.css +0 -46
  10. package/dist/components/checkbox.js +1 -0
  11. package/dist/components/choice.css +2 -2
  12. package/dist/components/display.css +1 -1
  13. package/dist/components/index.js +56 -153
  14. package/dist/components/input.css +53 -119
  15. package/dist/components/input.js +33 -143
  16. package/dist/components/radio-group.js +1 -0
  17. package/dist/components/select.css +0 -6
  18. package/dist/components/slider.css +27 -18
  19. package/dist/components/specs.js +2 -1
  20. package/dist/components/switch.css +3 -3
  21. package/dist/components/switch.js +1 -0
  22. package/dist/components/text-input.css +21 -90
  23. package/dist/components/text.js +12 -0
  24. package/dist/components/tooling.css +0 -1
  25. package/dist/runtime.cjs +67 -28
  26. package/dist/runtime.js +67 -28
  27. package/dist/slexkit.cjs +123 -181
  28. package/dist/slexkit.css +54 -167
  29. package/dist/slexkit.js +123 -181
  30. package/dist/types/engine/types.d.ts +1 -1
  31. package/dist/types/version.d.ts +2 -2
  32. package/dist/umd/slexkit.tooling.umd.js +122 -181
  33. package/dist/umd/slexkit.umd.js +123 -181
  34. package/package.json +3 -2
  35. package/skills/slexkit-host-integration/SKILL.md +1 -1
  36. package/src/components/svelte/display/Text.svelte +14 -1
  37. package/src/components/svelte/input/Checkbox.svelte +1 -1
  38. package/src/components/svelte/input/Input.svelte +0 -110
  39. package/src/components/svelte/input/RadioGroup.svelte +1 -1
  40. package/src/components/svelte/input/Switch.svelte +1 -1
  41. package/src/styles/components/choice.css +2 -2
  42. package/src/styles/components/select.css +0 -6
  43. package/src/styles/components/slider.css +27 -18
  44. package/src/styles/components/switch.css +3 -3
  45. package/src/styles/components/text-input.css +21 -90
  46. package/src/styles/display.css +1 -1
  47. package/src/styles/layout.css +0 -46
  48. package/src/styles/tooling.css +0 -1
@@ -499,19 +499,31 @@
499
499
  function createComponentAccessor(read) {
500
500
  const subscribers = new Set;
501
501
  let current = read();
502
- const accessor = () => current;
503
- accessor.subscribe = (run) => {
504
- subscribers.add(run);
505
- run(current);
506
- const stop = createEffect(() => {
502
+ let stopEffect;
503
+ const start = () => {
504
+ if (stopEffect)
505
+ return;
506
+ stopEffect = createEffect(() => {
507
507
  current = read();
508
- for (const subscriber of subscribers)
508
+ for (const subscriber of Array.from(subscribers))
509
509
  subscriber(current);
510
510
  flushDom?.();
511
511
  });
512
+ };
513
+ const accessor = () => current;
514
+ accessor.subscribe = (run) => {
515
+ const wasIdle = subscribers.size === 0;
516
+ subscribers.add(run);
517
+ if (wasIdle)
518
+ start();
519
+ else
520
+ run(current);
512
521
  return () => {
513
522
  subscribers.delete(run);
514
- stop();
523
+ if (subscribers.size === 0) {
524
+ stopEffect?.();
525
+ stopEffect = undefined;
526
+ }
515
527
  };
516
528
  };
517
529
  return accessor;
@@ -792,6 +804,9 @@
792
804
  const mode = getComponentStateMode(type);
793
805
  return mode === "value" || mode === "checked" || mode === "enabled";
794
806
  }
807
+ function isReadableComponent(type) {
808
+ return getComponentStateMode(type) === "readable";
809
+ }
795
810
  function isStatefulComponent(type) {
796
811
  return getComponentStateMode(type) !== "none";
797
812
  }
@@ -908,7 +923,10 @@
908
923
  function ensureComponentState(name, type, components, componentTypes) {
909
924
  if (!components[name])
910
925
  components[name] = {};
911
- componentTypes[name] = type;
926
+ const previousType = componentTypes[name] ?? "";
927
+ if (!(isWritableComponent(previousType) && isReadableComponent(type))) {
928
+ componentTypes[name] = type;
929
+ }
912
930
  return components[name];
913
931
  }
914
932
  function syncReadableComponentProp(type, state, propName, value) {
@@ -936,6 +954,9 @@
936
954
  function syncComponentProps(type, name, props, components, componentTypes) {
937
955
  if (!name || !isStatefulComponent(type))
938
956
  return;
957
+ const previousType = componentTypes[name] ?? "";
958
+ if (isWritableComponent(previousType) && isReadableComponent(type))
959
+ return;
939
960
  const state = ensureComponentState(name, type, components, componentTypes);
940
961
  if (type === "input" && typeof props.type === "string") {
941
962
  assignInputType(state, props.type);
@@ -1000,7 +1021,7 @@
1000
1021
  function warnDuplicateState(ns, name, currentType, currentPath, previous) {
1001
1022
  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.`);
1002
1023
  if (previous.type !== currentType) {
1003
- console.warn(`[SlexKit][${ns}] Component state '${name}' is used by multiple component types (${previous.type}, ${currentType}); the latest rendered type controls write behavior.`);
1024
+ 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.`);
1004
1025
  }
1005
1026
  }
1006
1027
  function dynamicStateBinding(type, props) {
@@ -1012,6 +1033,9 @@
1012
1033
  return previousType === "input" && currentType === "slider" || previousType === "slider" && currentType === "input";
1013
1034
  }
1014
1035
  function shouldWarnDuplicateState(currentType, currentBinding, previous) {
1036
+ if (isReadableComponent(previous.type) && isReadableComponent(currentType)) {
1037
+ return false;
1038
+ }
1015
1039
  if (currentBinding && previous.stateBinding === currentBinding && isMirroredValueControlPair(previous.type, currentType)) {
1016
1040
  return false;
1017
1041
  }
@@ -1350,9 +1374,10 @@
1350
1374
  const renderer = getRenderer(type);
1351
1375
  if (!renderer)
1352
1376
  return;
1353
- const forWrapper = (container.ownerDocument || document).createElement("div");
1354
- forWrapper.className = "slexkit-for-wrapper";
1355
- container.appendChild(forWrapper);
1377
+ const doc = container.ownerDocument || document;
1378
+ const startAnchor = doc.createComment(`slexkit-for:${fullKey}:start`);
1379
+ const endAnchor = doc.createComment(`slexkit-for:${fullKey}:end`);
1380
+ container.append(startAnchor, endAnchor);
1356
1381
  const evalCtx = buildComponentEvalContext(g, components, componentTypes, api, forCtx);
1357
1382
  const items = createMemo(() => trackForCollection(evalRead(props.$for, evalCtx, ns, `${fullKey}:$for`)));
1358
1383
  const $keyProp = props.$key;
@@ -1365,8 +1390,10 @@
1365
1390
  disposedSlots.add(slot);
1366
1391
  leavingSlots.delete(slot);
1367
1392
  callHook(g, name, "onUnmount");
1368
- disposeComponent(slot.el);
1369
- slot.el.remove();
1393
+ if (slot.el) {
1394
+ disposeComponent(slot.el);
1395
+ slot.el.remove();
1396
+ }
1370
1397
  if (slot.dispose)
1371
1398
  slot.dispose();
1372
1399
  };
@@ -1391,12 +1418,16 @@
1391
1418
  }
1392
1419
  }
1393
1420
  for (const slot of deletedSlots) {
1394
- container.appendChild(slot.el);
1395
1421
  leavingSlots.add(slot);
1422
+ if (!slot.el) {
1423
+ disposeSlot(slot);
1424
+ continue;
1425
+ }
1396
1426
  applyLeaveAnimation(slot.el, slot.props, () => {
1397
1427
  disposeSlot(slot);
1398
1428
  });
1399
1429
  }
1430
+ let cursor = startAnchor;
1400
1431
  arr.forEach((item, index) => {
1401
1432
  item = asReactiveValue(item, g);
1402
1433
  const keyVal = newKeys[index];
@@ -1424,20 +1455,22 @@
1424
1455
  const indexSignal = createSignal(index);
1425
1456
  const revisionSignal = createSignal(0);
1426
1457
  slot = renderAndMountSlot(item, index, keyVal, indexSignal, revisionSignal, renderer, type, name, props, container, g, components, componentTypes, api, forCtx, ns, fullKey, options);
1427
- if (slot.el) {
1428
- applyEnterAnimation(slot.el, slot.props);
1429
- callHook(g, name, "onMount");
1458
+ if (!slot.el) {
1459
+ disposeSlot(slot);
1460
+ return;
1430
1461
  }
1462
+ applyEnterAnimation(slot.el, slot.props);
1463
+ callHook(g, name, "onMount");
1431
1464
  slotMap.set(keyVal, slot);
1432
1465
  }
1433
- const refChild = forWrapper.children[index];
1434
- if (slot.el && refChild !== slot.el) {
1435
- forWrapper.insertBefore(slot.el, refChild ?? null);
1466
+ const nextChild = cursor.nextSibling;
1467
+ if (slot.el && nextChild !== slot.el) {
1468
+ container.insertBefore(slot.el, nextChild ?? endAnchor);
1469
+ }
1470
+ if (slot.el) {
1471
+ cursor = slot.el;
1436
1472
  }
1437
1473
  });
1438
- while (forWrapper.children.length > arr.length) {
1439
- forWrapper.lastChild.remove();
1440
- }
1441
1474
  });
1442
1475
  onCleanup(() => {
1443
1476
  for (const slot of Array.from(slotMap.values()))
@@ -1445,7 +1478,8 @@
1445
1478
  slotMap.clear();
1446
1479
  for (const slot of Array.from(leavingSlots))
1447
1480
  disposeSlot(slot);
1448
- forWrapper.remove();
1481
+ startAnchor.remove();
1482
+ endAnchor.remove();
1449
1483
  });
1450
1484
  }
1451
1485
  function renderNormalNode(fullKey, props, container, g, components, componentTypes, api, forCtx, ns, options) {
@@ -1822,7 +1856,7 @@ ${parseSource}
1822
1856
  var parseSlexKitDsl = parseSlexSource;
1823
1857
 
1824
1858
  // src/version.ts
1825
- var SLEXKIT_VERSION = "0.3.0";
1859
+ var SLEXKIT_VERSION = "0.3.2";
1826
1860
  var SLEX_PROTOCOL_VERSION = "0.1";
1827
1861
  var SLEXKIT_COMPONENTS_VERSION = SLEXKIT_VERSION;
1828
1862
  function getSlexKitInfo() {
@@ -2950,7 +2984,6 @@ ${parseSource}
2950
2984
  min: { type: "string | number", dynamic: true, description: "Minimum value used by numeric input controls." },
2951
2985
  max: { type: "string | number", dynamic: true, description: "Maximum value used by numeric input controls." },
2952
2986
  step: { type: "string | number", dynamic: true, description: "Step size used by numeric input controls." },
2953
- controls: { type: "boolean", default: true, dynamic: true, description: "Show decrement and increment buttons for numeric inputs." },
2954
2987
  onchange: { type: "write-expression", description: "Write expression invoked when the value changes." }
2955
2988
  },
2956
2989
  children: noChildren,
@@ -3458,6 +3491,8 @@ ${parseSource}
3458
3491
  content: { type: "string", dynamic: true, description: "Alias for text." },
3459
3492
  label: { type: "string", dynamic: true, description: "Alias for text." },
3460
3493
  variant: { type: "string", values: ["default", "muted"], default: "default", description: "Text visual variant." },
3494
+ color: { type: "string", dynamic: true, description: "Optional CSS color for controlled previews." },
3495
+ size: { type: "string | number", dynamic: true, description: "Optional font size. Numbers are treated as px." },
3461
3496
  class: { type: "string", description: "Additional host-controlled CSS class." }
3462
3497
  },
3463
3498
  children: noChildren,
@@ -3828,7 +3863,11 @@ ${parseSource}
3828
3863
  return Object.keys(value).some((key) => key.includes(":"));
3829
3864
  }
3830
3865
  function bareLayoutFromSource(value) {
3831
- const { slex: _slex, namespace: _namespace, g: _g, layout: _layout, ...layout } = value;
3866
+ const layout = { ...value };
3867
+ delete layout.slex;
3868
+ delete layout.namespace;
3869
+ delete layout.g;
3870
+ delete layout.layout;
3832
3871
  return layout;
3833
3872
  }
3834
3873
  function isStateOnlySource(value) {
@@ -12599,6 +12638,7 @@ ${component_stack}
12599
12638
  reset(label2);
12600
12639
  reset(span);
12601
12640
  template_effect(($0, $1) => {
12641
+ set_attribute2(label2, "data-disabled", get2(p).disabled ? "true" : undefined);
12602
12642
  input.disabled = !!get2(p).disabled;
12603
12643
  set_attribute2(input, "data-state", get2(checked) ? "checked" : "unchecked");
12604
12644
  set_attribute2(input, "aria-label", $0);
@@ -12674,6 +12714,7 @@ ${component_stack}
12674
12714
  reset(span);
12675
12715
  template_effect(($0, $1) => {
12676
12716
  set_attribute2(label2, "data-state", get2(enabled) ? "on" : "off");
12717
+ set_attribute2(label2, "data-disabled", get2(p).disabled ? "true" : undefined);
12677
12718
  input.disabled = !!get2(p).disabled;
12678
12719
  set_attribute2(input, "aria-label", $0);
12679
12720
  set_text(text_1, $1);
@@ -12694,26 +12735,11 @@ ${component_stack}
12694
12735
  var nextInputId = 0;
12695
12736
  var root_14 = from_html(`<label class="slex-input-label"> </label>`);
12696
12737
  var root_2 = from_html(`<span class="slex-input-unit" aria-hidden="true"> </span>`);
12697
- var root_33 = from_html(`<span class="slex-input-controls"><button class="slex-input-step" type="button">+</button> <button class="slex-input-step" type="button">-</button></span>`);
12698
- var root_4 = from_html(`<div class="slex-input-description"> </div>`);
12699
- var root_52 = from_html(`<div class="slex-input-error" role="alert"> </div>`);
12700
- var root8 = from_html(`<div class="slex-input-field"><!> <div class="slex-input-control"><input class="slex-input"/> <!> <!></div> <!> <!></div>`);
12738
+ var root_33 = from_html(`<div class="slex-input-description"> </div>`);
12739
+ var root_4 = from_html(`<div class="slex-input-error" role="alert"> </div>`);
12740
+ var root8 = from_html(`<div class="slex-input-field"><!> <div class="slex-input-control"><input class="slex-input"/> <!></div> <!> <!></div>`);
12701
12741
  function Input($$anchor, $$props) {
12702
12742
  push($$props, true);
12703
- const engineeringPrefixFactors = {
12704
- p: 0.000000000001,
12705
- n: 0.000000001,
12706
- u: 0.000001,
12707
- "µ": 0.000001,
12708
- "碌": 0.000001,
12709
- m: 0.001,
12710
- k: 1000,
12711
- K: 1000,
12712
- M: 1e6,
12713
- meg: 1e6,
12714
- G: 1e9,
12715
- T: 1000000000000
12716
- };
12717
12743
  let p = state(proxy({}));
12718
12744
  let value = state("");
12719
12745
  const fallbackId = `slex-input-${++nextInputId}`;
@@ -12725,8 +12751,6 @@ ${component_stack}
12725
12751
  const readonly = user_derived(() => bool(get2(p).readonly) || bool(get2(p).readOnly));
12726
12752
  const required = user_derived(() => bool(get2(p).required));
12727
12753
  const invalid2 = user_derived(() => bool(get2(p).invalid) || !!get2(errorText));
12728
- const steppable = user_derived(isSteppableInput);
12729
- const controls = user_derived(() => get2(steppable) && get2(p).controls !== false && get2(p).controls !== "false");
12730
12754
  const componentId = user_derived(() => safeId($$props.componentName));
12731
12755
  const inputId = user_derived(() => text2(get2(p).id) || (get2(componentId) ? `slex-input-${get2(componentId)}` : fallbackId));
12732
12756
  const descriptionId = user_derived(() => `${get2(inputId)}-description`);
@@ -12737,10 +12761,6 @@ ${component_stack}
12737
12761
  get2(descriptionText) ? get2(descriptionId) : "",
12738
12762
  get2(errorText) ? get2(errorId) : ""
12739
12763
  ].filter(Boolean).join(" "));
12740
- const numericValue = user_derived(readNumericValue);
12741
- const controlLabel = user_derived(() => get2(labelText) || text2(get2(p).placeholder) || $$props.componentName || "input");
12742
- const decrementDisabled = user_derived(() => !canStep(-1));
12743
- const incrementDisabled = user_derived(() => !canStep(1));
12744
12764
  user_effect(() => bindPropStore($$props.props, (next2) => {
12745
12765
  set(p, next2, true);
12746
12766
  set(value, text2(next2.value), true);
@@ -12751,72 +12771,6 @@ ${component_stack}
12751
12771
  function inputType() {
12752
12772
  return text2(get2(p).type, "text") === "engineering" ? "text" : text2(get2(p).type, "text");
12753
12773
  }
12754
- function isSteppableInput() {
12755
- const kind = text2(get2(p).type, "text");
12756
- return kind === "number" || kind === "engineering" || get2(p).min !== undefined || get2(p).max !== undefined || get2(p).step !== undefined;
12757
- }
12758
- function numericProp(input) {
12759
- if (input === undefined || input === null || input === "")
12760
- return;
12761
- const next2 = Number(input);
12762
- return Number.isFinite(next2) ? next2 : undefined;
12763
- }
12764
- function stepSize() {
12765
- const next2 = numericProp(get2(p).step);
12766
- if (next2 !== undefined && next2 > 0)
12767
- return next2;
12768
- if (text2(get2(p).type, "text") !== "engineering")
12769
- return 1;
12770
- const parsed = parseEngineeringNumber(get2(value));
12771
- if (!parsed.valid || !parsed.prefix)
12772
- return 1;
12773
- return engineeringPrefixFactors[parsed.prefix] ?? 1;
12774
- }
12775
- function readNumericValue() {
12776
- if (text2(get2(p).type, "text") === "engineering") {
12777
- const parsed = parseEngineeringNumber(get2(value));
12778
- return parsed.valid && parsed.number !== null ? parsed.number : null;
12779
- }
12780
- const next2 = Number(get2(value));
12781
- return Number.isFinite(next2) ? next2 : null;
12782
- }
12783
- function clamp(next2) {
12784
- const min = numericProp(get2(p).min);
12785
- const max = numericProp(get2(p).max);
12786
- let clamped = next2;
12787
- if (min !== undefined)
12788
- clamped = Math.max(min, clamped);
12789
- if (max !== undefined)
12790
- clamped = Math.min(max, clamped);
12791
- return clamped;
12792
- }
12793
- function canStep(direction) {
12794
- if (!get2(controls) || get2(disabled) || get2(readonly) || get2(numericValue) === null)
12795
- return false;
12796
- return clamp(get2(numericValue) + direction * stepSize()) !== get2(numericValue);
12797
- }
12798
- function formatSteppedValue(next2) {
12799
- if (text2(get2(p).type, "text") !== "engineering")
12800
- return text2(next2);
12801
- const parsed = parseEngineeringNumber(get2(value));
12802
- if (!parsed.valid)
12803
- return text2(next2);
12804
- const factor = parsed.prefix ? engineeringPrefixFactors[parsed.prefix] : undefined;
12805
- const visibleNumber = factor ? next2 / factor : next2;
12806
- return `${formatNumber2(visibleNumber)}${parsed.prefix}${parsed.unit}`;
12807
- }
12808
- function formatNumber2(next2) {
12809
- return text2(Number(next2.toPrecision(12)));
12810
- }
12811
- function emitValue(nextValue) {
12812
- set(value, nextValue, true);
12813
- emit($$props.ctx, "change", text2(get2(p).type, "text") === "engineering" ? parseEngineeringNumber(get2(value)) : get2(value));
12814
- }
12815
- function stepBy(direction) {
12816
- if (!canStep(direction) || get2(numericValue) === null)
12817
- return;
12818
- emitValue(formatSteppedValue(clamp(get2(numericValue) + direction * stepSize())));
12819
- }
12820
12774
  function update2(event2) {
12821
12775
  if (get2(disabled) || get2(readonly))
12822
12776
  return;
@@ -12842,9 +12796,9 @@ ${component_stack}
12842
12796
  });
12843
12797
  }
12844
12798
  var div_1 = sibling(node, 2);
12845
- var input_1 = child(div_1);
12846
- remove_input_defaults(input_1);
12847
- var node_1 = sibling(input_1, 2);
12799
+ var input = child(div_1);
12800
+ remove_input_defaults(input);
12801
+ var node_1 = sibling(input, 2);
12848
12802
  {
12849
12803
  var consequent_1 = ($$anchor2) => {
12850
12804
  var span = root_2();
@@ -12858,33 +12812,11 @@ ${component_stack}
12858
12812
  $$render(consequent_1);
12859
12813
  });
12860
12814
  }
12861
- var node_2 = sibling(node_1, 2);
12862
- {
12863
- var consequent_2 = ($$anchor2) => {
12864
- var span_1 = root_33();
12865
- var button = child(span_1);
12866
- var button_1 = sibling(button, 2);
12867
- reset(span_1);
12868
- template_effect(() => {
12869
- set_attribute2(button, "aria-label", `Increase ${get2(controlLabel)}`);
12870
- button.disabled = get2(incrementDisabled);
12871
- set_attribute2(button_1, "aria-label", `Decrease ${get2(controlLabel)}`);
12872
- button_1.disabled = get2(decrementDisabled);
12873
- });
12874
- delegated("click", button, () => stepBy(1));
12875
- delegated("click", button_1, () => stepBy(-1));
12876
- append($$anchor2, span_1);
12877
- };
12878
- if_block(node_2, ($$render) => {
12879
- if (get2(controls))
12880
- $$render(consequent_2);
12881
- });
12882
- }
12883
12815
  reset(div_1);
12884
- var node_3 = sibling(div_1, 2);
12816
+ var node_2 = sibling(div_1, 2);
12885
12817
  {
12886
- var consequent_3 = ($$anchor2) => {
12887
- var div_2 = root_4();
12818
+ var consequent_2 = ($$anchor2) => {
12819
+ var div_2 = root_33();
12888
12820
  var text_3 = child(div_2, true);
12889
12821
  reset(div_2);
12890
12822
  template_effect(() => {
@@ -12893,15 +12825,15 @@ ${component_stack}
12893
12825
  });
12894
12826
  append($$anchor2, div_2);
12895
12827
  };
12896
- if_block(node_3, ($$render) => {
12828
+ if_block(node_2, ($$render) => {
12897
12829
  if (get2(descriptionText))
12898
- $$render(consequent_3);
12830
+ $$render(consequent_2);
12899
12831
  });
12900
12832
  }
12901
- var node_4 = sibling(node_3, 2);
12833
+ var node_3 = sibling(node_2, 2);
12902
12834
  {
12903
- var consequent_4 = ($$anchor2) => {
12904
- var div_3 = root_52();
12835
+ var consequent_3 = ($$anchor2) => {
12836
+ var div_3 = root_4();
12905
12837
  var text_4 = child(div_3, true);
12906
12838
  reset(div_3);
12907
12839
  template_effect(() => {
@@ -12910,9 +12842,9 @@ ${component_stack}
12910
12842
  });
12911
12843
  append($$anchor2, div_3);
12912
12844
  };
12913
- if_block(node_4, ($$render) => {
12845
+ if_block(node_3, ($$render) => {
12914
12846
  if (get2(errorText))
12915
- $$render(consequent_4);
12847
+ $$render(consequent_3);
12916
12848
  });
12917
12849
  }
12918
12850
  reset(div);
@@ -12921,21 +12853,20 @@ ${component_stack}
12921
12853
  set_attribute2(div, "data-required", get2(required) ? "true" : undefined);
12922
12854
  set_attribute2(div, "data-readonly", get2(readonly) ? "true" : undefined);
12923
12855
  set_attribute2(div_1, "data-has-unit", get2(unitText) ? "true" : undefined);
12924
- set_attribute2(div_1, "data-has-controls", get2(controls) ? "true" : undefined);
12925
- set_attribute2(input_1, "id", get2(inputId));
12926
- set_attribute2(input_1, "type", $0);
12927
- set_attribute2(input_1, "inputmode", $1);
12928
- set_attribute2(input_1, "name", $2);
12929
- set_attribute2(input_1, "placeholder", $3);
12930
- input_1.disabled = get2(disabled);
12931
- input_1.readOnly = get2(readonly);
12932
- input_1.required = get2(required);
12933
- set_attribute2(input_1, "min", $4);
12934
- set_attribute2(input_1, "max", $5);
12935
- set_attribute2(input_1, "step", $6);
12936
- set_attribute2(input_1, "aria-label", get2(computedAriaLabel) || undefined);
12937
- set_attribute2(input_1, "aria-describedby", get2(describedBy) || undefined);
12938
- set_attribute2(input_1, "aria-invalid", get2(invalid2) ? "true" : undefined);
12856
+ set_attribute2(input, "id", get2(inputId));
12857
+ set_attribute2(input, "type", $0);
12858
+ set_attribute2(input, "inputmode", $1);
12859
+ set_attribute2(input, "name", $2);
12860
+ set_attribute2(input, "placeholder", $3);
12861
+ input.disabled = get2(disabled);
12862
+ input.readOnly = get2(readonly);
12863
+ input.required = get2(required);
12864
+ set_attribute2(input, "min", $4);
12865
+ set_attribute2(input, "max", $5);
12866
+ set_attribute2(input, "step", $6);
12867
+ set_attribute2(input, "aria-label", get2(computedAriaLabel) || undefined);
12868
+ set_attribute2(input, "aria-describedby", get2(describedBy) || undefined);
12869
+ set_attribute2(input, "aria-invalid", get2(invalid2) ? "true" : undefined);
12939
12870
  }, [
12940
12871
  () => inputType(),
12941
12872
  () => text2(get2(p).type, "text") === "engineering" ? "decimal" : undefined,
@@ -12945,12 +12876,12 @@ ${component_stack}
12945
12876
  () => get2(p).max === undefined ? undefined : text2(get2(p).max),
12946
12877
  () => get2(p).step === undefined ? undefined : text2(get2(p).step)
12947
12878
  ]);
12948
- delegated("input", input_1, update2);
12949
- bind_value(input_1, () => get2(value), ($$value) => set(value, $$value));
12879
+ delegated("input", input, update2);
12880
+ bind_value(input, () => get2(value), ($$value) => set(value, $$value));
12950
12881
  append($$anchor, div);
12951
12882
  pop();
12952
12883
  }
12953
- delegate(["input", "click"]);
12884
+ delegate(["input"]);
12954
12885
 
12955
12886
  // src/components/svelte/input/RadioGroup.svelte
12956
12887
  var root_34 = from_html(`<span> </span>`);
@@ -13066,6 +12997,7 @@ ${component_stack}
13066
12997
  reset(label2);
13067
12998
  reset(span_1);
13068
12999
  template_effect(($0, $1, $2) => {
13000
+ set_attribute2(label2, "data-disabled", get2(disabled) ? "true" : undefined);
13069
13001
  set_attribute2(input, "name", $0);
13070
13002
  set_value(input, $1);
13071
13003
  set_checked(input, get2(itemValue) === get2(value));
@@ -13101,7 +13033,7 @@ ${component_stack}
13101
13033
  var root_16 = from_html(`<label class="slex-select-label"><!> <!></label>`);
13102
13034
  var root_8 = from_html(`<span class="slex-select-check" aria-hidden="true"></span>`);
13103
13035
  var root_6 = from_html(`<li role="option"><span class="slex-select-option-label"><!> <span> </span></span> <!></li>`);
13104
- var root_53 = from_html(`<ul class="slex-select-menu" role="listbox"></ul>`);
13036
+ var root_52 = from_html(`<ul class="slex-select-menu" role="listbox"></ul>`);
13105
13037
  var root_9 = from_html(`<option> </option>`);
13106
13038
  var root10 = from_html(`<div class="slex-select"><!> <button type="button" class="slex-select-trigger" aria-haspopup="listbox"><span class="slex-select-value"><!> <span> </span></span> <span class="slex-select-icon" aria-hidden="true"></span></button> <!> <select class="slex-select-native" tabindex="-1" aria-hidden="true"><option> </option><!></select></div>`);
13107
13039
  function Select($$anchor, $$props) {
@@ -13305,7 +13237,7 @@ ${component_stack}
13305
13237
  var node_4 = sibling(button, 2);
13306
13238
  {
13307
13239
  var consequent_6 = ($$anchor2) => {
13308
- var ul = root_53();
13240
+ var ul = root_52();
13309
13241
  each(ul, 21, () => get2(options), index, ($$anchor3, item, index2) => {
13310
13242
  const isSelected = user_derived(() => get2(item).value === text2(get2(value)));
13311
13243
  const isActive = user_derived(() => index2 === get2(activeIndex));
@@ -16156,7 +16088,7 @@ Migration example: ${hintLines.join(" ")}`;
16156
16088
  delegate(["click"]);
16157
16089
 
16158
16090
  // src/components/svelte/input/Tabs.svelte
16159
- var root_54 = from_html(`<span class="slex-sr-only"> </span>`);
16091
+ var root_53 = from_html(`<span class="slex-sr-only"> </span>`);
16160
16092
  var root_36 = from_html(`<!> <!>`, 1);
16161
16093
  var root_82 = from_html(`<div></div>`);
16162
16094
  var root_18 = from_html(`<!> <span class="slex-tabs-selected-indicator" aria-hidden="true"></span>`, 1);
@@ -16334,7 +16266,7 @@ Migration example: ${hintLines.join(" ")}`;
16334
16266
  var node_4 = sibling(node_3, 2);
16335
16267
  {
16336
16268
  var consequent_1 = ($$anchor5) => {
16337
- var span = root_54();
16269
+ var span = root_53();
16338
16270
  var text_1 = child(span, true);
16339
16271
  reset(span);
16340
16272
  template_effect(() => set_text(text_1, get2(label2)));
@@ -16774,7 +16706,7 @@ Migration example: ${hintLines.join(" ")}`;
16774
16706
  var root_111 = from_html(`<button><!> <!></button>`);
16775
16707
  var root_62 = from_html(`<span class="sr-only"> </span>`);
16776
16708
  var root_83 = from_svg(`<svg fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>`);
16777
- var root_55 = from_html(`<a><!> <!></a>`);
16709
+ var root_54 = from_html(`<a><!> <!></a>`);
16778
16710
  function CloseButton($$anchor, $$props) {
16779
16711
  push($$props, true);
16780
16712
  let color = prop($$props, "color", 3, "gray"), name = prop($$props, "name", 3, "Close"), size = prop($$props, "size", 3, "md"), restProps = rest_props($$props, [
@@ -16848,7 +16780,7 @@ Migration example: ${hintLines.join(" ")}`;
16848
16780
  append($$anchor2, button);
16849
16781
  };
16850
16782
  var alternate_2 = ($$anchor2) => {
16851
- var a = root_55();
16783
+ var a = root_54();
16852
16784
  attribute_effect(a, ($0) => ({
16853
16785
  ...restProps,
16854
16786
  onclick,
@@ -17481,11 +17413,21 @@ Migration example: ${hintLines.join(" ")}`;
17481
17413
  push($$props, true);
17482
17414
  let p = state(proxy({}));
17483
17415
  user_effect(() => bindPropStore($$props.props, (next2) => set(p, next2, true)));
17416
+ const color = user_derived(() => get2(p).color == null || get2(p).color === "" ? undefined : text2(get2(p).color));
17417
+ const size = user_derived(() => cssLength(get2(p).size));
17418
+ function cssLength(value) {
17419
+ if (value == null || value === "")
17420
+ return;
17421
+ const rendered = text2(value);
17422
+ return /^-?\d+(\.\d+)?$/.test(rendered) ? `${rendered}px` : rendered;
17423
+ }
17484
17424
  var div = root20();
17425
+ let styles;
17485
17426
  var text_1 = child(div, true);
17486
17427
  reset(div);
17487
17428
  template_effect(($0, $1) => {
17488
17429
  set_class(div, 1, $0);
17430
+ styles = set_style(div, "", styles, { color: get2(color), "font-size": get2(size) });
17489
17431
  set_text(text_1, $1);
17490
17432
  }, [
17491
17433
  () => `slex-text${get2(p).variant ? ` slex-text--${text2(get2(p).variant)}` : ""}${get2(p).class ? ` ${text2(get2(p).class)}` : ""}`,
@@ -17657,7 +17599,7 @@ Migration example: ${hintLines.join(" ")}`;
17657
17599
  var root_45 = from_html(`<span class="slex-code-block-language"> </span>`);
17658
17600
  var root_114 = from_html(`<figcaption class="slex-code-block-header"><span class="slex-code-block-title"><!> <!></span> <!></figcaption>`);
17659
17601
  var root_72 = from_html(`<span> </span>`);
17660
- var root_56 = from_html(`<span class="slex-code-line"><span class="slex-code-line-content"></span></span>`);
17602
+ var root_55 = from_html(`<span class="slex-code-line"><span class="slex-code-line-content"></span></span>`);
17661
17603
  var root22 = from_html(`<figure class="slex-code-block"><!> <pre class="slex-code-block-pre"><code></code></pre></figure>`);
17662
17604
  function CodeBlock($$anchor, $$props) {
17663
17605
  push($$props, true);
@@ -17826,7 +17768,7 @@ Migration example: ${hintLines.join(" ")}`;
17826
17768
  var pre = sibling(node, 2);
17827
17769
  var code_1 = child(pre);
17828
17770
  each(code_1, 21, () => get2(lines), index, ($$anchor2, line) => {
17829
- var span_3 = root_56();
17771
+ var span_3 = root_55();
17830
17772
  var span_4 = child(span_3);
17831
17773
  each(span_4, 21, () => get2(line), index, ($$anchor3, token) => {
17832
17774
  var fragment_1 = comment();
@@ -31966,7 +31908,7 @@ l0,-` + (midHeight + 144) + `c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949
31966
31908
 
31967
31909
  // src/components/svelte/content/Section.svelte
31968
31910
  var root_28 = from_html(`<div class="slex-section-eyebrow"> </div>`);
31969
- var root_57 = from_html(`<span> </span>`);
31911
+ var root_56 = from_html(`<span> </span>`);
31970
31912
  var root_313 = from_html(`<h2 class="slex-section-title"><!><!></h2>`);
31971
31913
  var root_65 = from_html(`<p class="slex-section-subtitle"> </p>`);
31972
31914
  var root_73 = from_html(`<a class="slex-section-action"> </a>`);
@@ -32017,7 +31959,7 @@ l0,-` + (midHeight + 144) + `c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949
32017
31959
  var node_4 = sibling(node_3);
32018
31960
  {
32019
31961
  var consequent_2 = ($$anchor4) => {
32020
- var span = root_57();
31962
+ var span = root_56();
32021
31963
  var text_2 = child(span, true);
32022
31964
  reset(span);
32023
31965
  template_effect(($0) => set_text(text_2, $0), [() => text2(get2(p).title)]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slexkit",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Zero-build, Markdown-friendly reactive UI runtime for AI output.",
5
5
  "author": "SlexKit contributors",
6
6
  "type": "module",
@@ -81,7 +81,7 @@
81
81
  ],
82
82
  "scripts": {
83
83
  "dev": "bun run site/server.ts",
84
- "build": "bun run build:core && bun run --filter @slexkit/streamdown build && bun run --filter @slexkit/obsidian build && bun run --filter @slexkit/mcp build",
84
+ "build": "bun run build:core && bun run --filter @slexkit/streamdown build && bun run --filter @slexkit/mcp build",
85
85
  "build:core": "bun run scripts/build-core.ts",
86
86
  "ai:docs": "bun run scripts/generate-ai-docs.ts",
87
87
  "version:sync": "bun run scripts/sync-version.ts",
@@ -120,6 +120,7 @@
120
120
  "flowbite": "^4.0.2",
121
121
  "flowbite-svelte": "^1.33.1",
122
122
  "katex": "^0.17.0",
123
+ "marked": "^18.0.5",
123
124
  "svelte": "^5.55.9",
124
125
  "svelte-highlight": "^7.9.0"
125
126
  },
@@ -21,7 +21,7 @@ Use this skill as `/host` when adding SlexKit to a host application or documenta
21
21
 
22
22
  - Vanilla host: `createSlexKitMarkdownRuntimeHost`.
23
23
  - React/Streamdown: `@slexkit/streamdown`.
24
- - Obsidian: `@slexkit/obsidian`, readonly fenced block rendering.
24
+ - Obsidian: install the official SlexKit plugin from Obsidian Community Plugins; release repo `slexkit/obsidian-slexkit`.
25
25
  - Custom host: detect the `slex` code fence, pass source plus artifact id to the runtime host, dispose when the block is removed.
26
26
 
27
27
  ## Rules
@@ -6,6 +6,19 @@
6
6
  let { props }: SvelteComponentProps = $props();
7
7
  let p = $state<PropValues>({});
8
8
  $effect(() => bindPropStore(props, (next) => (p = next)));
9
+
10
+ const color = $derived(p.color == null || p.color === "" ? undefined : text(p.color));
11
+ const size = $derived(cssLength(p.size));
12
+
13
+ function cssLength(value: unknown): string | undefined {
14
+ if (value == null || value === "") return undefined;
15
+ const rendered = text(value);
16
+ return /^-?\d+(\.\d+)?$/.test(rendered) ? `${rendered}px` : rendered;
17
+ }
9
18
  </script>
10
19
 
11
- <div class={`slex-text${p.variant ? ` slex-text--${text(p.variant)}` : ""}${p.class ? ` ${text(p.class)}` : ""}`}>{text(p.content ?? p.text ?? p.label)}</div>
20
+ <div
21
+ class={`slex-text${p.variant ? ` slex-text--${text(p.variant)}` : ""}${p.class ? ` ${text(p.class)}` : ""}`}
22
+ style:color={color}
23
+ style:font-size={size}
24
+ >{text(p.content ?? p.text ?? p.label)}</div>
@@ -34,7 +34,7 @@
34
34
  </script>
35
35
 
36
36
  <span class="slex-choice-event-layer" onpointerdown={() => haptic(8)} onclick={() => haptic(8)}>
37
- <label class="slex-checkbox-field">
37
+ <label class="slex-checkbox-field" data-disabled={p.disabled ? "true" : undefined}>
38
38
  <input
39
39
  type="checkbox"
40
40
  class="slex-checkbox"