Preview Mode
(1) By Richard Hipp (drh) on 2020-05-04 12:56:57 [link] [source]
Here is a UX improvement suggestion stolen from bugzilla: Instead of having a "Preview" button at the bottom of pages that offer markup input, put the text entry box inside of a notebook widget with two tabs labeled "Edit" and "Preview". Clicking to the Preview tab updates the preview using an XMLHttpRequest round-trip to the server.
I'll try to illustrate using ASCII-art:
,---------.
| Edit | Preview
| `---------------------------------------------------.
| Here is where you type |
| your raw Markdown text. |
| |
`-------------------------------------------------------------'
Then when you click the "Preview" tab, it switches to:
,-----------.
Edit | Preview |
,-----------' `-------------------------------------.
| Non-editable view of what the markdown looks like after |
| rendering. |
| |
`-------------------------------------------------------------'
Presumably, the "Submit" button would be grayed-out for Edit and would only function for "Preview".
This approach requires more javascript (obviously) which some people do
not like. But javascript nay-sayers are becoming less common, so maybe
something like this would be ok. Or, maybe the current solution could
be provided as an alternative inside of <noscript>...</noscript>
.
(2) By Warren Young (wyoung) on 2020-05-04 13:11:17 in reply to 1 [link] [source]
Looks good.
We could have both. Just put the current "Preview" button and in a <noscript>
tag and apply the markup for the tabbed UI in JS on page load. Then the no-JS fans have their view, and the vast majority of the Fossil audience has the new view.
I wouldn't suggest this if we didn't have the code for the HTTP POST round-trip option alteady, but since we do...
The main question then is whether we want to carry the maintenance burden of two Preview methods.
(3) By Stephan Beal (stephan) on 2020-05-04 13:15:37 in reply to 1 [link] [source]
Here is a UX improvement suggestion stolen from bugzilla:
As is so often the case, you're one step ahead of me! BoardGameGeek (my other home page) recently switched to that approach in the their forums, and that is my plan for /fileedit
, once/if it's AJAXified. The current approach simply requires too much scrolling.
(4) By sean (jungleboogie) on 2020-05-04 13:47:02 in reply to 1 [link] [source]
Looks good to me as well. I don't remember the bugzilla layout, but github has something similar to this as well for adding an issue.
(5) By aitap on 2020-05-04 13:50:38 in reply to 1 [link] [source]
maybe the current solution could be provided as an alternative inside of
<noscript>...</noscript>
I would like to voice my support for the <noscript>
fallback, if that is possible. My CLA is in the post right now, so I could offer patches fixing bugs, should they arise in the course of evolution of XHR-powered interface.
(6) By Richard Hipp (drh) on 2020-05-04 14:15:35 in reply to 3 [link] [source]
Markup text entry boxes come up in various places. It would be good if we could figure out some common subroutine that could be used for them all, so that the code only needs to be written once. But I'm not sure what the interface to that subroutine would look like.
- Forum post entry
- Wiki page entry and editing
- Tech-note entry and editing
- Check-in comment editing
This also comes up for ticket description entry and editing, but that is controlled by TH1 script, not by C-code. So if some common subroutine is developed, we might need to make it accessible to TH1 as well.
(7) By Stephan Beal (stephan) on 2020-05-04 14:39:06 in reply to 6 [link] [source]
It would be good if we could figure out some common subroutine that could be used for them all,
That problem soon needs to be solved for the /fileedit
page, anyway, so that can be my TODO.
Regarding the tabbed editor/preview: my old fossil-json-based wiki front-end does that:
https://fossil.wanderinghorse.net/wikis/cson/
In the editor area, change some random text and tap the preview or diff buttons on that first tab (not the ones along the top - those are tab panels).
It just needs to be reimplemented to work without jquery, which isn't a big deal.
(8) By Stephan Beal (stephan) on 2020-05-05 17:30:30 in reply to 6 [link] [source]
Markup text entry boxes come up in various places. It would be good if we could figure out some common subroutine that could be used for them all, so that the code only needs to be written once. But I'm not sure what the interface to that subroutine would look like.
There are two aspects here with updates: the ajax/network layer and the tabbed interface:
Screenshot:
https://fossil.wanderinghorse.net/screenshots/filepage-demo-tabbed1.png
The generic ajax bits are in place and are being used to populate the newly-tabbed content, preview, and diffs in /fileedit
. C-cide, there's really nothing to do but emit the script code:
style_emit_script_fetch(); /* ajax API */
style_emit_script_tabs(); /* tab manager API */
A small amount of the work is done in the HTML generation and the majority is done in JS. How much of this can be made truly generic, though, is not yet clear.
JS-side, the results of saving and preview need to be handled differently per data type, and depends heavily on the structure of the page: comment editing will be much different, in terms of how JS responds to the ajax result, than wiki saving will. Wiki/forum/technote previews, on the other hand, will have much in common and we can possibly reduce these to a small set of data
DOM element attributions, and let JS use that configuration to automate the rest.
As a basis for comparison, the marked code is the load/preview/diff/commit bits for /fileedit
:
https://fossil-scm.org/fossil/artifact?udc=1&ln=145-328&name=8e19dfedaccac3b5
Only a few lines of each operation (the bottom of each function) are the actual ajax parts - the rest is collecting the config parameters for submission and updating their UI elements for the response, both of which are notably different for each operation and is almost entirely independent of the UI being tabbed.
How most easily/generically to tie ajax requests to tabs is still anybody's guess. It's working in /fileedit
, but there are certainly ways to factor some of the duplication out. (Oh, that gives me an idea...)
This also comes up for ticket description entry and editing, but that is controlled by TH1 script, not by C-code. So if some common subroutine is developed, we might need to make it accessible to TH1 as well.
So long as TH1 is permitted to emit JS code (by calling bound C code), that doesn't seem problematic.
(9) By Stephan Beal (stephan) on 2020-05-06 17:02:25 in reply to 6 [source]
Markup text entry boxes come up in various places. It would be good if we could figure out some common subroutine that could be used for them all, so that the code only needs to be written once. But I'm not sure what the interface to that subroutine would look like.
Here's a first go at it from the JS side:
https://fossil.wanderinghorse.net/screenshots/fossil-ajax-preview-api-demo.png
Summary:
- Create an input DOM element (typically a TEXTAREA)
- Create an output/preview DOM element
- Create a JS method which calls the appropriate AJAX interface with any parameters it needs (this is the data-type-dependent part).
- Create a clickable DOM element and add "data" fields which describe the connection between parts 1, 2, and 3.
The screenshot demonstrates 3 and 4, but the 1 and 2 parts are off-screen. Parts 1, 2, and 4 could be automated in C via an interface with something like this hypothetical call:
style_emit_previewable_editor( "topCssTag"/*unique-per-editor CSS tag*/);
which would emit something like (untested):
<div class='tab-container {{topCssTag}}' id='tabs-{{topCssTag}}'></div>
<!-- Tab 1: the editor: -->
<div data-tab-parent='tabs-{{topCssTag}} "
data-tab-label='Edit'
class='{{topCssTag}}-tab-editor'>
<textarea id='{{topCssTag}}-editor'></textarea><!-- preview data source -->
</div>
<!-- Tab 2: the previewer: -->
<div data-tab-parent='tabs-{{topCssTag}} "
data-tab-label='Preview'
class='{{topCssTag}}-tab-preview'>
<div>
<!-- preview refresh button: -->
<button id='{{topCssTag}}-preview-button'
data-f-post-from='{{topCssTag}}-editor' <!-- data source element ID -->
data-f-post-to='{{topCssTag}}-preview-content' <!-- preview target element ID -->
data-f-post-via='post{{topCssTag}}' <!-- explained below -->
>Refresh</button>
<!-- we can also/instead of cause it to refresh automatically when the
preview tab is selected -->
</div>
<div class='{{topCssTag}}-preview-content'><!-- preview content goes here --></div>
</div>
<script nonce='...'>
// Setup the tabs:
fossil.page.editorTabs = new fossil.TabManager('#tabs-{{topCssTag}}');
// We can programmatically add more tabs, and modify the existing ones,
// from downstream JS code if desired.
// Connect the preview button to its various pieces:
fossil.connectPagePreviewers('#{{topCssTag}}-preview-button');
</script>
The one part which would then need to created by hand is the part referenced by the data-f-post-via='post{{topCssTag}}
. That's a JS-side method in the fossil.page
namespace (which is reserved for use by the site's current page). This part is necessarily data-type specific and looks something like:
fossil.page.post{{topCssTag}} = function(content,callback){
const fd = new FormData();
// collect POST parameters for the upcoming AJAX call:
fd.append('content', content);
fd.append('foo', ...);
fd.append('bar', ...);
fossil.fetch('preview_forumpost'/*page to send request to*/,{//ajax options
payload: fd,
onload: callback
// the ^^^^ callback is generated by fossil.connectPagePreviewers()
// and shoves the response content into the target (preview)
// DOM element.
});
};
See the bottom/right section of the screenshot for a "real-world" example of such a callback, currently used by /fileedit
.
i haven't yet written the C-side part, but something like the above ought to do the trick, more or less, and i'll give it a go sometime soon.
One part notably missing from the above is a button to submit the being-edited content. That handling is necessarily type-specific, and the button layout may also need to be, but we can certainly genericize/automate it to some degree.
Baby steps.