Fossil Forum

RFC: new /chat input widget (and possibly reverting it)
Login

RFC: new /chat input widget (and possibly reverting it)

RFC: new /chat input widget (and possibly reverting it)

(1) By Stephan Beal (stephan) on 2021-10-09 17:24:34 [link] [source]

Background: the new /chat input widget

As those who make use of fossil's /chat page have noticed, 2.17 uses a different input widget. In short: instead of a plain-text input field, we use a DIV element with the "contenteditable" attribute, making in possible to type directly in that element.

The plus sides of that include:

  • Most importantly, dynamic sizing. The element grows as text is added, so it's always ideally-sized for the screen.
  • Freedom of styling. Not all browsers can style a text field.

However, the down sides include, but are not limited to:

  • Every browser has its own quirks on how editing in such an element is done. e.g. they inject invisible HTML elements in the field as you type and they do so differently. That leads to, for example, different newline behaviour and completely different behaviour when pasting in text (some Firefox versions like to double the newlines when pasting).

  • Focus-grabbing of such an element, once it's lost focus, cannot restore the cursor to its previous position like a text field can. That may sound harmless but it's severely irritating when returning to editing and the cursor is suddenly at the beginning of the input field. (This is one of the few aspects of contenteditable handling which the browsers seem to agree on.)

  • Firefox and Pale Moon (more the latter than the former) will occasionally refuse to give that element input focus anymore until the user switches to another tab and back.

  • Browsers don't agree on which cursor to show when mousing over such an element which doesn't have input focus. We can force this to be consistent with CSS, but "we really shouldn't have to."

  • The most useful APIs for manipulating contenteditable elements have been deprecated (namely document.executeCommand()), making it much more work to do anything useful with them (such as trying to normalize how text is pasted in to strip any HTML formatting, as some browsers like to paste with formatting and some don't).

Even now, new browser-specific quirks are still being discovered by the users who've been helping test it the past couple of weeks. Sigh.

The Question: Keep it or not?

Despite the more modern and generally more comfortable behaviour of this new widget, trying to reproduce and work around the array of browser-specific quirks is, quite frankly, growing tiresome :/.

This change brought about several significant internal improvements in the /chat app, so a straight roll-back to the older version is not practical or sensible, but the current, and presumed future, maintenance regarding browser-specific quirks genuinely begs the question of whether we should/want to switch from this widget to the older model of having two input widgets: a single-line text field and a multi-line textarea, offering a toggle to switch between the two (like we had before, and analogous to the current "compact" and "non-compact" mode toggle). Such input elements, unlike contenteditable ones, behave completely predictably across all browsers.

My current (this moment (but ask me again tomorrow)) preference is to revert back to the older model. My attempts at getting a textarea to grow on demand, like the new widget, have failed. That's apparently only possible by measuring the element after each keystroke, expanding or shrinking it depending on its apparent amount of content (judged by whether the element has scrollbars or not), but such a solution would be painfully inefficient, battery-hungry, and ugly, so is a non-starter.

Your opinions on the topic of whether to keep or drop the new widget are very welcomed, noting that anyone who would like to see the new widget retained needs a more compelling argument than its modern appearance and comfort (the latter being superficial, as the numerous platforms-specific quirks replace that with maintenance and support hassles). i'm not currently entertaining the idea of supporting a toggle to swap between models (though the internals are encapsulated to make that fairly easy to do), but could be convinced to, with the caveat that users encountering browser-specific weirdness in the new widget would then likely be told to use the text input fields (as opposed to me expending X time trying to resolve such weirdness).

(2) By graham on 2021-10-09 20:16:45 in reply to 1 [link] [source]

I've not used either version of chat, so this opinion is based almost entirely on your description...

From that description, I would concur with moving back to the older edit box. While not a UI/UX expert, I believe one of the main things you don't want from an interface is to be "surprised" by it, and it sounds like the new widget, with current browsers, is ripe for a lot of unexpected behaviour. The old model, though perhaps not as slick as the new one might become, presumably worked, and in a non-surprising way.

[I]'m not currently entertaining the idea of supporting a toggle to swap between models [...], but could be convinced to

Depending on how quickly you expect/anticipate browsers to get better at supporting the new widget, there may be an argument for having a "Use experimental input method" toggle somewhere, so that interested parties can more easily (re)try it as-and-when browsers get updated. However, were this to be done, the default should be the old way, and – for the time being – it should be made clear that it is an option to use an experimental method, not a toggle between two alternative, fully-supported, methods.

My 2c.

(3) By Scott Robison (sdr) on 2021-10-10 02:51:02 in reply to 2 [link] [source]

If the maintenance burden isn't too much, I concur with an experimental checkbox that is off by default. I do like the dynamically resizing box, and I don't C&P much into it. For actual chat where I am typing things in, it is far superior in my experience thus far.

That being said, I don't plan to maintain it and obviously was able to use the ugly textarea, so ... do what you must. :)

(4) By jamsek on 2021-10-10 03:02:09 in reply to 3 [link] [source]

I agree with the above; that is, label it experimental and default to the old setup.

(5) By Stephan Beal (stephan) on 2021-10-10 04:20:16 in reply to 2 [link] [source]

... there may be an argument for having a "Use experimental input method" toggle somewhere...

Challenge Accepted!

That sounds like an agreeable approach. i'd hate to outright toss the new widget because it's far more comfortable to use, but the quirks are completely killing it for me (in particular, FF and Pale Moon frequently blocking focus access to it until one switches to another browser tab and back).

Part 1 is in place: revert to the two plain-text input fields, with the "compact mode" toggle swapping between them.

src:/timeline?r=chat-input-revisited

Up next: add the "experimental mode" toggle.

(6) By Florian Balmer (florian.balmer) on 2021-10-10 08:14:57 in reply to 1 [source]

My attempts at getting a textarea to grow on demand, like the new widget, have failed. That's apparently only possible by measuring the element after each keystroke, expanding or shrinking it depending on its apparent amount of content (judged by whether the element has scrollbars or not), but such a solution would be painfully inefficient, battery-hungry, and ugly, so is a non-starter.

This could be solved with a "debouncing timer", and recalculating the dimensions of the textarea element only after user input has been idle for some 200-500 ms, for example. That's an established technology we all use on a daily basis on websites and local applications.

(This is not a feature request, just an idea. The same approach might also work with the Diff.checkTableWidth() resize handler, which is quite expesnive due to it's getComputedStyle() calls; not a feature request either, also just an idea in case there's problem reports that resizing is sluggish.)

(7) By Florian Balmer (florian.balmer) on 2021-10-10 09:55:03 in reply to 6 [link] [source]

Just something to paly around with, based on [db54f4b706].

Index: src/fossil.diff.js
==================================================================
--- src/fossil.diff.js
+++ src/fossil.diff.js
@@ -637,10 +637,11 @@
 window.fossil.onPageLoad(function(){
   const SCROLL_LEN = 25;
   const F = window.fossil, D = F.dom, Diff = F.diff;
   var lastWidth;
   Diff.checkTableWidth = function f(force){
+    console.log('Diff.checkTableWidth() called!');
     if(undefined === f.contentNode){
       f.contentNode = document.querySelector('div.content');
     }
     force = true;
     const parentCS = window.getComputedStyle(f.contentNode);
@@ -736,8 +737,14 @@
   }
   window.fossil.page.tweakSbsDiffs = function(){
     document.querySelectorAll('table.splitdiff').forEach((e)=>Diff.initTableDiff(e));
     Diff.checkTableWidth();
   };
-  Diff.initTableDiff().checkTableWidth();
-  window.addEventListener('resize', ()=>Diff.checkTableWidth());
+  setTimeout(function(){Diff.initTableDiff().checkTableWidth();});
+  var debounceResize = function(f,t){
+    clearTimeout(this.idTimer);
+    this.idTimer = setTimeout(f,t);
+  };
+  window.addEventListener('resize', function(){
+    debounceResize(Diff.checkTableWidth,500);
+  });
 }, false);

This patch causes Diff.checkTableWidth() to be called only after at least 500 ms of input idle time. For larger diffs resized with the mouse, this has the effect that the browser has more free resources to recalculate the page layout, as multiple expensive resize events are coalesced, and that it works smoother, which is quite nice. Then after half a second, the individual diffs are also adjusted.

The code is the most simple traditional/vanilla Javascript version, as I first tried it with my IE-compatible version. The Fossil JS Framework may have more requirements to pass around this pointers and other variables -- and maybe some fancier notation with arrow functions, and the like! ;-) But the above patch works with the version mentioned at the top, so is ready to try out!

