diff --git a/ivette/src/frama-c/api/generated/kernel/ast/index.ts b/ivette/src/frama-c/api/generated/kernel/ast/index.ts
index f72426c5a3222d23e3d51df6f2ef0807fd723987..f5b3bee763af939ac7897df4dcfbe9ee6e75e19e 100644
--- a/ivette/src/frama-c/api/generated/kernel/ast/index.ts
+++ b/ivette/src/frama-c/api/generated/kernel/ast/index.ts
@@ -71,6 +71,11 @@ const compute_internal: Server.ExecRequest<null,null> = {
 /** Ensures that AST is computed */
 export const compute: Server.ExecRequest<null,null>= compute_internal;
 
+/** Emitted when the AST has been computed */
+export const computed: Server.Signal = {
+  name: 'kernel.ast.computed',
+};
+
 /** Marker kind */
 export enum markerKind {
   /** Expression */
diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx
index 2f2d9bbae19f40609fe3f95f0526a67ca9ada098..d127bba677eb8472f55e5ea035994f2b5e0f89fc 100644
--- a/ivette/src/frama-c/kernel/ASTview.tsx
+++ b/ivette/src/frama-c/kernel/ASTview.tsx
@@ -201,14 +201,20 @@ export default function ASTview() {
     return () => { buffer.off('change', setBullets); };
   }, [buffer, setBullets]);
 
+  async function reload() {
+    printed.current = theFunction;
+    loadAST(buffer, theFunction, theMarker);
+  }
+
   // Hook: async loading
   React.useEffect(() => {
-    if (printed.current !== theFunction) {
-      printed.current = theFunction;
-      loadAST(buffer, theFunction, theMarker);
-    }
+    if (printed.current !== theFunction)
+      reload();
   });
 
+  // Also reload the buffer when the AST is recomputed.
+  Server.onSignal(Ast.computed, reload);
+
   React.useEffect(() => {
     const decorator = (marker: string) => {
       if (multipleSelections?.some((location) => location?.marker === marker))
diff --git a/ivette/src/frama-c/menu.ts b/ivette/src/frama-c/menu.ts
index 0fa355294406c4d3ae5a615dc883c9f37653dcfd..66fc3b9a8b54b21d42554817124ab7a1ef121990 100644
--- a/ivette/src/frama-c/menu.ts
+++ b/ivette/src/frama-c/menu.ts
@@ -28,7 +28,6 @@ import * as Dome from 'dome';
 import * as Dialogs from 'dome/dialogs';
 import * as Server from 'frama-c/server';
 import * as Ast from 'frama-c/api/kernel/ast';
-import * as States from 'frama-c/states';
 
 const cFilter = {
   name: 'C source files',
@@ -46,8 +45,6 @@ async function setFiles(): Promise<void> {
   });
   await Server.send(Ast.setFiles, files);
   await Server.send(Ast.compute, { });
-  const main = await Server.send(Ast.getMainFunction, { });
-  States.setSelection({ fct: main });
   return;
 }
 
diff --git a/ivette/src/frama-c/states.ts b/ivette/src/frama-c/states.ts
index 876761db7b47e3b4381140df8f8739838bd794b0..39786ef5aadea06bc25b51532aa4b0c9e0fa72c9 100644
--- a/ivette/src/frama-c/states.ts
+++ b/ivette/src/frama-c/states.ts
@@ -772,4 +772,21 @@ export function useSelection(): [Selection, (a: SelectionActions) => void] {
   return [current, (action) => setCurrent(reducer(current, action))];
 }
 
+/* Select the main function when the AST is recomputed, or when the current
+   project changes and the selection is still empty. */
+async function selectMainFunction() {
+  const main = await Server.send(Ast.getMainFunction, { });
+  const selection = {
+    ...emptySelection,
+    current: { fct: main },
+  };
+  GlobalSelection.setValue(selection);
+}
+Server.onSignal(Ast.computed, selectMainFunction);
+PROJECT.on(async () => {
+  const selection = GlobalSelection.getValue();
+  if (selection === emptySelection)
+    selectMainFunction();
+});
+
 // --------------------------------------------------------------------------
diff --git a/src/plugins/server/kernel_ast.ml b/src/plugins/server/kernel_ast.ml
index 10c62c2128a474836bcf83a0c95d42cdf6754fb5..6a83fdf6c71401cc0a5d945c300c12071d07125a 100644
--- a/src/plugins/server/kernel_ast.ml
+++ b/src/plugins/server/kernel_ast.ml
@@ -37,6 +37,11 @@ let () = Request.register ~package
     ~descr:(Md.plain "Ensures that AST is computed")
     ~input:(module Junit) ~output:(module Junit) Ast.compute
 
+let computed_signal = Request.signal ~package ~name:"computed"
+    ~descr:(Md.plain "Emitted when the AST has been computed")
+
+let () = Ast.apply_after_computed (fun _ -> Request.emit computed_signal)
+
 let ast_update_hook f =
   Ast.add_hook_on_update f;
   Ast.apply_after_computed (fun _ -> f ())