Make Time Travel modal-only

Add a Time Travel entry directly under Files in the sidebar dropdown and route it through the existing modal. Stop Time Travel from registering or mounting a right-canvas surface, and keep modal refresh tied to the modal state.

Alessandro committed May 2, 2026 at 17:20 UTC ae94f158df8a4f2cb6ba7eb1866d6ea09ffad57a
5 files changed +19 -47
plugins/_time_travel/extensions/webui/apply_snapshot_before/refresh-time-travel.js
+3 -2
@@ -1,7 +1,8 @@
1 export default function refreshTimeTravelOnContextChange(ctx) {
2 const store = globalThis.Alpine?.store?.("timeTravel");
3 - const canvas = globalThis.Alpine?.store?.("rightCanvas");
4 - if (!store || !canvas?.isOpen || canvas.activeSurfaceId !== "time-travel") return;
3 + const modalOpen = globalThis.isModalOpen?.("/plugins/_time_travel/webui/main.html")
4 + || globalThis.isModalOpen?.("plugins/_time_travel/webui/main.html");
5 + if (!store || !modalOpen) return;
6 const nextContextId = String(ctx?.snapshot?.context || "");
7 if (nextContextId && nextContextId !== store.contextId) {
8 store.scheduleRefresh({ contextId: nextContextId, reason: "context-change" });
plugins/_time_travel/extensions/webui/right-canvas-panels/time-travel-panel.html
+1 -8
@@ -1,8 +1 @@
1 -<div
2 - class="right-canvas-surface-panel time-travel-canvas-surface"
3 - data-surface-id="time-travel"
4 - x-show="$store.rightCanvas && $store.rightCanvas.isOpen && $store.rightCanvas.activeSurfaceId === 'time-travel'"
5 - style="display: none;"
6 ->
7 - <x-component path="/plugins/_time_travel/webui/time-travel-panel.html" mode="canvas"></x-component>
8 -</div>
1 +<!-- Time Travel is modal-only; the right canvas must not mount its panel. -->
plugins/_time_travel/extensions/webui/right_canvas_register_surfaces/register-time-travel.js
+2 -36
@@ -1,37 +1,3 @@
1 -function waitForElement(selector, timeoutMs = 3000) {
2 - const found = document.querySelector(selector);
3 - if (found) return Promise.resolve(found);
4 - return new Promise((resolve) => {
5 - const timeout = globalThis.setTimeout(() => {
6 - observer.disconnect();
7 - resolve(document.querySelector(selector));
8 - }, timeoutMs);
9 - const observer = new MutationObserver(() => {
10 - const element = document.querySelector(selector);
11 - if (!element) return;
12 - globalThis.clearTimeout(timeout);
13 - observer.disconnect();
14 - resolve(element);
15 - });
16 - observer.observe(document.body, { childList: true, subtree: true });
17 - });
18 -}
19 -
20 -export default async function registerTimeTravelSurface(canvas) {
21 - canvas.registerSurface({
22 - id: "time-travel",
23 - title: "Time Travel",
24 - icon: "history",
25 - order: 30,
26 - modalPath: "/plugins/_time_travel/webui/main.html",
27 - async open(payload = {}) {
28 - await waitForElement('[data-surface-id="time-travel"] .time-travel-panel');
29 - const store = globalThis.Alpine?.store?.("timeTravel");
30 - await store?.onOpen?.(payload);
31 - },
32 - async close() {
33 - const store = globalThis.Alpine?.store?.("timeTravel");
34 - store?.cleanup?.();
35 - },
36 - });
1 +export default async function registerTimeTravelSurface() {
2 + // Time Travel is opened from the sidebar dropdown as a modal-only surface.
3 }
plugins/_time_travel/extensions/webui/sidebar-quick-actions-dropdown-start/time-travel-entry.html new
+12
@@ -0,0 +1,12 @@
1 +<!-- Time Travel -->
2 +<div x-data>
3 + <button
4 + x-move-after="#sidebar-files-dropdown"
5 + class="dropdown-item"
6 + id="time-travel-dropdown"
7 + title="Time Travel"
8 + @click="ensureModalOpen('/plugins/_time_travel/webui/main.html'); $store.sidebar.menuClose()">
9 + <span class="material-symbols-outlined">history</span>
10 + <span>Time Travel</span>
11 + </button>
12 +</div>
webui/components/sidebar/top-section/header-icons.html
+1 -1
@@ -83,7 +83,7 @@
83 <span>Projects</span>
84 </button>
85
86 - <button class="dropdown-item" @click="$store.chatInput.browseFiles(); $store.sidebar.menuClose()">
86 + <button id="sidebar-files-dropdown" class="dropdown-item" @click="$store.chatInput.browseFiles(); $store.sidebar.menuClose()">
87 <span class="material-symbols-outlined">folder</span>
88 <span>Files</span>
89 </button>