(8) By Stephan Beal (stephan) on 2021-10-10 13:47:23 in reply to 7 [link] [source]

This patch causes Diff.checkTableWidth() to be called only after at least 500 ms of input idle time.

Very nice, thank you :). i thought we had a debouncer routine already in our JS toolkit, but it seems not. i will plug that in soon. We can use that in /chat as well, which also has to monitor the page size.

(9) By Florian Balmer (florian.balmer) on 2021-10-11 06:52:32 in reply to 8 [link] [source]

i thought we had a debouncer routine already in our JS toolkit ...

Was that a "royal we"? YOU da JS man!

BTW, this post contains links to various summaries about UI response delays that may be helpful when chosing timeouts. I think 500 ms is quite right for diff resizing.

(10) By Stephan Beal (stephan) on 2021-10-11 08:50:26 in reply to 9 [link] [source]

Was that a "royal we"? YOU da JS man!

Indeed, "the royal we."

BTW, this post contains links to various summaries about UI response delays that may be helpful when chosing timeouts. I think 500 ms is quite right for diff resizing.

For the diff layout i used 500, as you initially suggested. For chat i reduced it, after some experimentation, to 250 because the effect is far more noticeable in that app and 500 feels laggy. The default timeout for our newly-added generic debouncer is 500.

(11) By Florian Balmer (florian.balmer) on 2021-10-12 07:01:25 in reply to 10 [link] [source]

For the diff layout i used 500, as you initially suggested. For chat i reduced it, after some experimentation, to 250 because the effect is far more noticeable in that app and 500 feels laggy.

Yes, some people may perceive 500 ms as a bug (for the diff pages), and the timeout may have to be reduced to 400 ms, or 350 ms. But I think that resizing a complex web page is already a laggy operation, and delaying additional event handlers seems advantageous. Also, the resizing of the individual diff blocks is not something the user is "waiting for", i.e. visual orientation on the page is already possible before the event handler has completed, and the resizing of the individual diff blocks has no side effects outside of the blocks, i.e. the scrolling range and/or position of the whole page remain constant, so things "feel smooth."

What is still bugging me most on the Fossil and SQLite websites is the super-slow opening animation of the hamburger menu, because "you have to wait for it." I think the delay of 400 ms is already way too long for a popup menu, and due to my short-lived browser cache and my rare use of the menu, the AJAX request to load the menu contents regularly adds another 100-200 ms to the delay, driving me nuts.

The default timeout for our newly-added generic debouncer is 500.

Wow, that code ... all possibilities of obfuscation fully exploited! ;-) That's no longer something I can read and execute mentally.

(12) By Florian Balmer (florian.balmer) on 2021-10-21 12:15:45 in reply to 11 [link] [source]

Another nice effect of the delayed resize handler I just noticed: not only resizing is much smoother, but also zooming in/out! Quickly zooming in/out several levels at once is much faster, as new zoom levels also seem to trigger the resize handlers. Maybe even mobile users will like this (who probably don't resize their windows too often, or never at all).