stimeo-ui 0.18.0 → 0.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05c2871de0e4c97223dfb10803e8c4fa84abc3a0cf5e9c946d6570d88c46f065
4
- data.tar.gz: a772dc1b21e16d21ea37af8dbb2f6a56618fa94252fe43eebcbede4ab296c2a2
3
+ metadata.gz: 54a0ca530097c890ee9b7292939afa617a822905abb0c8b93ee841c9bbc196b8
4
+ data.tar.gz: 7ffbb49e1e7c617aadd557a774d0d88fd4d79361d56aa3ea3b7e656d492e2adf
5
5
  SHA512:
6
- metadata.gz: a74a9a638fadb04fcec7c72ced80825fb75215051533508d2e58c40fc64ef4eb3aa41a0b76ee85ab9697004f9861d6b6a46b75ec62c39b013d274487f7986d42
7
- data.tar.gz: 2b0ff0e66bc0f7062c241ee01032888206b300eb74c552561c3638638a9255a19ea69b1a9555fa5e72b50ab1e8bd8479f9b37f8e79743077df6da59ee65e2409
6
+ metadata.gz: 5b1c8cde31c41ff03270c5f8e5d94cfe75b453eaf89d0bd6dcbca1bf095a360b7669e2012860812c5b7b4fa0e363cba5c6dbb156419f971ba042af8c46dff4d7
7
+ data.tar.gz: 1ced263a41d46a475685c30b0e636b14fe6307aa310ce7d80ec5fd645662c6eb16d1af05c6dc1a3d006cb1da0d5506af14072e1706d2ddff932e82a2b6260847
data/CHANGELOG.md CHANGED
@@ -7,7 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
  While the version is `0.x`, the public API (the `stimeo--*` data attributes) may
8
8
  change between releases.
9
9
 
10
- ## [Unreleased]
10
+ ## [0.18.1] - 2026-10-31
11
+
12
+ Patch release focused on keyboard ownership, focus handoff, masked selections,
13
+ and Inspector compatibility.
14
+
15
+ ### Changed
16
+
17
+ - Inspector host contracts can admit the `<input>` types a controller composes
18
+ with instead of replacing (`inputTypes`); any other `<input>` on such a host is
19
+ still reported as `invalid-host`. The manifest carries this as `schemaVersion`
20
+ 17. Update Inspector engines and the VS Code extension together with their
21
+ manifest; an older engine reading it may report an admitted input as
22
+ `invalid-host`.
23
+ - Refresh pinned build and test tooling: Biome 2.6.3, happy-dom 21.0.4,
24
+ tsup 9.0.0, and Rollup 4.66.0.
25
+
26
+ ### Fixed
27
+
28
+ - command-palette and editable: respect keyboard events already handled by another
29
+ listener, including global shortcuts and F2.
30
+ - multi-select: close on Tab from a chip and keep the list closed when that Tab
31
+ reaches the input.
32
+ - popover: close when focus reaches an unrelated control after first falling to
33
+ the document body, while preserving the destination focus.
34
+ - dismissible: release focus from hidden content when no fallback can receive
35
+ focus.
36
+ - input-mask: preserve selected text when a single invalid typed character would
37
+ replace it.
38
+ - pointer-drag: cancel active sessions when a handle loses its target declaration
39
+ and restore the fallback handle when no declared handles remain.
11
40
 
12
41
  ## [0.18.0] - 2026-10-22
13
42
 
@@ -1411,7 +1440,7 @@ Initial public alpha: 101 behavior-only, accessible Stimulus controllers driven
1411
1440
  by `data-*` attributes, shipping no CSS. Published to npm (with provenance) and
1412
1441
  RubyGems.
1413
1442
 
