2026-02-14 18:20:07 +01:00
|
|
|
<!doctype html>
|
|
|
|
|
<html lang="en">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8" />
|
2026-02-16 14:26:36 +01:00
|
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
2026-02-14 18:20:07 +01:00
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
2026-02-17 11:03:23 +01:00
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
|
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
2026-03-31 09:27:27 +02:00
|
|
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600;700&family=JetBrains+Mono&display=swap" rel="stylesheet" />
|
2026-02-15 10:34:59 +01:00
|
|
|
<script>
|
2026-04-24 22:26:07 +02:00
|
|
|
(function () {
|
2026-05-11 16:37:06 +02:00
|
|
|
function isResizeObserverLoopMessage(message) {
|
|
|
|
|
return typeof message === 'string'
|
|
|
|
|
&& (
|
|
|
|
|
message.indexOf('ResizeObserver loop completed with undelivered notifications') !== -1
|
|
|
|
|
|| message.indexOf('ResizeObserver loop limit exceeded') !== -1
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
window.addEventListener('error', function (event) {
|
|
|
|
|
if (isResizeObserverLoopMessage(event.message)) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
window.addEventListener('unhandledrejection', function (event) {
|
|
|
|
|
var reason = event.reason && (event.reason.stack || event.reason.message || String(event.reason));
|
|
|
|
|
if (isResizeObserverLoopMessage(reason)) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-29 00:46:59 +02:00
|
|
|
try {
|
|
|
|
|
if (new URLSearchParams(window.location.search).get('window') === 'ai-workspace') {
|
|
|
|
|
document.documentElement.classList.add('ai-workspace-native-window');
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
// Leave the normal app chrome untouched if URL parsing is unavailable.
|
|
|
|
|
}
|
|
|
|
|
})();
|
|
|
|
|
</script>
|
|
|
|
|
<style>
|
|
|
|
|
:root {
|
|
|
|
|
color-scheme: light;
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
color: #37352F;
|
|
|
|
|
}
|
|
|
|
|
:root[data-theme="dark"] {
|
|
|
|
|
color-scheme: dark;
|
|
|
|
|
background: #1F1E1B;
|
|
|
|
|
color: #E6E1D8;
|
|
|
|
|
}
|
|
|
|
|
body {
|
|
|
|
|
background: inherit;
|
|
|
|
|
color: inherit;
|
|
|
|
|
}
|
|
|
|
|
:root.ai-workspace-native-window,
|
|
|
|
|
:root.ai-workspace-native-window body {
|
|
|
|
|
background: transparent;
|
|
|
|
|
height: 100%;
|
|
|
|
|
margin: 0;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
:root.ai-workspace-native-window body {
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
|
|
|
|
:root.ai-workspace-native-window #root {
|
|
|
|
|
background: #FFFFFF;
|
|
|
|
|
border: 1px solid #E9E9E7;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.12), 0 1px 3px rgba(15, 23, 42, 0.10);
|
|
|
|
|
height: 100%;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
:root.ai-workspace-native-window[data-theme="dark"] #root {
|
|
|
|
|
background: #1F1E1B;
|
|
|
|
|
border-color: #34322D;
|
|
|
|
|
box-shadow: 0 12px 26px rgba(0, 0, 0, 0.30), 0 1px 4px rgba(0, 0, 0, 0.22);
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<title>Tolaria</title>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<script>
|
|
|
|
|
(function () {
|
2026-04-24 22:26:07 +02:00
|
|
|
var key = 'tolaria-theme';
|
|
|
|
|
var legacyKey = 'laputa-theme';
|
2026-05-05 15:00:24 +02:00
|
|
|
var systemDarkQuery = '(prefers-color-scheme: dark)';
|
2026-04-24 22:26:07 +02:00
|
|
|
function normalizeTheme(value) {
|
2026-05-05 15:00:24 +02:00
|
|
|
return value === 'dark' || value === 'light' || value === 'system' ? value : null;
|
2026-04-24 22:26:07 +02:00
|
|
|
}
|
2026-05-05 15:00:24 +02:00
|
|
|
function prefersDarkTheme() {
|
|
|
|
|
try {
|
|
|
|
|
return typeof window.matchMedia === 'function' && window.matchMedia(systemDarkQuery).matches;
|
2026-05-26 10:11:53 +02:00
|
|
|
} catch {
|
2026-05-05 15:00:24 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function resolveTheme(value) {
|
2026-04-24 22:26:07 +02:00
|
|
|
var mode = normalizeTheme(value) || 'light';
|
2026-05-05 15:00:24 +02:00
|
|
|
return mode === 'system' ? (prefersDarkTheme() ? 'dark' : 'light') : mode;
|
|
|
|
|
}
|
|
|
|
|
function applyTheme(value) {
|
|
|
|
|
var mode = resolveTheme(value);
|
2026-04-24 22:26:07 +02:00
|
|
|
document.documentElement.setAttribute('data-theme', mode);
|
|
|
|
|
document.documentElement.classList.toggle('dark', mode === 'dark');
|
|
|
|
|
}
|
2026-05-26 10:11:53 +02:00
|
|
|
var mode;
|
2026-04-24 22:26:07 +02:00
|
|
|
try {
|
2026-05-26 10:11:53 +02:00
|
|
|
mode = normalizeTheme(localStorage.getItem(key));
|
2026-04-24 22:26:07 +02:00
|
|
|
if (mode === null) {
|
|
|
|
|
mode = normalizeTheme(localStorage.getItem(legacyKey));
|
|
|
|
|
if (mode !== null) localStorage.setItem(key, mode);
|
|
|
|
|
}
|
|
|
|
|
applyTheme(mode);
|
2026-05-26 10:11:53 +02:00
|
|
|
} catch {
|
2026-04-24 22:26:07 +02:00
|
|
|
applyTheme('light');
|
|
|
|
|
}
|
|
|
|
|
})();
|
2026-02-15 10:34:59 +01:00
|
|
|
</script>
|
2026-05-01 04:47:41 +02:00
|
|
|
<script>
|
|
|
|
|
(function () {
|
|
|
|
|
var readyEventName = 'tolaria:frontend-ready';
|
|
|
|
|
var reloadAttemptKey = 'tolaria:startup-reload-attempted';
|
|
|
|
|
var startupTimeoutMs = 10000;
|
|
|
|
|
var isTauri = '__TAURI__' in window || '__TAURI_INTERNALS__' in window;
|
|
|
|
|
|
|
|
|
|
if (!isTauri) return;
|
|
|
|
|
|
|
|
|
|
function hasReloadAttempted() {
|
|
|
|
|
try {
|
|
|
|
|
return sessionStorage.getItem(reloadAttemptKey) === '1';
|
2026-05-26 10:11:53 +02:00
|
|
|
} catch {
|
2026-05-01 04:47:41 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function markReloadAttempted() {
|
|
|
|
|
try {
|
|
|
|
|
sessionStorage.setItem(reloadAttemptKey, '1');
|
|
|
|
|
return true;
|
2026-05-26 10:11:53 +02:00
|
|
|
} catch {
|
2026-05-01 04:47:41 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearReloadAttempt() {
|
|
|
|
|
try {
|
|
|
|
|
sessionStorage.removeItem(reloadAttemptKey);
|
2026-05-26 10:11:53 +02:00
|
|
|
} catch {
|
2026-05-01 04:47:41 +02:00
|
|
|
// Storage can be unavailable in hardened WebView/privacy modes.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.addEventListener(readyEventName, clearReloadAttempt, { once: true });
|
|
|
|
|
window.setTimeout(function () {
|
|
|
|
|
if (window.__tolariaFrontendReady === true) return;
|
|
|
|
|
if (hasReloadAttempted()) return;
|
|
|
|
|
if (!markReloadAttempted()) return;
|
|
|
|
|
|
|
|
|
|
window.location.reload();
|
|
|
|
|
}, startupTimeoutMs);
|
|
|
|
|
})();
|
|
|
|
|
</script>
|
2026-02-14 18:20:07 +01:00
|
|
|
<div id="root"></div>
|
|
|
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|