Agent Zero AI framework
Align Time Travel modal with settings shell
Remove Time Travel's floating modal behavior so it uses the standard centered modal shell and shared backdrop like Settings. Keep the modal-only sizing classes on the component document and stop exempting Time Travel from backdrop rendering.
Alessandro committed
May 5, 2026 at 17:33 UTC
278a696b8ec6534f5c10e227d0ee011b1a051c2a
4 files changed
+5
-115
plugins/_time_travel/webui/main.html
+2
-2
@@ -1,8 +1,8 @@
1
-<html>
1
+<html class="time-travel-modal">
2
<head>
3
<title>Time Travel</title>
4
</head>
5
-<body>
5
+<body class="time-travel-modal-body">
6
<x-component path="/plugins/_time_travel/webui/time-travel-panel.html" mode="modal"></x-component>
7
</body>
8
</html>
plugins/_time_travel/webui/time-travel-panel.html
+2
-13
@@ -242,26 +242,15 @@
242
min-height: min(520px, calc(100vh - 16px));
243
max-width: calc(100vw - 16px);
244
max-height: calc(100vh - 16px);
245
- resize: both;
245
border: 1px solid color-mix(in srgb, var(--color-border) 75%, transparent);
246
border-radius: 7px;
247
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.32);
248
background: color-mix(in srgb, var(--color-background) 94%, #000 6%);
249
}
250
252
- .modal.modal-floating {
253
- pointer-events: none;
254
- }
255
-
256
- .modal.modal-floating .modal-inner {
257
- pointer-events: auto;
258
- }
259
-
251
.modal-inner.time-travel-modal .modal-header {
261
- min-height: 34px;
262
- padding: 0.35rem 0.75rem 0.35rem 1rem;
263
- cursor: move;
264
- user-select: none;
252
+ min-height: 42px;
253
+ padding: 0.45rem 0.75rem 0.45rem 1rem;
254
background: color-mix(in srgb, var(--color-background) 92%, #000 8%);
255
border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent);
256
}
plugins/_time_travel/webui/time-travel-store.js
+1
-98
@@ -49,7 +49,6 @@ const model = {
49
_mode: "canvas",
50
_refreshTimer: null,
51
_filterTimer: null,
52
- _floatingCleanup: null,
52
_requestSeq: 0,
53
_diffSeq: 0,
54
@@ -60,9 +59,7 @@ const model = {
59
async onMount(element = null, options = {}) {
60
if (element) this._root = element;
61
this._mode = options?.mode === "modal" ? "modal" : "canvas";
63
- if (this._mode === "modal") {
64
- this.setupFloatingModal(element);
65
- } else {
62
+ if (this._mode !== "modal") {
63
this.setupCanvasSurface(element);
64
}
65
this.contextId = this.resolveContextId();
@@ -85,103 +82,9 @@ const model = {
82
clearTimeout(this._filterTimer);
83
this._filterTimer = null;
84
}
88
- this._floatingCleanup?.();
89
- this._floatingCleanup = null;
90
- },
91
-
92
- setupFloatingModal(element = null) {
93
- this._floatingCleanup?.();
94
- const root = element || globalThis.document?.querySelector(".time-travel-panel");
95
- const modal = root?.closest?.(".modal");
96
- const inner = modal?.querySelector?.(".modal-inner");
97
- const body = modal?.querySelector?.(".modal-bd");
98
- const header = modal?.querySelector?.(".modal-header");
99
- if (!modal || !inner || !header) return;
100
- modal.classList.add("modal-floating");
101
- inner.classList.add("time-travel-modal", "modal-no-backdrop");
102
- body?.classList?.add("time-travel-modal-body");
103
-
104
- const rect = inner.getBoundingClientRect();
105
- inner.style.left = `${Math.max(8, rect.left)}px`;
106
- inner.style.top = `${Math.max(8, rect.top)}px`;
107
- inner.style.transform = "none";
108
-
109
- let drag = null;
110
- let resizeObserver = null;
111
- const viewportGap = 8;
112
- const clampPosition = (left, top) => {
113
- const bounds = inner.getBoundingClientRect();
114
- const maxLeft = Math.max(viewportGap, globalThis.innerWidth - bounds.width - viewportGap);
115
- const maxTop = Math.max(viewportGap, globalThis.innerHeight - bounds.height - viewportGap);
116
- return {
117
- left: Math.min(Math.max(viewportGap, left), maxLeft),
118
- top: Math.min(Math.max(viewportGap, top), maxTop),
119
- };
120
- };
121
- const clampGeometry = () => {
122
- const bounds = inner.getBoundingClientRect();
123
- const maxWidth = Math.max(360, globalThis.innerWidth - viewportGap * 2);
124
- const maxHeight = Math.max(420, globalThis.innerHeight - viewportGap * 2);
125
- if (bounds.width > maxWidth) inner.style.width = `${maxWidth}px`;
126
- if (bounds.height > maxHeight) inner.style.height = `${maxHeight}px`;
127
- const next = clampPosition(bounds.left, bounds.top);
128
- inner.style.left = `${next.left}px`;
129
- inner.style.top = `${next.top}px`;
130
- inner.style.maxWidth = `${Math.max(360, globalThis.innerWidth - next.left - viewportGap)}px`;
131
- inner.style.maxHeight = `${Math.max(420, globalThis.innerHeight - next.top - viewportGap)}px`;
132
- };
133
- clampGeometry();
134
- globalThis.addEventListener("resize", clampGeometry);
135
- if (globalThis.ResizeObserver) {
136
- resizeObserver = new ResizeObserver(clampGeometry);
137
- resizeObserver.observe(inner);
138
- }
139
-
140
- const onPointerMove = (event) => {
141
- if (!drag) return;
142
- const next = clampPosition(drag.left + event.clientX - drag.x, drag.top + event.clientY - drag.y);
143
- inner.style.left = `${next.left}px`;
144
- inner.style.top = `${next.top}px`;
145
- clampGeometry();
146
- };
147
- const onPointerUp = () => {
148
- drag = null;
149
- globalThis.removeEventListener("pointermove", onPointerMove);
150
- globalThis.removeEventListener("pointerup", onPointerUp);
151
- try {
152
- header.releasePointerCapture?.(header.__timeTravelPanelPointerId || 0);
153
- } catch {}
154
- };
155
- const onPointerDown = (event) => {
156
- if (event.button !== 0) return;
157
- if (event.target?.closest?.("button, input, select, textarea, a")) return;
158
- const current = inner.getBoundingClientRect();
159
- drag = {
160
- x: event.clientX,
161
- y: event.clientY,
162
- left: current.left,
163
- top: current.top,
164
- };
165
- header.__timeTravelPanelPointerId = event.pointerId;
166
- header.setPointerCapture?.(event.pointerId);
167
- globalThis.addEventListener("pointermove", onPointerMove);
168
- globalThis.addEventListener("pointerup", onPointerUp);
169
- event.preventDefault();
170
- };
171
- header.addEventListener("pointerdown", onPointerDown);
172
-
173
- this._floatingCleanup = () => {
174
- header.removeEventListener("pointerdown", onPointerDown);
175
- globalThis.removeEventListener("pointermove", onPointerMove);
176
- globalThis.removeEventListener("pointerup", onPointerUp);
177
- globalThis.removeEventListener("resize", clampGeometry);
178
- resizeObserver?.disconnect?.();
179
- };
85
},
86
87
setupCanvasSurface(element = null) {
183
- this._floatingCleanup?.();
184
- this._floatingCleanup = null;
88
if (element) this._root = element;
89
},
90
webui/js/modals.js
-2
@@ -139,8 +139,6 @@ function modalSuppressesBackdrop(modal) {
139
|| path === "plugins/_browser/webui/main.html"
140
|| path === "/plugins/_office/webui/main.html"
141
|| path === "plugins/_office/webui/main.html"
142
- || path === "/plugins/_time_travel/webui/main.html"
143
- || path === "plugins/_time_travel/webui/main.html"
142
|| modal?.element?.classList?.contains("modal-floating")
143
|| modal?.element?.classList?.contains("modal-no-backdrop")
144
|| modal?.inner?.classList?.contains("modal-no-backdrop");