1414
- [Unreleased]: https://github.com/taiyaky/stimeo-ui/compare/v0.18.0...HEAD
1443
+ [0.18.1]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.18.1
1415
1444
  [0.18.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.18.0
1416
1445
  [0.17.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.17.0
1417
1446
  [0.16.0]: https://github.com/taiyaky/stimeo-ui/releases/tag/v0.16.0
@@ -1629,6 +1629,7 @@ var CommandPaletteController = class _CommandPaletteController extends Controlle
1629
1629
  return this.hasDialogTarget && !this.dialogTarget.hidden;
1630
1630
  }
1631
1631
  #onGlobalKeydown = (event) => {
1632
+ if (event.defaultPrevented) return;
1632
1633
  if (this.#composition.isComposing(event) || !this.#matchesHotkey(event)) return;
1633
1634
  event.preventDefault();
1634
1635
  this.toggle();
@@ -323,6 +323,8 @@ var DismissibleController = class extends Controller {
323
323
  if (document.activeElement === candidate) return;
324
324
  }
325
325
  document.body.focus();
326
+ const remaining = document.activeElement;
327
+ if (remaining instanceof HTMLElement && root.contains(remaining)) remaining.blur();
326
328
  }
327
329
  *#focusFallbacks(root) {
328
330
  const explicitFallback = this.hasFallbackTarget ? this.fallbackTarget : null;
@@ -420,6 +420,7 @@ var EditableController = class extends Controller {
420
420
  this.#revertValue = null;
421
421
  }
422
422
  onDisplayKeydown(event) {
423
+ if (event.defaultPrevented) return;
423
424
  if (event.key === "F2") {
424
425
  event.preventDefault();
425
426
  this.edit();
@@ -590,6 +590,27 @@ var InputMaskController = class extends Controller {
590
590
  #textBeforeComposition = "";
591
591
  #formatted = null;
592
592
  #shown = { result: null, empty: true };
593
+ #edits = new ListenerSet();
594
+ #onBeforeInput = (event) => {
595
+ const data = event.data;
596
+ if (event.defaultPrevented || !event.cancelable || event.inputType !== "insertText" || data === null || Array.from(data).length !== 1 || !this.#started || this.#composition.isComposing(event))
597
+ return;
598
+ const input = this.element;
599
+ const start = input.selectionStart;
600
+ const end = input.selectionEnd;
601
+ if (start === null || end === null || start === end || input.readOnly || input.disabled || inheritsFieldsetDisabled(input) || !input.isConnected || !this.application.element.contains(input) || !(input.getAttribute("data-controller") ?? "").split(/\s+/).includes(this.identifier) || this.patternValue === "" || this.#formatted !== this.#declared || input.value !== this.#lastValue)
602
+ return;
603
+ const prefix = applyMask(input.value.slice(0, start), this.patternValue, this.#tokens);
604
+ for (const character of Array.from(this.patternValue).slice(prefix.tokenFlags.length)) {
605
+ const token = this.#tokens.get(character);
606
+ if (token) {
607
+ if (acceptedForm(token, data) === null) event.preventDefault();
608
+ return;
609
+ }
610
+ if (halfWidthChar(data) === halfWidthChar(character)) return;
611
+ }
612
+ event.preventDefault();
613
+ };
593
614
  #composition = new CompositionTracker({
594
615
  onStart: () => {
595
616
  this.#textBeforeComposition = this.element.value;
@@ -625,6 +646,7 @@ var InputMaskController = class extends Controller {
625
646
  this.#morph.observe(this.element);
626
647
  this.#started = true;
627
648
  this.#composition.observe(this.element);
649
+ this.#edits.add(this.element, "beforeinput", this.#onBeforeInput);
628
650
  if (!this.unmaskToHiddenValue) this.#withdraw();
629
651
  if (this.#composition.isComposing()) {
630
652
  this.#reformat("reconcile");
@@ -639,6 +661,7 @@ var InputMaskController = class extends Controller {
639
661
  disconnect() {
640
662
  this.#morph.disconnect();
641
663
  this.#started = false;
664
+ this.#edits.dispose();
642
665
  this.#composition.disconnect();
643
666
  this.#adoption.clearAll();
644
667
  this.#gate.disconnected(this, () => this.#retractRaw());
@@ -1118,7 +1118,9 @@ var MultiSelectController = class _MultiSelectController extends Controller {
1118
1118
  focusAfterEnd: () => this.#focusInput()
1119
1119
  });
1120
1120
  #listeners = new ListenerSet();
1121
+ #tab = null;
1121
1122
  connect() {
1123
+ this.#tab = null;
1122
1124
  this.#tabindex.reclaim(this.element);
1123
1125
  this.#rows.connect();
1124
1126
  this.#normalizeSelection([]);
@@ -1130,6 +1132,9 @@ var MultiSelectController = class _MultiSelectController extends Controller {
1130
1132
  this.#selectionValues = this.#values;
1131
1133
  this.#syncFields();
1132
1134
  this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
1135
+ this.#listeners.add(this.element, "keydown", this.#onTab);
1136
+ for (const type of ["focus", "keydown", "keyup", "pointerdown"])
1137
+ this.#listeners.add(document, type, this.#trackTab, { capture: true });
1133
1138
  this.#connected = true;
1134
1139
  this.#reconcile.observe(this.element);
1135
1140
  }
@@ -1151,6 +1156,7 @@ var MultiSelectController = class _MultiSelectController extends Controller {
1151
1156
  this.#composition.disconnect();
1152
1157
  this.#chipRow.disconnect();
1153
1158
  this.#listeners.dispose();
1159
+ this.#tab = null;
1154
1160
  this.#releaseTabindex();
1155
1161
  }
1156
1162
  optionTargetConnected() {
@@ -1320,7 +1326,11 @@ var MultiSelectController = class _MultiSelectController extends Controller {
1320
1326
  this.#setActive(visible[0] ?? null);
1321
1327
  this.dispatch("filter", { detail: { query } });
1322
1328
  }
1323
- open() {
1329
+ open(event) {
1330
+ if (event instanceof FocusEvent && event === this.#tab) {
1331
+ this.#tab = null;
1332
+ return;
1333
+ }
1324
1334
  if (!this.hasListTarget || !this.hasInputTarget) {
1325
1335
  this.#reflectEmpty();
1326
1336
  return;
@@ -1407,6 +1417,16 @@ var MultiSelectController = class _MultiSelectController extends Controller {
1407
1417
  break;
1408
1418
  }
1409
1419
  }
1420
+ #onTab = (event) => {
1421
+ if (event.key !== "Tab" || event.defaultPrevented || event.isComposing || !this.hasTagsTarget || !this.tagsTarget.contains(event.target))
1422
+ return;
1423
+ this.#tab = event;
1424
+ this.close();
1425
+ };
1426
+ #trackTab = (event) => {
1427
+ const key = this.#tab;
1428
+ this.#tab = event.type === "focus" && key instanceof KeyboardEvent && !key.defaultPrevented && event.target === this.inputTargets[0] ? event : null;
1429
+ };
1410
1430
  toggleOption(source) {
1411
1431
  const { event, host, origin, reason } = actionSource(source);
1412
1432
  if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
@@ -408,6 +408,7 @@ var PointerDragController = class _PointerDragController extends Controller {
408
408
  #tabindex = new TabindexLoan("0", this.identifier, { preserveFocus: true });
409
409
  #pointer = null;
410
410
  #keyboard = null;
411
+ #connected = false;
411
412
  #keyboardFocusSession = null;
412
413
  #listeners = new ListenerSet();
413
414
  #shadowFocus = new ListenerSet();
@@ -421,6 +422,7 @@ var PointerDragController = class _PointerDragController extends Controller {
421
422
  this.#reflectSession();
422
423
  });
423
424
  connect() {
425
+ this.#connected = true;
424
426
  this.#morphRender.observe(this.element);
425
427
  if (!this.#pointer && !this.#keyboard) this.#followOutput = null;
426
428
  this.#gate.cancel();
@@ -438,6 +440,7 @@ var PointerDragController = class _PointerDragController extends Controller {
438
440
  }
439
441
  }
440
442
  disconnect() {
443
+ this.#connected = false;
441
444
  this.#morphRender.disconnect();
442
445
  this.#listeners.dispose();
443
446
  this.#shadowFocus.dispose();
@@ -464,7 +467,9 @@ var PointerDragController = class _PointerDragController extends Controller {
464
467
  }
465
468
  handleTargetDisconnected(handle) {
466
469
  this.#restoreHandle(handle);
467
- if (this.element.contains(handle)) return;
470
+ if (!this.#connected) return;
471
+ if (this.element.contains(handle) && (this.handleTargets.includes(handle) || DetachGate.isDetached(this)))
472
+ return;
468
473
  const interrupted = this.#pointer?.handle === handle && this.#pointer.started ? this.#pointer.pointerType : this.#keyboard?.handle === handle ? "keyboard" : null;
469
474
  if (this.#pointer?.handle === handle) this.#endPointerSession();
470
475
  if (this.#keyboard?.handle === handle) this.#clearKeyboardSession();
@@ -369,6 +369,7 @@ var PopoverController = class extends Controller {
369
369
  this.#close("api");
370
370
  this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
371
371
  this.#listeners.add(this.element, "focusout", this.#onFocusOut);
372
+ this.#listeners.add(document, "focusin", this.#onDocumentFocusIn, { capture: true });
372
373
  this.#connected = true;
373
374
  this.#reporting = true;
374
375
  }
@@ -475,6 +476,14 @@ var PopoverController = class extends Controller {
475
476
  if (!(next instanceof Node) || this.element.contains(next)) return;
476
477
  this.#close("focus");
477
478
  };
479
+ #onDocumentFocusIn = (event) => {
480
+ if (!this.#closable) return;
481
+ const target = event.target;
482
+ if (!(target instanceof Node) || target === document.body || this.element.contains(target)) {
483
+ return;
484
+ }
485
+ this.#close("focus");
486
+ };
478
487
  get #isOpen() {
479
488
  return this.hasPanelTarget && !this.panelTarget.hidden;
480
489
  }
data/dist/index.js CHANGED
@@ -5735,6 +5735,7 @@ var CommandPaletteController = class _CommandPaletteController extends Controlle
5735
5735
  return this.hasDialogTarget && !this.dialogTarget.hidden;
5736
5736
  }
5737
5737
  #onGlobalKeydown = (event) => {
5738
+ if (event.defaultPrevented) return;
5738
5739
  if (this.#composition.isComposing(event) || !this.#matchesHotkey(event)) return;
5739
5740
  event.preventDefault();
5740
5741
  this.toggle();
@@ -9037,6 +9038,8 @@ var DismissibleController = class extends Controller {
9037
9038
  if (document.activeElement === candidate) return;
9038
9039
  }
9039
9040
  document.body.focus();
9041
+ const remaining = document.activeElement;
9042
+ if (remaining instanceof HTMLElement && root.contains(remaining)) remaining.blur();
9040
9043
  }
9041
9044
  *#focusFallbacks(root) {
9042
9045
  const explicitFallback = this.hasFallbackTarget ? this.fallbackTarget : null;
@@ -9482,6 +9485,7 @@ var EditableController = class extends Controller {
9482
9485
  this.#revertValue = null;
9483
9486
  }
9484
9487
  onDisplayKeydown(event) {
9488
+ if (event.defaultPrevented) return;
9485
9489
  if (event.key === "F2") {
9486
9490
  event.preventDefault();
9487
9491
  this.edit();
@@ -11666,6 +11670,27 @@ var InputMaskController = class extends Controller {
11666
11670
  #textBeforeComposition = "";
11667
11671
  #formatted = null;
11668
11672
  #shown = { result: null, empty: true };
11673
+ #edits = new ListenerSet();
11674
+ #onBeforeInput = (event) => {
11675
+ const data = event.data;
11676
+ if (event.defaultPrevented || !event.cancelable || event.inputType !== "insertText" || data === null || Array.from(data).length !== 1 || !this.#started || this.#composition.isComposing(event))
11677
+ return;
11678
+ const input = this.element;
11679
+ const start = input.selectionStart;
11680
+ const end = input.selectionEnd;
11681
+ if (start === null || end === null || start === end || input.readOnly || input.disabled || inheritsFieldsetDisabled(input) || !input.isConnected || !this.application.element.contains(input) || !(input.getAttribute("data-controller") ?? "").split(/\s+/).includes(this.identifier) || this.patternValue === "" || this.#formatted !== this.#declared || input.value !== this.#lastValue)
11682
+ return;
11683
+ const prefix = applyMask(input.value.slice(0, start), this.patternValue, this.#tokens);
11684
+ for (const character of Array.from(this.patternValue).slice(prefix.tokenFlags.length)) {
11685
+ const token = this.#tokens.get(character);
11686
+ if (token) {
11687
+ if (acceptedForm(token, data) === null) event.preventDefault();
11688
+ return;
11689
+ }
11690
+ if (halfWidthChar(data) === halfWidthChar(character)) return;
11691
+ }
11692
+ event.preventDefault();
11693
+ };
11669
11694
  #composition = new CompositionTracker({
11670
11695
  onStart: () => {
11671
11696
  this.#textBeforeComposition = this.element.value;
@@ -11701,6 +11726,7 @@ var InputMaskController = class extends Controller {
11701
11726
  this.#morph.observe(this.element);
11702
11727
  this.#started = true;
11703
11728
  this.#composition.observe(this.element);
11729
+ this.#edits.add(this.element, "beforeinput", this.#onBeforeInput);
11704
11730
  if (!this.unmaskToHiddenValue) this.#withdraw();
11705
11731
  if (this.#composition.isComposing()) {
11706
11732
  this.#reformat("reconcile");
@@ -11715,6 +11741,7 @@ var InputMaskController = class extends Controller {
11715
11741
  disconnect() {
11716
11742
  this.#morph.disconnect();
11717
11743
  this.#started = false;
11744
+ this.#edits.dispose();
11718
11745
  this.#composition.disconnect();
11719
11746
  this.#adoption.clearAll();
11720
11747
  this.#gate.disconnected(this, () => this.#retractRaw());
@@ -13885,7 +13912,9 @@ var MultiSelectController = class _MultiSelectController extends Controller {
13885
13912
  focusAfterEnd: () => this.#focusInput()
13886
13913
  });
13887
13914
  #listeners = new ListenerSet();
13915
+ #tab = null;
13888
13916
  connect() {
13917
+ this.#tab = null;
13889
13918
  this.#tabindex.reclaim(this.element);
13890
13919
  this.#rows.connect();
13891
13920
  this.#normalizeSelection([]);
@@ -13897,6 +13926,9 @@ var MultiSelectController = class _MultiSelectController extends Controller {
13897
13926
  this.#selectionValues = this.#values;
13898
13927
  this.#syncFields();
13899
13928
  this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
13929
+ this.#listeners.add(this.element, "keydown", this.#onTab);
13930
+ for (const type of ["focus", "keydown", "keyup", "pointerdown"])
13931
+ this.#listeners.add(document, type, this.#trackTab, { capture: true });
13900
13932
  this.#connected = true;
13901
13933
  this.#reconcile.observe(this.element);
13902
13934
  }
@@ -13918,6 +13950,7 @@ var MultiSelectController = class _MultiSelectController extends Controller {
13918
13950
  this.#composition.disconnect();
13919
13951
  this.#chipRow.disconnect();
13920
13952
  this.#listeners.dispose();
13953
+ this.#tab = null;
13921
13954
  this.#releaseTabindex();
13922
13955
  }
13923
13956
  optionTargetConnected() {
@@ -14087,7 +14120,11 @@ var MultiSelectController = class _MultiSelectController extends Controller {
14087
14120
  this.#setActive(visible[0] ?? null);
14088
14121
  this.dispatch("filter", { detail: { query } });
14089
14122
  }
14090
- open() {
14123
+ open(event) {
14124
+ if (event instanceof FocusEvent && event === this.#tab) {
14125
+ this.#tab = null;
14126
+ return;
14127
+ }
14091
14128
  if (!this.hasListTarget || !this.hasInputTarget) {
14092
14129
  this.#reflectEmpty();
14093
14130
  return;
@@ -14174,6 +14211,16 @@ var MultiSelectController = class _MultiSelectController extends Controller {
14174
14211
  break;
14175
14212
  }
14176
14213
  }
14214
+ #onTab = (event) => {
14215
+ if (event.key !== "Tab" || event.defaultPrevented || event.isComposing || !this.hasTagsTarget || !this.tagsTarget.contains(event.target))
14216
+ return;
14217
+ this.#tab = event;
14218
+ this.close();
14219
+ };
14220
+ #trackTab = (event) => {
14221
+ const key = this.#tab;
14222
+ this.#tab = event.type === "focus" && key instanceof KeyboardEvent && !key.defaultPrevented && event.target === this.inputTargets[0] ? event : null;
14223
+ };
14177
14224
  toggleOption(source) {
14178
14225
  const { event, host, origin, reason } = actionSource(source);
14179
14226
  if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
@@ -17986,6 +18033,7 @@ var PointerDragController = class _PointerDragController extends Controller {
17986
18033
  #tabindex = new TabindexLoan("0", this.identifier, { preserveFocus: true });
17987
18034
  #pointer = null;
17988
18035
  #keyboard = null;
18036
+ #connected = false;
17989
18037
  #keyboardFocusSession = null;
17990
18038
  #listeners = new ListenerSet();
17991
18039
  #shadowFocus = new ListenerSet();
@@ -17999,6 +18047,7 @@ var PointerDragController = class _PointerDragController extends Controller {
17999
18047
  this.#reflectSession();
18000
18048
  });
18001
18049
  connect() {
18050
+ this.#connected = true;
18002
18051
  this.#morphRender.observe(this.element);
18003
18052
  if (!this.#pointer && !this.#keyboard) this.#followOutput = null;
18004
18053
  this.#gate.cancel();
@@ -18016,6 +18065,7 @@ var PointerDragController = class _PointerDragController extends Controller {
18016
18065
  }
18017
18066
  }
18018
18067
  disconnect() {
18068
+ this.#connected = false;
18019
18069
  this.#morphRender.disconnect();
18020
18070
  this.#listeners.dispose();
18021
18071
  this.#shadowFocus.dispose();
@@ -18042,7 +18092,9 @@ var PointerDragController = class _PointerDragController extends Controller {
18042
18092
  }
18043
18093
  handleTargetDisconnected(handle) {
18044
18094
  this.#restoreHandle(handle);
18045
- if (this.element.contains(handle)) return;
18095
+ if (!this.#connected) return;
18096
+ if (this.element.contains(handle) && (this.handleTargets.includes(handle) || DetachGate.isDetached(this)))
18097
+ return;
18046
18098
  const interrupted = this.#pointer?.handle === handle && this.#pointer.started ? this.#pointer.pointerType : this.#keyboard?.handle === handle ? "keyboard" : null;
18047
18099
  if (this.#pointer?.handle === handle) this.#endPointerSession();
18048
18100
  if (this.#keyboard?.handle === handle) this.#clearKeyboardSession();
@@ -18397,6 +18449,7 @@ var PopoverController = class extends Controller {
18397
18449
  this.#close("api");
18398
18450
  this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
18399
18451
  this.#listeners.add(this.element, "focusout", this.#onFocusOut);
18452
+ this.#listeners.add(document, "focusin", this.#onDocumentFocusIn, { capture: true });
18400
18453
  this.#connected = true;
18401
18454
  this.#reporting = true;
18402
18455
  }
@@ -18503,6 +18556,14 @@ var PopoverController = class extends Controller {
18503
18556
  if (!(next instanceof Node) || this.element.contains(next)) return;
18504
18557
  this.#close("focus");
18505
18558
  };
18559
+ #onDocumentFocusIn = (event) => {
18560
+ if (!this.#closable) return;
18561
+ const target = event.target;
18562
+ if (!(target instanceof Node) || target === document.body || this.element.contains(target)) {
18563
+ return;
18564
+ }
18565
+ this.#close("focus");
18566
+ };
18506
18567
  get #isOpen() {
18507
18568
  return this.hasPanelTarget && !this.panelTarget.hidden;
18508
18569
  }
@@ -5,6 +5,6 @@ module Stimeo
5
5
  # Kept in lockstep with package.json (stable versions share the same
6
6
  # notation; a prerelease maps npm `-beta.N` ⇔ gem `.pre.beta.N` because
7
7
  # RubyGems forbids dashes). Bump both together.
8
- VERSION = "0.18.0"
8
+ VERSION = "0.18.1"
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stimeo-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stimeo Labs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-22 00:00:00.000000000 Z
11
+ date: 2026-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties