main
html 275 lines 12.6 KB
Raw
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
7 <title>Agent Zero</title>
8
9 <style id="startup-transition-critical">
10 #startup-transition {
11 position: fixed;
12 inset: 0;
13 z-index: 2147483647;
14 display: grid;
15 place-items: center;
16 margin: 0;
17 background: #131313;
18 color: #fff;
19 opacity: 1;
20 transition: opacity 240ms ease;
21 }
22 #startup-transition[data-theme="light"],
23 body.light-mode #startup-transition { background: #fafafa; color: #000; }
24 #startup-transition > svg {
25 width: clamp(12rem, 34vw, 23rem);
26 max-width: 72vw;
27 height: auto;
28 opacity: 1;
29 animation: none;
30 }
31 #startup-transition > p {
32 position: absolute;
33 width: 1px;
34 height: 1px;
35 overflow: hidden;
36 clip-path: inset(50%);
37 }
38 #startup-transition.startup-transition-leaving {
39 opacity: 0;
40 pointer-events: none;
41 }
42 @media (prefers-reduced-motion: reduce) {
43 #startup-transition { transition: none; }
44 }
45 </style>
46
47 <script>
48 if (location.pathname === "/safe") {
49 const safeUrl = new URL(location.href)
50 safeUrl.searchParams.delete("__direct")
51 history.replaceState(null, "", `${safeUrl.pathname}${safeUrl.search}${safeUrl.hash}`)
52 if ("serviceWorker" in navigator) {
53 navigator.serviceWorker.getRegistrations()
54 .then((registrations) => Promise.allSettled(
55 registrations.map((registration) => registration.unregister()),
56 ))
57 .catch((error) => console.warn("Unable to disable service workers.", error))
58 }
59 }
60 </script>
61
62 <link rel="icon" type="image/svg+xml" href="public/favicon.svg">
63 <link rel="preload" href="/vendor/google/google-icons.woff2" as="font" type="font/woff2" crossorigin>
64 <link rel="stylesheet" href="vendor/google/google-icons.css">
65 <script type="module" src="/js/icons.js"></script>
66 <script>
67 (() => {
68 const root = document.documentElement
69 let applicationReady = false
70 let revealStarted = false
71
72 const revealApplication = (force = false) => {
73 if (revealStarted || (!force && !applicationReady)) return
74 const overlay = document.getElementById("startup-transition")
75 if (!overlay) return
76 revealStarted = true
77 requestAnimationFrame(() => requestAnimationFrame(() => {
78 const removeOverlay = () => overlay.remove()
79 overlay.classList.add("startup-transition-leaving")
80 overlay.addEventListener("transitionend", removeOverlay, { once: true })
81 setTimeout(removeOverlay, 600)
82 }))
83 }
84
85 document.addEventListener("webui-extensions-loaded", () => {
86 applicationReady = true
87 revealApplication()
88 }, { once: true })
89
90 const loadMaterialIcons = () => {
91 const fontLoad = document.fonts?.load
92 ? document.fonts.load('24px "Material Symbols Outlined"')
93 : Promise.resolve([])
94 fontLoad.then((faces) => {
95 if (faces.length > 0) root.classList.add("material-icons-ready")
96 }).catch((error) => {
97 console.warn("Material Symbols font unavailable.", error)
98 })
99 }
100
101 // Firefox can resolve FontFaceSet.load() with no matching faces when
102 // it runs while the splash is replacing the document. Wait until
103 // the replacement document is parsed and its font set is stable.
104 if (document.readyState === "loading") {
105 document.addEventListener("DOMContentLoaded", loadMaterialIcons, { once: true })
106 } else {
107 loadMaterialIcons()
108 }
109
110 setTimeout(() => revealApplication(true), 8000)
111 })()
112 </script>
113 <link rel="preload" as="style" href="/vendor/fonts/fonts.css" onload="this.onload=null;this.rel='stylesheet'">
114 <link rel="preload" as="style" href="index.css" onload="this.onload=null;this.rel='stylesheet'">
115 <link rel="preload" as="style" href="css/loading-indicators.css" onload="this.onload=null;this.rel='stylesheet'">
116 <link rel="preload" as="style" href="css/messages.css" onload="this.onload=null;this.rel='stylesheet'">
117 <link rel="preload" as="style" href="components/messages/action-buttons/simple-action-buttons.css" onload="this.onload=null;this.rel='stylesheet'">
118 <link rel="preload" as="style" href="components/messages/process-group/process-group.css" onload="this.onload=null;this.rel='stylesheet'">
119 <link rel="preload" as="style" href="css/toast.css" onload="this.onload=null;this.rel='stylesheet'">
120 <link rel="preload" as="style" href="css/settings.css" onload="this.onload=null;this.rel='stylesheet'">
121 <link rel="preload" as="style" href="css/modals.css" onload="this.onload=null;this.rel='stylesheet'">
122 <link rel="preload" as="style" href="css/surfaces.css" onload="this.onload=null;this.rel='stylesheet'">
123 <link rel="preload" as="style" href="css/scheduler-datepicker.css" onload="this.onload=null;this.rel='stylesheet'">
124 <link rel="preload" as="style" href="css/scheduler.css" onload="this.onload=null;this.rel='stylesheet'">
125 <link rel="preload" as="style" href="css/notification.css" onload="this.onload=null;this.rel='stylesheet'">
126 <link rel="preload" as="style" href="css/buttons.css" onload="this.onload=null;this.rel='stylesheet'">
127 <link rel="preload" as="style" href="css/tables.css" onload="this.onload=null;this.rel='stylesheet'">
128 <link rel="preload" as="style" href="components/canvas/right-canvas.css" onload="this.onload=null;this.rel='stylesheet'">
129
130 <!-- Flatpickr for datetime picker -->
131 <link rel="preload" as="style" href="vendor/flatpickr/flatpickr.min.css" onload="this.onload=null;this.rel='stylesheet'">
132 <script defer src="vendor/flatpickr/flatpickr.min.js"></script>
133
134 <script>
135 globalThis.safeCall = function (name, ...args) {
136 if (window[name]) window[name](...args)
137 }
138 </script>
139
140 <script type="module" src="index.js"></script>
141
142 <!-- Bootstrap JS (only for logic, importing bundled CSS => UI conflicts) -->
143 <script defer src="vendor/bootstrap/bootstrap.bundle.min.js"></script>
144
145 <!-- Then load Alpine.js -->
146 <script defer src="vendor/ace-min/ace.js"></script>
147 <script defer src="vendor/qrcode.min.js"></script>
148 <script type="module" src="js/initFw.js"></script>
149
150 <link rel="preload" as="style" href="vendor/ace-min/ace.min.css" onload="this.onload=null;this.rel='stylesheet'">
151 <!-- KaTeX CSS -->
152 <link rel="preload" as="style" href="vendor/katex/katex.min.css" crossorigin="anonymous" onload="this.onload=null;this.rel='stylesheet'">
153
154 <!-- KaTeX javascript -->
155 <script defer src="vendor/katex/katex.min.js" crossorigin="anonymous"></script>
156 <script defer src="vendor/katex/katex.auto-render.min.js" crossorigin="anonymous"></script>
157
158 <!-- Link the PWA manifest file -->
159 <link rel="manifest" href="js/manifest.json">
160
161 <script>
162 // Expose git info for sidebar component
163 globalThis.gitinfo = { version: "{{version_no}}", commit_time: "{{version_time}}" };
164
165 // Expose runtime info for frontend components (development gating, runtime-scoped cookies).
166 globalThis.runtimeInfo = {
167 ...(globalThis.runtimeInfo || {}),
168 id: "{{runtime_id}}",
169 isDevelopment: "{{runtime_is_development}}" === "true",
170 loggedIn: "{{logged_in}}" === "true",
171 timezone: "{{user_timezone_setting}}",
172 timeFormat: "{{user_time_format_setting}}",
173 uiControlVisibility: {{user_ui_control_visibility}},
174 webuiExtensions: {{webui_extension_manifest}},
175 };
176 </script>
177 <!-- Plugin head injections (scripts, stylesheets) -->
178 <x-extension id="page-head"></x-extension>
179 </head>
180
181 <body class="dark-mode device-pointer" x-data>
182 <script>
183 try {
184 if (localStorage.getItem("darkMode") === "false") document.body.classList.replace("dark-mode", "light-mode")
185 } catch (_) {}
186 </script>
187 <div class="container">
188 <!-- Sidebar Overlay -->
189 <div id="sidebar-overlay" x-data>
190 <template x-if="$store.sidebar">
191 <div class="sidebar-overlay" :class="{'visible': $store.sidebar.isOpen && $store.sidebar.isMobile()}" @click="$store.sidebar.close()"></div>
192 </template>
193 </div>
194 <!-- Left Sidebar (Header Icons, Quick Actions, Tabs, Chats, Tasks) -->
195 <x-component path="sidebar/left-sidebar.html"></x-component>
196
197 <!-- Right Panel (Message History and Input Section) -->
198 <div id="right-panel" class="panel"
199 :class="{ 'chat-active': $store.welcomeStore && !$store.welcomeStore.isVisible }">
200
201 <!-- top section with time, status etc. -->
202 <x-component path="chat/top-section/chat-top.html"></x-component>
203
204 <!-- Welcome Screen Component -->
205 <div x-data>
206 <template x-if="$store.welcomeStore && $store.welcomeStore.isVisible">
207 <div class="welcome-host">
208 <x-component path="welcome/welcome-screen.html"></x-component>
209 </div>
210 </template>
211 </div>
212 <!-- Message History (actual messages) -->
213 <div id="chat-area-wrapper" x-data x-show="!$store.welcomeStore || !$store.welcomeStore.isVisible">
214 <div id="chat-history"></div>
215
216 <div id="chat-loading-splash"
217 class="message-window-loader is-loading chat-loading-splash"
218 role="status"
219 aria-live="polite"
220 hidden>
221 <span class="loading-indicator-label">Loading chat</span>
222 </div>
223
224 <!-- Chat Navigation Buttons (floating over chat area) -->
225 <div id="chat-nav-buttons" aria-label="Chat navigation" x-cloak>
226 <button class="btn-icon-action" title="Scroll to top" @click="$store.chatNavigation.scrollToTop()">
227 <x-icon name="vertical_align_top"></x-icon>
228 </button>
229 <button class="btn-icon-action" title="Previous user message" @click="$store.chatNavigation.scrollToPrevUserMessage()">
230 <x-icon name="keyboard_arrow_up"></x-icon>
231 </button>
232 <button class="btn-icon-action" title="Next user message" @click="$store.chatNavigation.scrollToNextUserMessage()">
233 <x-icon name="keyboard_arrow_down"></x-icon>
234 </button>
235 <button class="btn-icon-action" title="Scroll to bottom" @click="$store.chatNavigation.scrollToBottom()">
236 <x-icon name="vertical_align_bottom"></x-icon>
237 </button>
238 </div>
239 </div>
240
241 <!-- NEW: Toast Stack Component -->
242 <div style="position: relative; height: 0;">
243 <x-component path="notifications/notification-toast-stack.html"></x-component>
244 </div>
245
246 <div id="toast" class="toast">
247 <div class="toast__content">
248 <div class="toast__title"></div>
249 <div class="toast__separator"></div>
250 <div class="toast__message"></div>
251 </div>
252 <button class="toast__copy" style="display: none;">Copy</button>
253 <button class="toast__close">Close</button>
254 </div>
255 <!-- Chat Input Area -->
256 <div x-data>
257 <template x-if="!$store.welcomeStore || !$store.welcomeStore.isVisible">
258 <div>
259 <!-- Progress Bar -->
260 <x-component path="chat/input/progress.html"></x-component>
261 <!-- Input Section -->
262 <x-component path="chat/input/chat-bar.html"></x-component>
263 </div>
264 </template>
265 </div>
266 </div>
267 <x-component path="canvas/right-canvas.html"></x-component>
268 </div>
269
270 <!-- Drag and Drop Overlay Component -->
271 <x-component path="chat/attachments/dragDropOverlay.html"></x-component>
272
273 </body>
274
275 </html>