fix some issues

This commit is contained in:
Frederik Spirgi
2026-09-09 09:56:38 +02:00
parent 93dd57a336
commit abd5238252
19 changed files with 142 additions and 78 deletions
+19 -13
View File
@@ -5,11 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sign in &middot; Elsewhere</title> <title>Sign in &middot; Elsewhere</title>
<link rel="icon" href="/favicon.svg"> <link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js"> <script src="/app/theme-bootstrap.js?v=20260909-account-controls">
</script> </script>
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css"> <link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
</head> </head>
<body class="site-page account-page"> <body class="site-page account-page">
@@ -29,11 +29,15 @@
<span>Username</span> <span>Username</span>
<input name="username" autocomplete="username" required minlength="3"> <input name="username" autocomplete="username" required minlength="3">
</label> </label>
<label class="field"> <div class="field">
<span>Password</span> <label for="accountPassword">Password</label>
<input name="password" type="password" autocomplete="current-password" required minlength="6"> <div class="password-input">
</label> <input id="accountPassword" name="password" type="password" autocomplete="current-password" required minlength="6">
<button type="button" id="showPassword" class="text-button" aria-pressed="false">Show password</button> <button type="button" id="showPassword" class="icon-button password-toggle" aria-label="Show password" title="Show password" aria-controls="accountPassword" aria-pressed="false">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/><path class="eye-slash" d="m3 3 18 18"/></svg>
</button>
</div>
</div>
<label id="inviteField" class="field hidden"> <label id="inviteField" class="field hidden">
<span>Invite code</span> <span>Invite code</span>
<input name="inviteCode" autocomplete="off"> <input name="inviteCode" autocomplete="off">
@@ -42,9 +46,11 @@
<input name="consent" type="checkbox"> <input name="consent" type="checkbox">
<span>I agree to the <a href="/tos.html" target="_blank" rel="noopener">Terms</a> and <a href="/privacy.html" target="_blank" rel="noopener">Privacy Policy</a>.</span> <span>I agree to the <a href="/tos.html" target="_blank" rel="noopener">Terms</a> and <a href="/privacy.html" target="_blank" rel="noopener">Privacy Policy</a>.</span>
</label> </label>
<div id="accountStatus" class="account-status hidden" role="status" aria-live="polite" aria-atomic="true" tabindex="-1">
<svg class="account-error-icon" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v6m0 3v1"/></svg>
<span id="accountStatusText"></span>
</div>
<button id="accountSubmit" class="primary-action" type="submit">Sign in</button> <button id="accountSubmit" class="primary-action" type="submit">Sign in</button>
<p id="accountStatus" role="status" aria-live="polite">
</p>
</form> </form>
<p id="accountSwitch"> <p id="accountSwitch">
</p> </p>
@@ -56,8 +62,8 @@
<a href="/privacy.html">Privacy</a> <a href="/privacy.html">Privacy</a>
<a href="/tos.html">Terms</a> <a href="/tos.html">Terms</a>
</footer> </footer>
<script type="module" src="/pages.js"> <script type="module" src="/pages.js?v=20260909-account-controls">
</script> </script>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+1 -1
View File
@@ -1,4 +1,4 @@
import {chatDrawer, chatDrawerButton, chatDrawerCloseBtn} from "./dom.js"; import {chatDrawer, chatDrawerButton, chatDrawerCloseBtn} from "./dom.js?v=20260909-account-controls";
let lastFocusedElement = null; let lastFocusedElement = null;
+1 -1
View File
@@ -1,4 +1,4 @@
import {themes} from "./constants.js"; import {themes} from "./constants.js?v=20260909-account-controls";
export const THEME_STORAGE_KEY = "krishd-theme"; export const THEME_STORAGE_KEY = "krishd-theme";
export const THEME_MODE_STORAGE_KEY = "krishd-theme-mode"; export const THEME_MODE_STORAGE_KEY = "krishd-theme-mode";
+1 -1
View File
@@ -1,4 +1,4 @@
import {post} from './api.js'; import {post} from './api.js?v=20260909-account-controls';
// Every page gets this control from the same server-rendered header. // Every page gets this control from the same server-rendered header.
for (const button of document.querySelectorAll('[data-sign-out]')) { for (const button of document.querySelectorAll('[data-sign-out]')) {
+5 -5
View File
@@ -7,10 +7,10 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script src="/app/theme-bootstrap.js"></script> <script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.css?v=20260909-elsewhere"/> <link rel="stylesheet" href="/style.css?v=20260909-account-controls"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/pages.css"><link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/pages.css?v=20260909-account-controls"><link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
</head> </head>
<body class="site-page chat-page" data-theme="fakegpt" data-theme-mode="light"> <body class="site-page chat-page" data-theme="fakegpt" data-theme-mode="light">
<!-- site-header --> <!-- site-header -->
@@ -352,7 +352,7 @@
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" defer></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js" defer></script> <script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js" defer></script>
<script type="module" src="/script.js?v=20260908-model-descriptions"></script> <script type="module" src="/script.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+6 -6
View File
@@ -5,11 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Discover personas &middot; Elsewhere</title> <title>Discover personas &middot; Elsewhere</title>
<link rel="icon" href="/favicon.svg"> <link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js"> <script src="/app/theme-bootstrap.js?v=20260909-account-controls">
</script> </script>
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css"> <link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
</head> </head>
<body class="site-page discover-page"> <body class="site-page discover-page">
@@ -30,8 +30,8 @@
<a href="/privacy.html">Privacy</a> <a href="/privacy.html">Privacy</a>
<a href="/tos.html">Terms</a> <a href="/tos.html">Terms</a>
</footer> </footer>
<script type="module" src="/pages.js"> <script type="module" src="/pages.js?v=20260909-account-controls">
</script> </script>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+6 -6
View File
@@ -5,11 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Home &middot; Elsewhere</title> <title>Home &middot; Elsewhere</title>
<link rel="icon" href="/favicon.svg"> <link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js"> <script src="/app/theme-bootstrap.js?v=20260909-account-controls">
</script> </script>
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css"> <link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
</head> </head>
<body class="site-page home-page"> <body class="site-page home-page">
@@ -50,8 +50,8 @@
</p> </p>
</main> </main>
<script type="module" src="/pages.js"> <script type="module" src="/pages.js?v=20260909-account-controls">
</script> </script>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+6 -6
View File
@@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Elsewhere</title> <title>Elsewhere</title>
<link rel="icon" href="/favicon.svg"> <link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js"></script> <script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css"> <link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
</head> </head>
<body class="site-page landing-page"> <body class="site-page landing-page">
@@ -26,7 +26,7 @@
</div> </div>
</main> </main>
<footer class="site-footer"><span>elsewhere / a little room for imagination</span><a href="/privacy.html">Privacy</a><a href="/tos.html">Terms</a></footer> <footer class="site-footer"><span>elsewhere / a little room for imagination</span><a href="/privacy.html">Privacy</a><a href="/tos.html">Terms</a></footer>
<script type="module" src="/pages.js"></script> <script type="module" src="/pages.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+5 -5
View File
@@ -7,10 +7,10 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script src="/app/theme-bootstrap.js"></script> <script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.css?v=20260909-elsewhere"/> <link rel="stylesheet" href="/style.css?v=20260909-account-controls"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
</head> </head>
<body class="site-page market-page" data-theme="fakegpt" data-theme-mode="light"> <body class="site-page market-page" data-theme="fakegpt" data-theme-mode="light">
<!-- site-header --> <!-- site-header -->
@@ -237,6 +237,6 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/market.js"></script> <script type="module" src="/market.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js"></script></body> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script></body>
</html> </html>
+4 -4
View File
@@ -1,7 +1,7 @@
import {get, post, put} from "./app/api.js"; import {get, post, put} from "./app/api.js?v=20260909-account-controls";
import {initializeAppearance} from "./app/themeController.js"; import {initializeAppearance} from "./app/themeController.js?v=20260909-account-controls";
import {clearStatus, setStatus} from "./app/statusNotice.js"; import {clearStatus, setStatus} from "./app/statusNotice.js?v=20260909-account-controls";
import {populatePersonaForm, readPersonaForm} from "./app/personaForm.js"; import {populatePersonaForm, readPersonaForm} from "./app/personaForm.js?v=20260909-account-controls";
const $ = (id) => document.getElementById(id); const $ = (id) => document.getElementById(id);
const marketPersonaList = $("marketPersonaList"); const marketPersonaList = $("marketPersonaList");
+19
View File
@@ -49,3 +49,22 @@
.entry-door { width:65px; height:92px; border-width:1px; }.entry-door::before { inset:4px; }.entry-door span { inset:4px 4px 0 14px; border-width:1px; }.entry-door span::after { width:3px; height:3px; left:5px; }.entry-door::after { bottom:-8px; left:-10px; right:-10px; } .entry-door { width:65px; height:92px; border-width:1px; }.entry-door::before { inset:4px; }.entry-door span { inset:4px 4px 0 14px; border-width:1px; }.entry-door span::after { width:3px; height:3px; left:5px; }.entry-door::after { bottom:-8px; left:-10px; right:-10px; }
.entry-copy h1 { font-size:2.8rem; }.entry-copy > p:not(.eyebrow) { margin:18px 0 24px; } .entry-copy h1 { font-size:2.8rem; }.entry-copy > p:not(.eyebrow) { margin:18px 0 24px; }
} }
/* Account controls stay quiet; errors must be unmistakable. */
.password-input { position:relative; }
#accountForm .password-input input { padding-right:50px; }
#accountForm .password-toggle { position:absolute; right:6px; top:50%; transform:translateY(-50%); width:36px; height:36px; min-height:36px; padding:0; border:0; border-radius:8px; background:transparent; color:var(--text-soft); box-shadow:none; }
#accountForm .password-toggle:hover { transform:translateY(-50%); background:var(--surface-hover); color:var(--text); }
.password-toggle svg { display:block; flex-shrink:0; }
.password-toggle .eye-slash { display:none; }
.password-toggle[aria-pressed="true"] .eye-slash { display:block; }
.account-status { display:flex; align-items:flex-start; gap:10px; margin:0; font-size:.9rem; line-height:1.6; color:var(--text-soft); }
.account-error-icon { display:none; flex:0 0 20px; margin-top:2px; }
.account-status[data-state="error"] { padding:13px 15px; border:1px solid #dfaaa5; border-left:4px solid #b3261e; border-radius:10px; background:#fff1f0; color:#92221b; font-weight:600; }
.account-status[data-state="error"] .account-error-icon { display:block; }
#accountForm input[aria-invalid="true"] { border-color:#b3261e; }
body[data-theme-mode="dark"] .account-status[data-state="error"] { background:#351f22; color:#ffb4ad; border-color:#925350; border-left-color:#ffb4ad; }
body[data-theme-mode="dark"] #accountForm input[aria-invalid="true"] { border-color:#ffb4ad; }
#accountForm .field > label { font-size:.82rem; font-weight:600; color:var(--text-soft); }
.account-status:focus { outline:none; }
#accountSwitch { margin-top:20px; }
+26 -5
View File
@@ -1,4 +1,4 @@
import {get, post} from "/app/api.js"; import {get, post} from "/app/api.js?v=20260909-account-controls";
const params = new URLSearchParams(location.search); const params = new URLSearchParams(location.search);
function returnPath() { function returnPath() {
@@ -20,6 +20,14 @@ if (form) {
const register = location.pathname === "/register"; const register = location.pathname === "/register";
const status = document.querySelector("#accountStatus"); const status = document.querySelector("#accountStatus");
const submit = document.querySelector("#accountSubmit"); const submit = document.querySelector("#accountSubmit");
function showAccountStatus(message, error = false) {
status.dataset.state = error ? "error" : "pending";
status.setAttribute("role", error ? "alert" : "status");
status.setAttribute("aria-live", error ? "assertive" : "polite");
status.classList.toggle("hidden", !message);
document.querySelector("#accountStatusText").textContent = message;
if (error) status.focus();
}
const label = register ? "Create account" : "Sign in"; const label = register ? "Create account" : "Sign in";
document.title = `${label} · Elsewhere`; document.title = `${label} · Elsewhere`;
document.querySelector("#accountTitle").textContent = label; document.querySelector("#accountTitle").textContent = label;
@@ -34,7 +42,7 @@ if (form) {
document.querySelector("#accountHeadline").textContent = "Your next conversation starts here."; document.querySelector("#accountHeadline").textContent = "Your next conversation starts here.";
document.querySelector("#accountDescription").textContent = "Choose a username and a password of at least 6 characters."; document.querySelector("#accountDescription").textContent = "Choose a username and a password of at least 6 characters.";
} }
if (session.error) status.textContent = session.error; if (session.error) showAccountStatus(session.error, true);
const switchText = document.querySelector("#accountSwitch"); const switchText = document.querySelector("#accountSwitch");
switchText.append(register ? "Already have an account? " : "New to Elsewhere? "); switchText.append(register ? "Already have an account? " : "New to Elsewhere? ");
const link = document.createElement("a"); const link = document.createElement("a");
@@ -44,18 +52,31 @@ if (form) {
document.querySelector("#showPassword").addEventListener("click", event => { document.querySelector("#showPassword").addEventListener("click", event => {
const show = form.elements.password.type === "password"; const show = form.elements.password.type === "password";
form.elements.password.type = show ? "text" : "password"; form.elements.password.type = show ? "text" : "password";
event.currentTarget.textContent = show ? "Hide password" : "Show password"; const label = show ? "Hide password" : "Show password";
event.currentTarget.setAttribute("aria-label", label);
event.currentTarget.title = label;
event.currentTarget.setAttribute("aria-pressed", String(show)); event.currentTarget.setAttribute("aria-pressed", String(show));
}); });
form.addEventListener("submit", async event => { form.addEventListener("submit", async event => {
event.preventDefault(); event.preventDefault();
submit.disabled = true; submit.disabled = true;
status.textContent = register ? "Creating your account…" : "Signing in…"; form.elements.username.removeAttribute("aria-invalid");
form.elements.username.removeAttribute("aria-describedby");
showAccountStatus(register ? "Creating your account..." : "Signing in...");
const result = await post(register ? "/register" : "/login", { const result = await post(register ? "/register" : "/login", {
username: form.elements.username.value.trim(), password: form.elements.password.value, username: form.elements.username.value.trim(), password: form.elements.password.value,
...(register ? {inviteCode: form.elements.inviteCode.value.trim()} : {}) ...(register ? {inviteCode: form.elements.inviteCode.value.trim()} : {})
}); });
if (result.error) { status.textContent = result.error; submit.disabled = false; return; } if (result.error) {
const duplicate = register && result.error === "User already exists";
if (duplicate) {
form.elements.username.setAttribute("aria-invalid", "true");
form.elements.username.setAttribute("aria-describedby", "accountStatus");
}
showAccountStatus(duplicate ? "That username is already taken. Choose another, or sign in below." : result.error, true);
submit.disabled = false;
return;
}
location.assign(returnPath()); location.assign(returnPath());
}); });
} }
+5 -5
View File
@@ -5,10 +5,10 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Privacy Policy – DeepFake</title> <title>Privacy Policy – DeepFake</title>
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css?v=20260909-account-controls" />
<link rel="stylesheet" href="legal.css" /> <link rel="stylesheet" href="legal.css?v=20260909-account-controls" />
<script src="/app/theme-bootstrap.js"></script> <script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
</head> </head>
<body class="site-page legal-page"> <body class="site-page legal-page">
@@ -89,6 +89,6 @@
<p>We may update this document. Changes will be posted with a “Last updated” date.</p> <p>We may update this document. Changes will be posted with a “Last updated” date.</p>
</section> </section>
</main> </main>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+6 -6
View File
@@ -1,8 +1,8 @@
import {del, get, post, put, stream} from "./app/api.js"; import {del, get, post, put, stream} from "./app/api.js?v=20260909-account-controls";
import {closeChatDrawer, openChatDrawer} from "./app/chatDrawer.js"; import {closeChatDrawer, openChatDrawer} from "./app/chatDrawer.js?v=20260909-account-controls";
import {onboardingPrompts, requestedChatId} from "./app/constants.js"; import {onboardingPrompts, requestedChatId} from "./app/constants.js?v=20260909-account-controls";
import {getModelProfile} from "./app/modelProfiles.js"; import {getModelProfile} from "./app/modelProfiles.js?v=20260909-account-controls";
import {applyTheme as applySharedTheme, applyThemeMode as applySharedThemeMode, readStoredAppearance} from "./app/themeController.js"; import {applyTheme as applySharedTheme, applyThemeMode as applySharedThemeMode, readStoredAppearance} from "./app/themeController.js?v=20260909-account-controls";
import { import {
activeChatTitle, activeChatTitle,
activeUserPersonaStatus, activeUserPersonaStatus,
@@ -99,7 +99,7 @@ import {
brandLogoTargets, brandLogoTargets,
brandNameTargets, brandNameTargets,
userPersonaList userPersonaList
} from "./app/dom.js"; } from "./app/dom.js?v=20260909-account-controls";
const headerModelButton = document.getElementById("headerModelButton"); const headerModelButton = document.getElementById("headerModelButton");
+5 -5
View File
@@ -7,10 +7,10 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script src="/app/theme-bootstrap.js"></script> <script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.css?v=20260909-elsewhere"/> <link rel="stylesheet" href="/style.css?v=20260909-account-controls"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
</head> </head>
<body class="site-page settings-page" data-theme="fakegpt" data-theme-mode="light"> <body class="site-page settings-page" data-theme="fakegpt" data-theme-mode="light">
<!-- site-header --> <!-- site-header -->
@@ -298,6 +298,6 @@
</div> </div>
</div> </div>
</div> </div>
<script type="module" src="/settings.js"></script> <script type="module" src="/settings.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js"></script></body> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script></body>
</html> </html>
+4 -4
View File
@@ -1,11 +1,11 @@
import {del, get, post, put} from "./app/api.js"; import {del, get, post, put} from "./app/api.js?v=20260909-account-controls";
import { import {
applyTheme as applySharedTheme, applyTheme as applySharedTheme,
applyThemeMode as applySharedThemeMode, applyThemeMode as applySharedThemeMode,
initializeAppearance initializeAppearance
} from "./app/themeController.js"; } from "./app/themeController.js?v=20260909-account-controls";
import {clearStatus, setStatus} from "./app/statusNotice.js"; import {clearStatus, setStatus} from "./app/statusNotice.js?v=20260909-account-controls";
import {populatePersonaForm, readPersonaForm} from "./app/personaForm.js"; import {populatePersonaForm, readPersonaForm} from "./app/personaForm.js?v=20260909-account-controls";
const $ = (id) => document.getElementById(id); const $ = (id) => document.getElementById(id);
const settingsNotice = $("settingsNotice"); const settingsNotice = $("settingsNotice");
+5 -5
View File
@@ -5,10 +5,10 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Terms of Service – DeepFake</title> <title>Terms of Service – DeepFake</title>
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css?v=20260909-account-controls" />
<link rel="stylesheet" href="legal.css" /> <link rel="stylesheet" href="legal.css?v=20260909-account-controls" />
<script src="/app/theme-bootstrap.js"></script> <script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/shell.css"> <link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
</head> </head>
<body class="site-page legal-page"> <body class="site-page legal-page">
@@ -47,6 +47,6 @@
<p>We may modify these Terms at any time. Updates will be reflected with a new “Last updated” date.</p> <p>We may modify these Terms at any time. Updates will be reflected with a new “Last updated” date.</p>
</section> </section>
</main> </main>
<script type="module" src="/app/workspaceNavigation.js"></script> <script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
</body> </body>
</html> </html>
+4
View File
@@ -283,6 +283,10 @@ export function createApp(options = {}) {
app.use(express.static(config.publicDir, { app.use(express.static(config.publicDir, {
maxAge: config.staticMaxAge, maxAge: config.staticMaxAge,
etag: config.staticEtag, etag: config.staticEtag,
setHeaders(res, filePath) {
// HTML and its modules must not drift apart after a deployment.
if (/\.(?:js|css)$/.test(filePath)) res.setHeader("Cache-Control", "no-cache");
},
fallthrough: config.staticFallthrough fallthrough: config.staticFallthrough
})); }));
app.use(createAuthRouter({repositories, authRateLimiters, config})); app.use(createAuthRouter({repositories, authRateLimiters, config}));
+14
View File
@@ -1064,3 +1064,17 @@ test("page headers share navigation and keep session markup private", async () =
assert.ok(html.includes('href="/login"')); assert.ok(html.includes('href="/login"'));
} finally { await server.close(); } } finally { await server.close(); }
}); });
test("frontend scripts and styles revalidate across deployments", async () => {
const server = await startTestServer({config: {staticMaxAge: "1h"}});
try {
for (const asset of ["/script.js?v=20260909-account-controls", "/app/dom.js?v=20260909-account-controls", "/pages.css?v=20260909-account-controls"]) {
const response = await fetch(server.baseUrl + asset);
assert.equal(response.status, 200);
assert.equal(response.headers.get("cache-control"), "no-cache");
const etag = response.headers.get("etag");
assert.ok(etag);
await response.text();
}
} finally { await server.close(); }
});