shadok-ai 0.15.52 → 0.15.53

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 (2) hide show
  1. package/package.json +1 -1
  2. package/public/index.html +25 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadok-ai",
3
- "version": "0.15.52",
3
+ "version": "0.15.53",
4
4
  "main": "dist/session.js",
5
5
  "scripts": {
6
6
  "test": "node --import tsx --test --test-timeout=120000 test/*.ts context/*/test/*.test.mjs",
package/public/index.html CHANGED
@@ -4633,12 +4633,18 @@
4633
4633
 
4634
4634
  // A single click on a group's icon SELECTS the group (when the rail is
4635
4635
  // collapsed to icons the icon is almost the whole row, so this is the primary
4636
- // action); a double-click opens the icon picker. The stamp has to live out
4637
- // here, not on the element: selecting a non-active group rebuilds the rail
4638
- // (renderRail keys on the active view) and replaces the very icon between the
4639
- // two clicks, so a native `dblclick` never fires and an element-local timer
4640
- // would be thrown away with it.
4641
- let lastIconClick = { view: null, t: 0 };
4636
+ // action); a double-click opens the icon picker, and a double-click on the
4637
+ // name renames it. Neither may rely on a native `dblclick` or on state kept on
4638
+ // the element: the rail is REBUILT under the pointer — by the first click
4639
+ // itself (selecting a group changes the active view) and, with live agents,
4640
+ // by any mood change in between — and a dblclick whose two clicks land on two
4641
+ // different nodes never fires. The browser's own click count (`e.detail`)
4642
+ // follows the pointer, not the node, and the OS double-click speed with it.
4643
+ const isSecondClick = (e) => e.detail >= 2;
4644
+ // While a click is under way, a rebuild that only repaints moods waits: it
4645
+ // would swap the node between mousedown and mouseup and swallow the click.
4646
+ let railHoldUntil = 0;
4647
+ railList.addEventListener("pointerdown", () => { railHoldUntil = Date.now() + 700; }, true);
4642
4648
 
4643
4649
  function railItem({ view, icon, name, group }) {
4644
4650
  const members = tabsInView(view);
@@ -4688,16 +4694,13 @@
4688
4694
  // and so the document-level close-on-click doesn't shut the picker we
4689
4695
  // may be about to open with this very click.
4690
4696
  e.stopPropagation();
4691
- const now = Date.now();
4692
- if (lastIconClick.view === view && now - lastIconClick.t < 350) {
4693
- lastIconClick = { view: null, t: 0 };
4694
- openIconPicker(group, ic);
4695
- } else {
4696
- lastIconClick = { view, t: now };
4697
- setView(view);
4698
- }
4697
+ if (isSecondClick(e)) openIconPicker(group, ic);
4698
+ else setView(view);
4699
4699
  });
4700
- nm.addEventListener("dblclick", (e) => {
4700
+ nm.addEventListener("click", (e) => {
4701
+ if (!isSecondClick(e)) return; // the item's own click selects the group
4702
+ // Not to the item: its setView could move the focus to the composer
4703
+ // and blur (= close) the field we are opening.
4701
4704
  e.stopPropagation();
4702
4705
  inlineRename(item, nm, (v) => {
4703
4706
  group.nameEl.textContent = v;
@@ -4770,6 +4773,13 @@
4770
4773
  return [e.view, e.icon, e.name, members.length, members.map(moodOf).join(",")].join("/");
4771
4774
  }).join("|");
4772
4775
  if (sig !== railSig) {
4776
+ // Same view → nothing the user did; it can wait for the click to finish.
4777
+ const wait = railHoldUntil - Date.now();
4778
+ if (wait > 0 && railSig.startsWith(v + "|")) {
4779
+ clearTimeout(renderRail.retry);
4780
+ renderRail.retry = setTimeout(renderRail, wait);
4781
+ return;
4782
+ }
4773
4783
  railSig = sig;
4774
4784
  railList.replaceChildren(...entries.map(railItem));
4775
4785
  }