rewind (development version)
- Password fields are never kept in the history. A
passwordInput()sends a plain string, sorewindcaptured it like any text box: the password was kept in the history, put back in the field by an undo, and shown in plain text byrewind_diff(). The browser now reports which fields are passwords, including fields thatrenderUI()adds later, andrewindleaves them out, even wheninputsnames them. A value captured before the report arrives is removed from the history. -
rewind_diff()shows an empty text box as"". A change from an empty box read-> hello, with nothing before the arrow. - The help page of
rewind_enable()has a new section on text inputs: slow typing can give several steps,textInput(updateOn = "blur")gives one step for each edit, andCtrl+Zinside a text box is the browser’s own undo, whichrewindthen records as a new step. - The fallback in
rewind.jsthat waits for Shiny to connect usedaddEventListener(), which never receives the jQuery event that Shiny triggers. It now uses jQuery. The fallback is not used when Shiny loads first, which is the usual order.
rewind 0.3.0
CRAN release: 2026-09-25
This release goes to CRAN. Version 0.2.1 was released on GitHub only, so an update from 0.2.0 on CRAN brings the changes of 0.2.1 below as well.
-
New
rewind_diff(). It shows what changed between two steps, and not only which names changed. [rewind_history()] gives a label such as"region, year";rewind_diff()gives one row for each value, with the value before, the value after, and a short description such as"North -> South". Theoldandnewcolumns are list columns, so a value of any type survives, including a date range, a multiple selection or a whole data frame. The result depends on the history, so it works insiderender*()andobserve().It is not an audit trail, and the documents say so. The history is in the memory of one session, it drops the oldest entries when it passes
depth, and it drops the steps in front of the position after a change. Write a record for an inspection as each change occurs, and keep it elsewhere. The history rail no longer scrolls the page or the sidebar that holds it. It kept the current step in view with
scrollIntoView(), which scrolls every scrollable ancestor of the element and not only the nearest one. In a layout that putsrewind_ui()in a sidebar, every change to the history scrolled that sidebar, so the application appeared to scroll by itself. The rail now sets its ownscrollTop, and moves nothing else.Fixed: undo, redo and the rail did nothing when
rewind_enable()was called inside amoduleServer(). The browser sends the global input idsrewind_undo,rewind_redoandrewind_jump, but inside a modulerewind_enable()watched the namespaced ids. Capture still worked, so the rail filled up, but no button, shortcut or rail click reached the history.rewind_enable()now reads these inputs from the root session.Fixed: the rail showed each time in UTC, and not in the viewer’s time zone. The server now sends the moment, and the browser formats it with its own clock. The format is still
HH:MM:SS. Thetimecolumn ofrewind_history()is unchanged.rewind_enable()andrewind_step()now refuseNA,NaNandInfincoalesce_ms,restore_timeoutandhold_ms, with a message that names the argument.NAgave “missing value where TRUE/FALSE needed”.Infwas accepted and then did harm with no message: incoalesce_msit stopped the history from recording anything.hold_mswas not checked at all before.rewind_diff()now refuses a position that is not a whole number, such as2.7. It truncated it to2before.
rewind 0.2.1
-
rewind_enable()takes arestore_timeoutargument, in seconds. It sets how longrewindwaits for the browser to finish a restore. The value was fixed at 2 seconds before, which is enough on a fast connection but not always on a slow one. If the limit is too short, capture starts again while the browser is still applying the restore, and a partial state becomes a history entry that the user never made. -
rewindno longer drops a data frame of the user that has the same four column names as the value of afileInput(). It now examines the column types as well as the names. Such a value was dropped from the history before, with no message. -
rewind_buttons()takes abutton_classargument. It adds CSS classes to the two buttons. Use it for a Bootstrap variant such as"btn-primary"or"btn-outline-secondary", and for a size such as"btn-sm". Theclassargument continues to hold classes for the container element only, so there was no way to reach the buttons before. - The arrows on the buttons are now SVG. They were the HTML entities
↶and↷, which come from the font of the browser and thus have a different weight and size on each system. The SVG arrows usecurrentColorand a size inem. They thus follow the colour and the size of the button, including withbtn-sm,btn-lg, an outline variant and a dark theme. - The redo button now says “Redo (Ctrl+Shift+Z or Ctrl+Y)” when the pointer rests on it. It said only “Redo (Ctrl+Shift+Z)” before.
Ctrl+Yhas always done a redo, but the button did not say so. - The description of the
shortcutsargument ofrewind_enable()now lists the three shortcuts, and says which one belongs to which system. - The demo application now names the redo shortcut in its hint.
rewind 0.2.0
CRAN release: 2026-08-21
-
rewind_disable()stops undo and redo for a session completely. It destroys each observer thatrewind_enable()made. It also clears the buttons and the rail in the browser. Userewind_pause()andrewind_resume()to stop capture for a short time. Userewind_disable()to stop it for the rest of the session. -
rewindno longer capturesfileInput(). Its value points to a temporary file on the server. Shiny deletes that file at the next upload. An old snapshot would thus point to a file that does not exist. - Fixed: a call to
rewind_pause()before the first reactive flush stopped capture for the rest of the session. A laterrewind_resume()did not start it again. This occurred, for example, with a call torewind_pause()immediately afterrewind_enable(), before any input changed. -
rewind_buttons(undo_label = NULL, redo_label = NULL)now setsaria-labelon the buttons. A button with an icon and no text thus has a correct accessible name. -
rewind_enable()inside amoduleServer()now has tests. The documents said “untested” before.
rewind 0.1.0
First release.
-
rewind_enable()captures the session inputs into an undo and redo history. It groups the changes by time. One movement of a slider is thus one step, and not forty steps. -
rewind_track()adds thereactiveValueson the server to the history. -
rewind_step()puts several changes into one entry with a label. -
rewind_buttons()andrewind_ui()give ready-made undo and redo controls, and a scrubbable history rail. - Keyboard shortcuts:
Ctrl/Cmd+Z,Ctrl/Cmd+Shift+Z, andCtrl+Y. They do nothing while the user types in a text field. - A restore uses the Shiny binding of each input. It does not use a fixed
update*Input()table. Inputs from other packages thus operate with no extra code.
