diff --git a/ivette/.eslintrc.js b/ivette/.eslintrc.js
index e57c8f64dbc20e9ed697cf0cf85f6090a5b84a41..163e36aee57deb7cfda15a74345bb71c0f2eb006 100644
--- a/ivette/.eslintrc.js
+++ b/ivette/.eslintrc.js
@@ -51,6 +51,8 @@ module.exports = {
     "padded-blocks": "off",
     // Allow braces on their own line
     "@typescript-eslint/brace-style": "off",
+    // Allow range conditions such as 0 <= x && x < 10
+    "yoda": [2, "never", { "onlyEquality": true }],
     // Allow single command on new line after 'if' statement
     "curly": "off",
     // Do not specify position for single commands
diff --git a/ivette/src/dome/src/renderer/controls/buttons.js b/ivette/src/dome/src/renderer/controls/buttons.js
index c1bb0d0724d4b633e58c6967929ffc5e920a3512..fcb2709e94abcff9ca8ff2bb4ff377d3abb32054 100644
--- a/ivette/src/dome/src/renderer/controls/buttons.js
+++ b/ivette/src/dome/src/renderer/controls/buttons.js
@@ -118,7 +118,7 @@ const LABEL = ({disabled,label}) => (
    Buttons without focus can not be triggered with the Enter key.
 */
 export const Button = (props) => {
-  const disabled = DISABLED(props);
+  const disabled = props.onClick ? DISABLED(props) : true ;
   const { focusable=false, kind='default',
           visible=true, display=true, blink=false,
           selected, icon, label, className='' } = props;
@@ -176,7 +176,7 @@ export const Button = (props) => {
    Buttons without focus can not be triggered with the Enter key.
 */
 export const CircButton = (props) => {
-  const disabled = DISABLED(props);
+  const disabled = props.onClick ? DISABLED(props) : true ;
   const { focusable=false, kind='default',
           visible=true, display=true,
           selected, icon, label, className='' } = props;
@@ -229,7 +229,7 @@ export const CircButton = (props) => {
    - `'negative'`: negative button, in red.
 */
 export const IconButton = (props) => {
-  const disabled = DISABLED(props);
+  const disabled = props.onClick ? DISABLED(props) : true;
   const {
     icon, size, title,
     visible=true, display=true, selected,
@@ -271,7 +271,7 @@ const CHECKBOX_DISABLED = 'dome-control-disabled dome-xCheckbox ' ;
    @property {object} [style] - Additional style
 */
 export const Checkbox = (props) => {
-  const disabled = DISABLED(props);
+  const disabled = onChange ? DISABLED(props) : true;
   const onChange = props.onChange && ((evt) => props.onChange(evt.target.checked)) ;
   const baseClass = disabled ? CHECKBOX_DISABLED : CHECKBOX_ENABLED ;
   const labelClass = props.className || '' ;
@@ -307,12 +307,11 @@ export const Checkbox = (props) => {
    @property {object} [style] - Additional style
 */
 export const Switch = (props) => {
-  const disabled = DISABLED(props);
+  const disabled = props.onChange ? DISABLED(props) : undefined ;
   const { visible=true, display=true } = props ;
   const onChange = props.onChange && ((evt) => props.onChange(evt.target.checked)) ;
   const iconId = props.value ? 'SWITCH.ON' : 'SWITCH.OFF' ;
-  const onClick = (disabled || !props.onChange)
-        ? undefined : () => props.onChange(!props.value) ;
+  const onClick = disabled ? undefined : () => props.onChange(!props.value) ;
   const className = 'dome-xSwitch '
         + (disabled ? 'dome-control-disabled' : 'dome-control-enabled')
         + (visible ? '' : ' dome-control-hidden')
@@ -353,7 +352,7 @@ export const Switch = (props) => {
    with HTML standards and DOM element properties.
 */
 export const Radio = (props) => {
-  const disabled = DISABLED(props);
+  const disabled = props.onSelection ? DISABLED(props) : true;
   const checked = props.value === props.selection ;
   const onChange = props.onSelection && (() => props.onSelection(props.value)) ;
   const baseClass = disabled ? CHECKBOX_DISABLED : CHECKBOX_ENABLED ;
@@ -363,7 +362,8 @@ export const Radio = (props) => {
       title={props.title}
       style={props.style}
       className={baseClass + labelClass} >
-      <input type="radio" disabled={disabled} checked={checked} onChange={onChange} />
+      <input type="radio"
+             disabled={disabled} checked={checked} onChange={onChange} />
       {props.label}
     </label>
   );
@@ -415,8 +415,9 @@ export class RadioGroup extends React.Component {
   }
 
   render() {
-    const groupdisabled = DISABLED(this.props);
-    const { className='', style } = this.props ;
+    const props = this.props ;
+    const groupdisabled = props.onChange ? DISABLED(props) : true ;
+    const { className='', style } = props ;
     const selection = this.state.value ;
     const makeRadio = (radio) => {
       const disabled = groupdisabled || DISABLED(radio.props) ;
@@ -424,7 +425,7 @@ export class RadioGroup extends React.Component {
       const onSelection =
             onRadioSelect
             ? (v) => { onRadioSelect(v) ; this.onChange(v); }
-            : this.handleSelect ;
+            : this.onChange ;
       return React.cloneElement( radio , { selection , disabled , onSelection } , null );
     } ;
     return (
diff --git a/ivette/src/dome/src/renderer/controls/icons.json b/ivette/src/dome/src/renderer/controls/icons.json
index 681bb6d5286a132d76594580042d864e12cec557..dbe695a6bcb167ef5bf1b97b004d2df3888477b1 100644
--- a/ivette/src/dome/src/renderer/controls/icons.json
+++ b/ivette/src/dome/src/renderer/controls/icons.json
@@ -35,6 +35,12 @@
     "viewBox": "0 0 26 24",
     "path": "M8.263 18.737l-0.67 0.67q-0.134 0.134-0.308 0.134t-0.308-0.134l-6.241-6.241q-0.134-0.134-0.134-0.308t0.134-0.308l6.241-6.241q0.134-0.134 0.308-0.134t0.308 0.134l0.67 0.67q0.134 0.134 0.134 0.308t-0.134 0.308l-5.263 5.263 5.263 5.263q0.134 0.134 0.134 0.308t-0.134 0.308zM16.179 4.446l-4.996 17.29q-0.054 0.174-0.208 0.261t-0.315 0.033l-0.83-0.228q-0.174-0.054-0.261-0.208t-0.033-0.328l4.996-17.29q0.054-0.174 0.208-0.261t0.315-0.033l0.83 0.228q0.174 0.054 0.261 0.208t0.033 0.328zM24.978 13.165l-6.241 6.241q-0.134 0.134-0.308 0.134t-0.308-0.134l-0.67-0.67q-0.134-0.134-0.134-0.308t0.134-0.308l5.263-5.263-5.263-5.263q-0.134-0.134-0.134-0.308t0.134-0.308l0.67-0.67q0.134-0.134 0.308-0.134t0.308 0.134l6.241 6.241q0.134 0.134 0.134 0.308t-0.134 0.308z"
   },
+  "TERMINAL": {
+    "section": "Tools",
+    "title": "Terminal",
+    "viewBox": "0 0 15 16",
+    "path": "M5.223 8.777l-4.161 4.161q-0.089 0.089-0.205 0.089t-0.205-0.089l-0.446-0.446q-0.089-0.089-0.089-0.205t0.089-0.205l3.509-3.509-3.509-3.509q-0.089-0.089-0.089-0.205t0.089-0.205l0.446-0.446q0.089-0.089 0.205-0.089t0.205 0.089l4.161 4.161q0.089 0.089 0.089 0.205t-0.089 0.205zM14.857 12.857v0.571q0 0.125-0.080 0.205t-0.205 0.080h-8.571q-0.125 0-0.205-0.080t-0.080-0.205v-0.571q0-0.125 0.080-0.205t0.205-0.080h8.571q0.125 0 0.205 0.080t0.080 0.205z"
+  },
   "EXECUTE": {
     "section": "Tools",
     "viewBox": "0 0 16 16",
diff --git a/ivette/src/dome/src/renderer/dome.js b/ivette/src/dome/src/renderer/dome.js
index 81de82545f3b09c424f98fb8c1b1710362bbf3e1..8dbe53f7deaa69d480c2903332fbfe695e10bf64 100644
--- a/ivette/src/dome/src/renderer/dome.js
+++ b/ivette/src/dome/src/renderer/dome.js
@@ -738,74 +738,6 @@ export function useSwitch( settings, defaultValue=false )
   return [ value, v => update(v===undefined ? !value : v) ];
 }
 
-/**
-   @summary Local state for managing history.
-   @param {string} [settings] - global window settings to store the history
-   @param {any} [defaultValue] - default current value
-   @return {object} for managing the history
-   @description
-The returned object contains the following properties:
-
-- `current` the current value in the history
-- `prev:()=>value` optional function for shifting to the next item (if any)
-- `next:()=>value` optional function for shifting to the previous item (if any)
-- `length:number` history size
-- `index:number` position in the history (starting from `0`)
-- `insert:(v)=>value` insert value `v` as the new current value
-- `fork:(v)=>value` insert value `v` but clear next items (if any)
-- `add:(v)=>value` insert value `v` in front of the history
-- `update:(v)=>value` only change the current value
-- `clear:()=>void` clear the history
-
-When inserting a value, previous and current values are kept in the history.
-When updating or insering a falsy value, the history is shifted to the next
-or previous element available in the history. You can drop the current value from the history
-with `update(undefined)`.
-All updating functions return the new current value.
-*/
-
-export function useHistory( settings, defaultValue )
-{
-  const [ { next,prev,current } , setHistory ] =
-        useSettings( true, settings, { next:[], prev:[], current:defaultValue } );
-  const setState = ({ next, prev, current }) => {
-    while( !current && next.length ) current = next.shift();
-    while( !current && prev.length ) current = prev.shift();
-    setHistory({next: next.slice(), prev: prev.slice(), current });
-    return current ;
-  };
-  const insert = (v) => {
-    if (current) prev.unshift(current);
-    return setState({ next, prev, current:v });
-  };
-  const fork = (v) => {
-    if (current) prev.unshift(current);
-    return setState({ next:[], prev, current:v });
-  };
-  const append = (v) => {
-    return setState({next:[], prev: next.reverse().append(current || [],next), current:v });
-  };
-  const move = ( src , tgt ) => src.length ? (() => {
-    let v = src.shift();
-    if (current) tgt.unshift(current);
-    return setState({ current:v, prev, next });
-  }) : undefined;
-  const update = (v) => setState({ next, prev, current:v });
-  const clear = (next.length || prev.length || current) && (() => setState({ next:[], prev:[] }));
-  return {
-    length:next.length + prev.length + (current ? 1 : 0),
-    index:next.length,
-    current,
-    insert,
-    fork,
-    append,
-    update,
-    next:move(next,prev),
-    prev:move(prev,next),
-    clear
-  };
-}
-
 /**
    @summary Local state with global settings (Custom React Hook).
    @param {string} settings - global settings for storing the value
diff --git a/ivette/src/dome/src/renderer/text/buffers.js b/ivette/src/dome/src/renderer/text/buffers.js
index bf6a2f190b7cef9f13fcb453838b51250dcfaed0..30c272d3f99a511b7d3b98b31320ac37bd418b8d 100644
--- a/ivette/src/dome/src/renderer/text/buffers.js
+++ b/ivette/src/dome/src/renderer/text/buffers.js
@@ -95,9 +95,12 @@ export class RichTextBuffer extends Emitter {
     this.setFocused = this.setFocused.bind(this);
     this.clear = this.clear.bind(this);
     this.append = this.append.bind(this);
+    this.setValue = this.setValue.bind(this);
+    this.getValue = this.getValue.bind(this);
     this.log = this.log.bind(this);
     this._doc.on('change', ( _target , { origin } ) => {
       if (origin !== 'buffer') this.setEdited(true);
+      this.emit('change');
     });
   }
 
@@ -119,14 +122,7 @@ export class RichTextBuffer extends Emitter {
   // --------------------------------------------------------------------------
 
   /** Clear buffer contents and resets _edited_ state. */
-  clear() {
-    this._doc.setValue('');
-    this._edited = false;
-    this._stacked = [] ;
-    this._focused = false ;
-    this._markid = 0 ;
-    this._markers = {} ;
-  }
+  clear() { this.setValue(''); }
 
   /**
      @summary Writes in the buffer.
@@ -171,6 +167,27 @@ export class RichTextBuffer extends Emitter {
     this.scroll();
   }
 
+  /**
+     @summary Replace textual content with the given value.
+     @param {string} [txt] - new text content
+     @description
+     Also remove all markers.
+   */
+  setValue(txt='') {
+    this._doc.setValue(txt);
+    this._edited = false;
+    this._stacked = [] ;
+    this._focused = false ;
+    this._markid = 0 ;
+    this._markers = {} ;
+  }
+
+  /**
+     @summary Return textual content.
+     @return {string}
+  */
+  getValue() { return this._doc.getValue(); }
+
   /**
      @summary Opens a text marker.
      @param {object} options - CodeMirror
diff --git a/ivette/src/frama-c/style.css b/ivette/src/frama-c/style.css
index f918358dc93d2336f9dc83ae63778c1913374f26..79a3307a70214e7304248a083e1ae2f724f90f34 100644
--- a/ivette/src/frama-c/style.css
+++ b/ivette/src/frama-c/style.css
@@ -37,11 +37,6 @@
     height: 24px ;
 }
 
-.labview-titlebar .dome-xLabel
-{
-    padding-top: 4px ;
-}
-
 .labview-handle
 {
     flex: 1 1 auto ;
diff --git a/ivette/src/renderer/Controller.tsx b/ivette/src/renderer/Controller.tsx
index 60387fdd6c8e15333575e7cf1855d879a70a1036..d463bdbac3b9829a3dfef68c950f8e0d40a912d6 100644
--- a/ivette/src/renderer/Controller.tsx
+++ b/ivette/src/renderer/Controller.tsx
@@ -20,32 +20,31 @@ import 'codemirror/theme/ambiance.css';
 // --- Configure Server
 // --------------------------------------------------------------------------
 
-let cmdConfig: Server.Configuration;
-const cmdLine = new RichTextBuffer();
+const quoteRe = new RegExp('^[-./:a-zA-Z0-9]+$');
+const quote = (s: string) => (quoteRe.test(s) ? s : `"${s}"`);
 
-function dumpCmdLine(sc: Server.Configuration): void {
+function dumpServerConfig(sc: Server.Configuration): string {
+  let buffer = '';
   const { cwd, command, sockaddr, params } = sc;
-  cmdLine.clear();
-  if (cwd) cmdLine.log('--cwd', cwd);
-  if (command) cmdLine.log('--command', command);
-  if (sockaddr) cmdLine.log('--socket', sockaddr);
+  if (cwd) buffer += `--cwd ${quote(cwd)}\n`;
+  if (command) buffer += `--command ${command}\n`;
+  if (sockaddr) buffer += `--socket ${sockaddr}\n`;
   if (params) {
     params.forEach((v: string, i: number) => {
       if (i > 0) {
         if (v.startsWith('-') || v.endsWith('.c')
           || v.endsWith('.h') || v.endsWith('.i')) {
-          cmdLine.append('\n');
-        } else {
-          cmdLine.append(' ');
-        }
+          buffer += '\n';
+        } else
+          buffer += ' ';
       }
-      cmdLine.append(v);
+      buffer += v;
     });
   }
-  cmdLine.append('\n');
+  return buffer;
 }
 
-function buildServerConfiguration(argv: string[], cwd?: string) {
+function buildServerConfig(argv: string[], cwd?: string) {
   const params = [];
   let command;
   let sockaddr;
@@ -77,10 +76,23 @@ function buildServerConfiguration(argv: string[], cwd?: string) {
   };
 }
 
+function insertConfig(hs: string[], cfg: Server.Configuration) {
+  const cmd = dumpServerConfig(cfg).trim();
+  const newhs =
+    hs.map((h) => h.trim())
+      .filter((h: string) => h !== cmd && h !== '')
+      .slice(0, 50);
+  newhs.unshift(cmd);
+  return newhs;
+}
+
+// --------------------------------------------------------------------------
+// --- Start Server on Command
+// --------------------------------------------------------------------------
+
 Dome.onCommand((argv: string[], cwd: string) => {
-  cmdConfig = buildServerConfiguration(argv, cwd);
-  dumpCmdLine(cmdConfig);
-  Server.configure(cmdConfig);
+  const cfg = buildServerConfig(argv, cwd);
+  Server.configure(cfg);
   Server.start();
 });
 
@@ -136,99 +148,146 @@ export const Control = () => {
 // --- Server Console
 // --------------------------------------------------------------------------
 
-function getCmdLine() {
-  return cmdLine.getDoc().getValue().trim();
-}
-
-function execCmdLine(cmd: string) {
-  const argv = cmd.split(/[ \t\n]+/);
-  const cfg = buildServerConfiguration(argv);
-  Server.configure(cfg);
-  Server.restart();
-}
+const editor = new RichTextBuffer();
 
 const RenderConsole = () => {
-  const [command, switchCmd] = Dome.useSwitch();
-  const { current, next, prev, index, length, update, insert, clear }: any =
-    Dome.useHistory('frama-c.command.history');
+  const scratch = React.useRef([] as string[]);
+  const [cursor, setCursor] = React.useState(-1);
+  const [H0, setH0] = Dome.useState('Controller.history', []);
+  const [isEmpty, setEmpty] = React.useState(true);
+  const [noTrash, setNoTrash] = React.useState(true);
+
+  // Cope with merge settings that keeps previous array entries (BUG in DOME)
+  const history = Array.isArray(H0) ? H0.filter((h) => h !== '') : [];
+  const setHistory = (hs: string[]) => {
+    const n = hs.length;
+    setH0(n < 50 ? hs.concat(Array(50 - n).fill('')) : hs);
+  };
+
+  Dome.useEmitter(editor, 'change', () => {
+    const cmd = editor.getValue().trim();
+    setEmpty(cmd === '');
+    setNoTrash(cursor === 0 && history.length === 1 && cmd === history[0]);
+  });
+
+  const doReload = () => {
+    const cfg = Server.getConfig();
+    const hst = insertConfig(history, cfg);
+    scratch.current = hst.slice();
+    editor.setValue(hst[0]);
+    setHistory(hst);
+    setCursor(0);
+  };
+
+  const doSwitch = () => {
+    if (cursor < 0) doReload();
+    else {
+      editor.clear();
+      scratch.current = [];
+      setCursor(-1);
+    }
+  };
 
   const doExec = () => {
-    const cmdline = getCmdLine();
-    if (cmdline !== current) insert(cmdline);
-    execCmdLine(cmdline);
-    switchCmd();
+    const cmd = editor.getValue().trim();
+    const argv = cmd.split(/[ \t\n]+/);
+    const cfg = buildServerConfig(argv);
+    const hst = insertConfig(history, cfg);
+    setHistory(hst);
+    setCursor(-1);
+    editor.clear();
+    Server.configure(cfg);
+    Server.restart();
+  };
+
+  const doMove = (target: number) => {
+    if (0 <= target && target < history.length && target !== cursor)
+      return () => {
+        const cmd = editor.getValue();
+        const pad = scratch.current;
+        pad[cursor] = cmd;
+        editor.setValue(pad[target]);
+        setCursor(target);
+      };
+    return undefined;
   };
-  const doNext = () => { cmdLine.getDoc().setValue(next() || ''); };
-  const doPrev = () => { cmdLine.getDoc().setValue(prev() || ''); };
-  const doReload = () => { dumpCmdLine(cmdConfig); };
-  const doDrop = () => {
-    cmdLine.clear();
-    cmdLine.getDoc().setValue(update(undefined) || '');
+
+  const doRemove = () => {
+    const n = history.length;
+    if (n <= 1) doReload();
+    else {
+      const hst = history.slice();
+      const pad = scratch.current;
+      hst.splice(cursor, 1);
+      pad.splice(cursor, 1);
+      setHistory(hst);
+      const next = cursor > 0 ? cursor - 1 : 0;
+      editor.setValue(pad[next]);
+      setCursor(next);
+    }
   };
+
+  const doPrev = doMove(cursor + 1);
+  const doNext = doMove(cursor - 1);
+  const edited = 0 <= cursor;
+  const n = history.length;
+
   return (
     <>
-      <TitleBar label={command ? 'Command Line' : 'Console'}>
-        <Label
-          className="dimmed"
-          display={command && length > 0}
-          title="Rank in History"
-        >
-          {1 + index}/{length}
-        </Label>
-        <Space />
+      <TitleBar label={edited ? 'Command line' : 'Console'}>
         <IconButton
           icon="TRASH"
-          display={command && clear}
-          disabled={!clear}
-          onClick={clear}
-          title="Clear History"
+          display={edited}
+          disabled={noTrash}
+          onClick={doRemove}
+          title="Discard command from history (irreversible)"
         />
+        <Space />
         <IconButton
-          icon="CROSS"
-          display={command && clear}
-          disabled={!current}
-          onClick={doDrop}
-          title="Remove Command"
+          icon="RELOAD"
+          display={edited}
+          onClick={doReload}
+          title="Discard changes"
         />
-        <Space />
         <IconButton
           icon="MEDIA.PREV"
-          display={command}
-          disabled={!prev}
+          display={edited}
           onClick={doPrev}
-          title="Previous Command"
-        />
-        <IconButton
-          icon="RELOAD"
-          display={command}
-          onClick={doReload}
-          title="Reset Command Line"
+          title="Previous command"
         />
         <IconButton
           icon="MEDIA.NEXT"
-          display={command}
-          disabled={!next}
+          display={edited}
           onClick={doNext}
-          title="Next Command"
+          title="Next command"
         />
         <Space />
+        <Label
+          className="controller-rank"
+          title="History (last command first)"
+          display={edited}
+        >
+          {1 + cursor} / {n}
+        </Label>
+        <Space />
         <IconButton
           icon="MEDIA.PLAY"
-          display={command}
+          display={edited}
+          disabled={isEmpty}
           onClick={doExec}
-          title="Execute Command"
+          title="Execute command"
         />
         <IconButton
-          icon="EDIT"
-          selected={command}
-          onClick={switchCmd}
-          title="Edit Command"
+          icon="TERMINAL"
+          selected={edited}
+          onClick={doSwitch}
+          title="Toggle command line editing"
         />
       </TitleBar>
       <Text
-        buffer={command ? cmdLine : Server.buffer}
+        buffer={edited ? editor : Server.buffer}
         mode="text"
-        readOnly={!command}
+        readOnly={!edited}
         theme="ambiance"
       />
     </>
diff --git a/ivette/src/renderer/style.css b/ivette/src/renderer/style.css
index de68fb842c2f4d783486ba20e1ab3b8a65b00792..9b3d079f492d510d53a4b2e990813dd4d2c2103b 100644
--- a/ivette/src/renderer/style.css
+++ b/ivette/src/renderer/style.css
@@ -2,8 +2,9 @@
 /* --- Frama-C Styling                                                    --- */
 /* -------------------------------------------------------------------------- */
 
-.dimmed {
+.controller-rank {
     color: #888 ;
+    min-width: 50px;
 }
 
 /* -------------------------------------------------------------------------- */