diff --git a/ivette/src/dome/src/renderer/data/states.ts b/ivette/src/dome/src/renderer/data/states.ts
index c1fbb827435c3b1f721268d059e9a4640d7aeb0b..3e07ef8cb453d111db4d9e2d5e47c48e5ae83c12 100644
--- a/ivette/src/dome/src/renderer/data/states.ts
+++ b/ivette/src/dome/src/renderer/data/states.ts
@@ -17,8 +17,49 @@ import * as JSON from './json';
 
 const UPDATE = 'dome.states.update';
 
+/** State interface. */
+export type State<A> = [A, (update: A) => void];
+
+/** State field of an object state. */
+export function key<A, K extends keyof A>(
+  state: State<A>,
+  key: K,
+): State<A[K]> {
+  const [props, setProps] = state;
+  return [props[key], (value: A[K]) => {
+    const newProps = Object.assign({}, props);
+    newProps[key] = value;
+    setProps(newProps);
+  }];
+}
+
+/** State index of an array state. */
+export function index<A>(
+  state: State<A[]>,
+  index: number,
+): State<A> {
+  const [array, setArray] = state;
+  return [array[index], (value: A) => {
+    const newArray = array.slice();
+    newArray[index] = value;
+    setArray(newArray);
+  }];
+}
+
+/** Log state updates in the console. */
+export function debug<A>(msg: string, st: State<A>): State<A> {
+  const [value, setValue] = st;
+  return [value, (v) => { console.log(msg, v); setValue(v); }];
+}
+
+/** Purely local value. No hook, no events, just a ref. */
+export function local<A>(init: A): State<A> {
+  const ref = { current: init };
+  return [ref.current, (v) => ref.current = v];
+}
+
 /** Cross-component State. */
-export class State<A> {
+export class GlobalState<A> {
 
   private value: A;
   private emitter: Emitter;
@@ -53,8 +94,10 @@ export class State<A> {
 
 }
 
-/** React Hook, similar to `React.useState()`. */
-export function useState<A>(s: State<A>): [A, (update: A) => void] {
+/** React Hook, similar to `React.useState()`.
+    Assignments to the global state also update _all_
+    its associated hooks and listeners. */
+export function useGlobalState<A>(s: GlobalState<A>): State<A> {
   const [current, setCurrent] = React.useState<A>(s.getValue);
   React.useEffect(() => {
     s.on(setCurrent);
diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts
index ea1737caee51e08a7610dc7560ffba30cf91c236..3474e9acd6cd3c21998f410e6a8a3d1381e1733f 100644
--- a/ivette/src/frama-c/states.ts
+++ b/ivette/src/frama-c/states.ts
@@ -13,7 +13,7 @@ import React from 'react';
 import * as Dome from 'dome';
 import * as Json from 'dome/data/json';
 import { Order } from 'dome/data/compare';
-import * as GlobalStates from 'dome/data/states';
+import { GlobalState, useGlobalState } from 'dome/data/states';
 import { useModel } from 'dome/table/models';
 import { CompactModel } from 'dome/table/arrays';
 import * as Server from './server';
@@ -534,7 +534,7 @@ function reducer(s: Selection, action: SelectionActions): Selection {
   }
 }
 
-const GlobalSelection = new GlobalStates.State<Selection>({
+const GlobalSelection = new GlobalState<Selection>({
   current: undefined,
   prevSelections: [],
   nextSelections: [],
@@ -544,7 +544,7 @@ const GlobalSelection = new GlobalStates.State<Selection>({
    Current selection.
  */
 export function useSelection(): [Selection, (a: SelectionActions) => void] {
-  const [selection, setSelection] = GlobalStates.useState(GlobalSelection);
+  const [selection, setSelection] = useGlobalState(GlobalSelection);
 
   function update(action: SelectionActions) {
     const nextSelection = reducer(selection, action);