moved js files in public into js and css into css folder #3

Merged
ScriptSorcerer merged 1 commits from main into dev 2026-09-22 21:56:49 +02:00
40 changed files with 1772 additions and 1555 deletions
+33 -14
View File
@@ -1,17 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sign in &middot; Elsewhere</title>
<link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls">
<link rel="icon" href="img/favicon.svg">
<script src="/js/theme-bootstrap.js?v=20260909-account-controls">
</script>
<link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<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 rel="stylesheet" href="/css/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/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">
</head>
<body class="site-page account-page">
<!-- site-header -->
<main id="main" class="account-layout page-content">
@@ -32,9 +36,17 @@
<div class="field">
<label for="accountPassword">Password</label>
<div class="password-input">
<input id="accountPassword" name="password" type="password" autocomplete="current-password" required minlength="6">
<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>
<input id="accountPassword" name="password" type="password" autocomplete="current-password"
required minlength="6">
<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>
@@ -44,10 +56,16 @@
</label>
<label id="consentField" class="consent-container hidden">
<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>
<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>
<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>
@@ -62,8 +80,9 @@
<a href="/privacy.html">Privacy</a>
<a href="/tos.html">Terms</a>
</footer>
<script type="module" src="/pages.js?v=20260909-account-controls">
<script type="module" src="js/pages.js?v=20260909-account-controls">
</script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+159 -61
View File
@@ -1,17 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/svg+xml" href="img/favicon.svg">
<title>Elsewhere</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<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">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.css?v=20260920-web-search-composer"/>
<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="/js/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/css/style.css?v=20260920-web-search-composer" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/pages.css?v=20260909-account-controls"><link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/shell.css?v=20260909-account-controls">
</head>
<body class="site-page chat-page" data-theme="fakegpt" data-theme-mode="light">
<!-- site-header -->
<div class="app-shell">
@@ -19,7 +24,10 @@
<div class="chat-layout chat-layout-focus">
<aside id="chatSidebar" class="chat-sidebar" aria-label="Conversation history">
<div class="sidebar-top">
<div class="sidebar-workspace-card"><h2>Conversations</h2><button id="newChat" class="primary-action" type="button">New chat</button></div>
<div class="sidebar-workspace-card">
<h2>Conversations</h2><button id="newChat" class="primary-action" type="button">New
chat</button>
</div>
<label class="field compact-field">
<span>Search</span>
<input id="chatSearch" type="search" placeholder="Search chats" />
@@ -35,39 +43,55 @@
</aside>
<section id="main" class="chat-main chat-main-focus" aria-label="Active conversation">
<div class="chat-heading"><p id="workspaceNotice" class="status error hidden" role="status" aria-live="polite"></p><header class="chat-header chat-header-focus">
<button id="chatSidebarToggle" class="icon-button sidebar-toggle" type="button" aria-controls="chatSidebar" aria-expanded="false" aria-label="Open conversation history">History</button>
<div class="chat-heading">
<p id="workspaceNotice" class="status error hidden" role="status" aria-live="polite"></p>
<header class="chat-header chat-header-focus">
<button id="chatSidebarToggle" class="icon-button sidebar-toggle" type="button"
aria-controls="chatSidebar" aria-expanded="false"
aria-label="Open conversation history">History</button>
<div class="chat-title-block">
<div class="chat-title">
<h2 id="activeChatTitle">New chat</h2>
</div>
<div class="chat-header-context" aria-label="Current chat setup">
<button id="headerModelButton" class="chat-context-item chat-context-button secondary-action" type="button" aria-controls="chatDrawer" aria-label="Change AI model"><span>Model</span><strong id="headerModelName">Loading...</strong></button>
<span class="chat-context-item"><span>Persona</span><strong id="headerPersonaName">None</strong></span>
<button id="headerModelButton"
class="chat-context-item chat-context-button secondary-action" type="button"
aria-controls="chatDrawer"
aria-label="Change AI model"><span>Model</span><strong
id="headerModelName">Loading...</strong></button>
<span class="chat-context-item"><span>Persona</span><strong
id="headerPersonaName">None</strong></span>
</div>
</div>
<div class="chat-header-actions">
<div class="chat-actions-menu">
<button id="chatDrawerButton" class="secondary-action overflow-trigger" type="button" aria-expanded="false" aria-controls="chatDrawer">
<button id="chatDrawerButton" class="secondary-action overflow-trigger"
type="button" aria-expanded="false" aria-controls="chatDrawer">
<span class="overflow-trigger-label">Chat settings</span>
</button>
<div id="chatDrawer" class="chat-drawer hidden" role="dialog" aria-modal="true" aria-labelledby="chatDrawerTitle">
<div id="chatDrawer" class="chat-drawer hidden" role="dialog" aria-modal="true"
aria-labelledby="chatDrawerTitle">
<div class="chat-drawer-panel">
<div class="chat-drawer-header">
<div>
<p class="eyebrow">Conversation</p>
<h2 id="chatDrawerTitle">Settings</h2>
</div>
<button id="chatDrawerClose" class="icon-button" type="button" aria-label="Close chat settings">×</button>
<button id="chatDrawerClose" class="icon-button" type="button"
aria-label="Close chat settings">×</button>
</div>
<div class="chat-drawer-body">
<div class="chat-drawer-summary">
<div class="chat-drawer-pills">
<span id="chatCharacterPill" class="context-pill hidden">Character: none</span>
<span id="chatUserPersonaPill" class="context-pill hidden">User persona: none</span>
<span id="chatCharacterPill"
class="context-pill hidden">Character: none</span>
<span id="chatUserPersonaPill" class="context-pill hidden">User
persona: none</span>
</div>
</div>
<details class="chat-drawer-section chat-drawer-disclosure chat-drawer-section--model" open>
<details
class="chat-drawer-section chat-drawer-disclosure chat-drawer-section--model"
open>
<summary class="chat-drawer-row">
<span class="chat-drawer-row-label">
<span class="eyebrow">AI model</span>
@@ -78,7 +102,9 @@
<span class="chat-drawer-row-hint">Change</span>
</span>
</summary>
<div id="modelPanel" class="model-panel chat-drawer-disclosure-panel" data-static-panel="true">
<div id="modelPanel"
class="model-panel chat-drawer-disclosure-panel"
data-static-panel="true">
<div class="model-select">
<div class="model-select-header">
<label for="model">Model</label>
@@ -88,26 +114,40 @@
<select id="model">
<option disabled selected>Loading models...</option>
</select>
<span class="model-select-chevron" aria-hidden="true"></span>
<span class="model-select-chevron"
aria-hidden="true"></span>
</div>
</div>
<div id="modelHelp" class="model-help" aria-live="polite">
<div class="model-help-header">
<strong id="modelHelpTitle">Choose a model</strong>
<span id="modelHelpBadge" class="model-help-badge">Waiting</span>
<span id="modelHelpBadge"
class="model-help-badge">Waiting</span>
</div>
<p id="modelHelpSummary" class="model-help-summary">Pick a model to see what it is best suited for.</p>
<p id="modelHelpSummary" class="model-help-summary">Pick a
model to see what it is best suited for.</p>
</div>
<div class="generation-settings-grid">
<label class="field"><span>Temperature</span><input id="chatTemperature" type="number" min="0" max="2" step="0.1" placeholder="Default"/></label>
<label class="field"><span>Context length</span><input id="chatContextLength" type="number" min="256" max="131072" placeholder="Default"/></label>
<label class="field"><span>Response length</span><input id="chatResponseLength" type="number" min="1" max="32768" placeholder="Default"/></label>
<label class="field field-wide"><span>System instruction</span><textarea id="chatSystemInstruction" rows="4" maxlength="8000" placeholder="Optional instructions for this chat"></textarea></label>
<button id="saveGenerationSettings" type="button">Save generation settings</button>
<label class="field"><span>Temperature</span><input
id="chatTemperature" type="number" min="0" max="2"
step="0.1" placeholder="Default" /></label>
<label class="field"><span>Context length</span><input
id="chatContextLength" type="number" min="256"
max="131072" placeholder="Default" /></label>
<label class="field"><span>Response length</span><input
id="chatResponseLength" type="number" min="1"
max="32768" placeholder="Default" /></label>
<label class="field field-wide"><span>System
instruction</span><textarea
id="chatSystemInstruction" rows="4" maxlength="8000"
placeholder="Optional instructions for this chat"></textarea></label>
<button id="saveGenerationSettings" type="button">Save
generation settings</button>
</div>
</div>
</details>
<details class="chat-drawer-section chat-drawer-disclosure chat-drawer-section--persona">
<details
class="chat-drawer-section chat-drawer-disclosure chat-drawer-section--persona">
<summary class="chat-drawer-row">
<span class="chat-drawer-row-label">
<span class="eyebrow">Your identity</span>
@@ -118,18 +158,25 @@
<span class="chat-drawer-row-hint">Change</span>
</span>
</summary>
<div id="personaPanel" class="persona-panel chat-drawer-disclosure-panel" data-static-panel="true">
<div id="personaPanel"
class="persona-panel chat-drawer-disclosure-panel"
data-static-panel="true">
<div class="persona-section">
<div class="persona-section-header">
<div>
<h2>Your persona</h2>
</div>
<div class="persona-section-actions">
<button id="clearUserPersona" class="secondary-action" type="button">Clear</button>
<a href="/app/personas" class="secondary-action">Manage</a>
<button id="clearUserPersona"
class="secondary-action"
type="button">Clear</button>
<a href="/app/personas"
class="secondary-action">Manage</a>
</div>
</div>
<p id="activeUserPersonaStatus" class="status persona-status">No default user persona set.</p>
<p id="activeUserPersonaStatus"
class="status persona-status">No default user persona
set.</p>
<div id="userPersonaList" class="persona-list"></div>
</div>
</div>
@@ -137,10 +184,17 @@
<div class="chat-drawer-section">
<p class="dropdown-section-label">Chat</p>
<div class="chat-drawer-action-list">
<button id="renameChat" class="secondary-action chat-drawer-action" type="button" data-chat-action="rename">Rename chat</button>
<button id="pinChat" class="secondary-action chat-drawer-action" type="button" data-chat-action="pin">Pin</button>
<button id="archiveChat" class="secondary-action chat-drawer-action" type="button" data-chat-action="archive">Archive</button>
<button id="exportChat" class="secondary-action chat-drawer-action" type="button" data-chat-action="export">Export</button>
<button id="renameChat"
class="secondary-action chat-drawer-action" type="button"
data-chat-action="rename">Rename chat</button>
<button id="pinChat" class="secondary-action chat-drawer-action"
type="button" data-chat-action="pin">Pin</button>
<button id="archiveChat"
class="secondary-action chat-drawer-action" type="button"
data-chat-action="archive">Archive</button>
<button id="exportChat"
class="secondary-action chat-drawer-action" type="button"
data-chat-action="export">Export</button>
</div>
</div>
<details class="chat-drawer-section chat-drawer-more">
@@ -151,12 +205,23 @@
<span class="chat-drawer-row-hint">Show</span>
</summary>
<div class="chat-drawer-action-list chat-drawer-disclosure-panel">
<button id="moveChatFolder" class="secondary-action chat-drawer-action" type="button" data-chat-action="folder">Move to folder</button>
<button id="manageChatMemory" class="secondary-action chat-drawer-action" type="button" data-chat-action="memory">Memory facts</button>
<button id="managePersonaState" class="secondary-action chat-drawer-action" type="button" data-chat-action="personaState">Roleplay state</button>
<button id="backupWorkspace" class="secondary-action chat-drawer-action" type="button">Backup workspace</button>
<button id="importWorkspace" class="secondary-action chat-drawer-action" type="button">Import workspace</button>
<input id="importWorkspaceFile" class="hidden" type="file" accept="application/json,.json"/>
<button id="moveChatFolder"
class="secondary-action chat-drawer-action" type="button"
data-chat-action="folder">Move to folder</button>
<button id="manageChatMemory"
class="secondary-action chat-drawer-action" type="button"
data-chat-action="memory">Memory facts</button>
<button id="managePersonaState"
class="secondary-action chat-drawer-action" type="button"
data-chat-action="personaState">Roleplay state</button>
<button id="backupWorkspace"
class="secondary-action chat-drawer-action"
type="button">Backup workspace</button>
<button id="importWorkspace"
class="secondary-action chat-drawer-action"
type="button">Import workspace</button>
<input id="importWorkspaceFile" class="hidden" type="file"
accept="application/json,.json" />
</div>
</details>
<details class="chat-drawer-section chat-drawer-danger">
@@ -167,7 +232,9 @@
<span class="chat-drawer-row-hint">Show</span>
</summary>
<div class="chat-drawer-action-list chat-drawer-disclosure-panel">
<button id="clearChat" class="secondary-action chat-drawer-action chat-drawer-action-danger" type="button" data-chat-action="clear">Clear chat</button>
<button id="clearChat"
class="secondary-action chat-drawer-action chat-drawer-action-danger"
type="button" data-chat-action="clear">Clear chat</button>
</div>
</details>
</div>
@@ -175,9 +242,11 @@
</div>
</div>
</div>
</header></div>
</header>
</div>
<div id="chatActivityOverlay" class="chat-activity-overlay hidden" role="status" aria-live="polite" aria-atomic="true">
<div id="chatActivityOverlay" class="chat-activity-overlay hidden" role="status" aria-live="polite"
aria-atomic="true">
<div class="chat-activity-card">
<div class="chat-activity-mark" aria-hidden="true">
<span></span>
@@ -190,7 +259,8 @@
</div>
</div>
<div id="messages" class="messages-panel messages-panel-focus" role="log" aria-live="polite" aria-relevant="additions text"></div>
<div id="messages" class="messages-panel messages-panel-focus" role="log" aria-live="polite"
aria-relevant="additions text"></div>
<div class="input-shell input-shell-focus">
<div class="input-meta">
@@ -198,15 +268,34 @@
</div>
<div id="uploadPreviews" class="upload-previews" aria-live="polite" hidden></div>
<div class="input-container">
<input id="imagePicker" type="file" accept="image/png,image/jpeg,image/webp" multiple hidden>
<button id="attachImage" class="icon-button" type="button" aria-label="Attach image" title="Attach image">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="3"/><circle cx="8" cy="8" r="1.5"/><path d="m3 17 5-5 4 4 4-6 5 7"/></svg>
<input id="imagePicker" type="file" accept="image/png,image/jpeg,image/webp" multiple
hidden>
<button id="attachImage" class="icon-button" type="button" aria-label="Attach image"
title="Attach image">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.6" aria-hidden="true">
<rect x="3" y="3" width="18" height="18" rx="3" />
<circle cx="8" cy="8" r="1.5" />
<path d="m3 17 5-5 4 4 4-6 5 7" />
</svg>
</button>
<button id="webSearch" class="icon-button" type="button" aria-label="Search the web" title="Search the web" aria-pressed="false">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><circle cx="12" cy="12" r="9"/><ellipse cx="12" cy="12" rx="4" ry="9"/><path d="M3 12h18"/></svg>
<button id="webSearch" class="icon-button" type="button" aria-label="Search the web"
title="Search the web" aria-pressed="false">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.6" aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<ellipse cx="12" cy="12" rx="4" ry="9" />
<path d="M3 12h18" />
</svg>
</button>
<button id="reasoning" class="icon-button" type="button" aria-label="Enable reasoning" title="Enable reasoning" aria-pressed="false" disabled>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 18V5a3 3 0 0 0-5.8-1A4 4 0 0 0 3 10a4 4 0 0 0 1 7.8A4 4 0 0 0 12 18ZM12 5a3 3 0 0 1 5.8-1A4 4 0 0 1 21 10a4 4 0 0 1-1 7.8A4 4 0 0 1 12 18M8 8a4 4 0 0 1-5 2m13-2a4 4 0 0 0 5 2M8 14a4 4 0 0 0-4 4m12-4a4 4 0 0 1 4 4"/></svg>
<button id="reasoning" class="icon-button" type="button" aria-label="Enable reasoning"
title="Enable reasoning" aria-pressed="false" disabled>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"
aria-hidden="true">
<path
d="M12 18V5a3 3 0 0 0-5.8-1A4 4 0 0 0 3 10a4 4 0 0 0 1 7.8A4 4 0 0 0 12 18ZM12 5a3 3 0 0 1 5.8-1A4 4 0 0 1 21 10a4 4 0 0 1-1 7.8A4 4 0 0 1 12 18M8 8a4 4 0 0 1-5 2m13-2a4 4 0 0 0 5 2M8 14a4 4 0 0 0-4 4m12-4a4 4 0 0 1 4 4" />
</svg>
</button>
<label class="visually-hidden" for="msgInput">Message</label>
<textarea id="msgInput" rows="1" placeholder="Type your message..."></textarea>
@@ -229,7 +318,8 @@
</div>
<form id="personaForm" class="persona-form">
<div class="persona-form-intro">
<p class="persona-helper">AI Characters shape who you talk to. User personas add context for who you are in the scene.</p>
<p class="persona-helper">AI Characters shape who you talk to. User personas add context for who you
are in the scene.</p>
</div>
<p id="personaFormNotice" class="status hidden"></p>
<div class="persona-form-grid">
@@ -254,11 +344,13 @@
</label>
<label class="field field-wide">
<span>Background</span>
<textarea id="personaBackground" rows="3" placeholder="Backstory, history, role, or origins"></textarea>
<textarea id="personaBackground" rows="3"
placeholder="Backstory, history, role, or origins"></textarea>
</label>
<label class="field field-wide">
<span>Details</span>
<textarea id="personaDetails" rows="4" placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
<textarea id="personaDetails" rows="4"
placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
</label>
<label id="personaExamplesField" class="field field-wide hidden">
<span>Example dialogues</span>
@@ -268,7 +360,8 @@
{{user}}: Hi, Marc!
{{character}}: Nice to meet you</pre>
</div>
<textarea id="personaExampleDialogues" rows="5" placeholder="Write example exchanges that define the AI's tone, phrasing, pacing, and personality."></textarea>
<textarea id="personaExampleDialogues" rows="5"
placeholder="Write example exchanges that define the AI's tone, phrasing, pacing, and personality."></textarea>
</label>
</div>
<div class="persona-actions">
@@ -288,7 +381,8 @@
<button id="roleplayStarterClose" type="button" class="icon-button">Close</button>
</div>
<div class="roleplay-starter-intro">
<p id="roleplayStarterIntroCopy" class="persona-helper">Pick a character and choose who you are before moving on to the opening scene.</p>
<p id="roleplayStarterIntroCopy" class="persona-helper">Pick a character and choose who you are before
moving on to the opening scene.</p>
</div>
<p id="roleplayStarterNotice" class="status hidden"></p>
<div class="roleplay-starter-actions">
@@ -307,9 +401,11 @@
<div id="roleplayStarterStepTwo" class="roleplay-starter-page hidden">
<label class="field field-wide">
<span>Optional opening direction</span>
<textarea id="roleplayScenarioInput" rows="4" placeholder="Describe the kind of opening scene you want, such as a setting, conflict, relationship dynamic, or mood. Leave it empty and the AI will invent its own scenario."></textarea>
<textarea id="roleplayScenarioInput" rows="4"
placeholder="Describe the kind of opening scene you want, such as a setting, conflict, relationship dynamic, or mood. Leave it empty and the AI will invent its own scenario."></textarea>
</label>
<p class="subtitle">Leave this empty if you want the AI Character to invent the scenario on its own. No character limit.</p>
<p class="subtitle">Leave this empty if you want the AI Character to invent the scenario on its own. No
character limit.</p>
<div id="roleplayStarterSuggestions" class="quick-prompts" aria-label="Scenario suggestions"></div>
</div>
<div class="popup-actions">
@@ -321,7 +417,8 @@
</div>
<div id="popupModal" class="modal-overlay hidden">
<div class="modal-card popup-card" role="dialog" aria-modal="true" aria-labelledby="popupTitle" aria-describedby="popupDescription">
<div class="modal-card popup-card" role="dialog" aria-modal="true" aria-labelledby="popupTitle"
aria-describedby="popupDescription">
<div class="modal-header">
<div class="persona-modal-heading">
<p id="popupEyebrow" class="eyebrow">Action</p>
@@ -363,7 +460,8 @@
<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 type="module" src="/script.js?v=20260921-reasoning-capability"></script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<script type="module" src="/js/script.js?v=20260921-reasoning-capability"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+15 -9
View File
@@ -1,23 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Discover personas &middot; Elsewhere</title>
<link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls">
<link rel="icon" href="/img/favicon.svg">
<script src="/js/theme-bootstrap.js?v=20260909-account-controls">
</script>
<link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<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 rel="stylesheet" href="/css/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/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">
</head>
<body class="site-page discover-page">
<!-- site-header -->
<main id="main" class="site-section page-content">
<p class="eyebrow">From the community</p>
<h1>Find your next conversation.</h1>
<p class="hero-copy">Discover characters and personas made by the community. Sign in when you're ready to bring one into your workspace.</p>
<p class="hero-copy">Discover characters and personas made by the community. Sign in when you're ready to bring
one into your workspace.</p>
<label class="field">
<span>Search personas</span>
<input id="publicSearch" type="search" placeholder="Search by name or description">
@@ -30,8 +35,9 @@
<a href="/privacy.html">Privacy</a>
<a href="/tos.html">Terms</a>
</footer>
<script type="module" src="/pages.js?v=20260909-account-controls">
<script type="module" src="/js/pages.js?v=20260909-account-controls">
</script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+10 -5
View File
@@ -1,17 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Home &middot; Elsewhere</title>
<link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls">
<link rel="icon" href="/img/favicon.svg">
<script src="/js/theme-bootstrap.js?v=20260909-account-controls">
</script>
<link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<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>
<body class="site-page home-page">
<!-- site-header -->
@@ -50,8 +54,9 @@
</p>
</main>
<script type="module" src="/pages.js?v=20260909-account-controls">
<script type="module" src="/js/pages.js?v=20260909-account-controls">
</script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>

Before

Width:  |  Height:  |  Size: 250 B

After

Width:  |  Height:  |  Size: 250 B

+7 -7
View File
@@ -1,7 +1,7 @@
import {get, post, put} from "./app/api.js?v=20260909-account-controls";
import {initializeAppearance} from "./app/themeController.js?v=20260909-account-controls";
import {clearStatus, setStatus} from "./app/statusNotice.js?v=20260909-account-controls";
import {populatePersonaForm, readPersonaForm} from "./app/personaForm.js?v=20260909-account-controls";
import { get, post, put } from "./js/api.js?v=20260909-account-controls";
import { initializeAppearance } from "./js/themeController.js?v=20260909-account-controls";
import { clearStatus, setStatus } from "./js/statusNotice.js?v=20260909-account-controls";
import { populatePersonaForm, readPersonaForm } from "./js/personaForm.js?v=20260909-account-controls";
const $ = (id) => document.getElementById(id);
const marketPersonaList = $("marketPersonaList");
@@ -540,7 +540,7 @@ async function startMarketPersonaChat(marketId, userPersonaSelection, scenarioPr
return;
}
if (userPersonaSelection === "create_new") {
window.location.href = "/app/personas?view=my-roleplay-companions&create=user";
window.location.href = "/js/personas?view=my-roleplay-companions&create=user";
return;
}
@@ -623,7 +623,7 @@ if (marketCreateAiCharacterBtn) {
}
if (marketCreateUserPersonaBtn) {
marketCreateUserPersonaBtn.addEventListener("click", () => {
window.location.href = "/app/personas?view=my-roleplay-companions&create=user";
window.location.href = "/js/personas?view=my-roleplay-companions&create=user";
});
}
marketPreviewClose.addEventListener("click", closeMarketPreview);
@@ -633,7 +633,7 @@ marketPreviewModal.addEventListener("click", (event) => {
marketPreviewUserPersonaSelect.addEventListener("change", () => {
if (!pendingMarketPersona || pendingMarketPersona.persona_type !== "assistant") return;
if (marketPreviewUserPersonaSelect.value === "create_new") {
window.location.href = "/app/personas?view=my-roleplay-companions&create=user";
window.location.href = "/js/personas?view=my-roleplay-companions&create=user";
return;
}
marketPreviewConfirm.classList.toggle("hidden", !marketPreviewUserPersonaSelect.value);
+1 -1
View File
@@ -1,4 +1,4 @@
import {get, post} from "/app/api.js?v=20260909-account-controls";
import { get, post } from "/js/api.js?v=20260909-account-controls";
const params = new URLSearchParams(location.search);
function returnPath() {
+10 -10
View File
@@ -1,12 +1,12 @@
import {del, get, post, put, stream} from "./app/api.js?v=20260909-account-controls";
import {regenerateInline} from "./app/regeneration.js";
import {appendSources, createWebSearchToggle} from "./app/webSearch.js";
import {appendAttachments, createAttachmentComposer} from "./app/attachments.js";
import {createReasoningToggle, finishReasoning, updateReasoning} from "./app/reasoning.js?v=20260921-reasoning-capability";
import {closeChatDrawer, openChatDrawer} from "./app/chatDrawer.js?v=20260909-account-controls";
import {onboardingPrompts, requestedChatId} from "./app/constants.js?v=20260909-account-controls";
import {getModelProfile} from "./app/modelProfiles.js?v=20260909-account-controls";
import {applyTheme as applySharedTheme, applyThemeMode as applySharedThemeMode, readStoredAppearance} from "./app/themeController.js?v=20260909-account-controls";
import { del, get, post, put, stream } from "./js/api.js?v=20260909-account-controls";
import { regenerateInline } from "./js/regeneration.js";
import { appendSources, createWebSearchToggle } from "./js/webSearch.js";
import { appendAttachments, createAttachmentComposer } from "./js/attachments.js";
import { createReasoningToggle, finishReasoning, updateReasoning } from "./js/reasoning.js?v=20260921-reasoning-capability";
import { closeChatDrawer, openChatDrawer } from "./js/chatDrawer.js?v=20260909-account-controls";
import { onboardingPrompts, requestedChatId } from "./js/constants.js?v=20260909-account-controls";
import { getModelProfile } from "./js/modelProfiles.js?v=20260909-account-controls";
import { applyTheme as applySharedTheme, applyThemeMode as applySharedThemeMode, readStoredAppearance } from "./js/themeController.js?v=20260909-account-controls";
import {
activeChatTitle,
activeUserPersonaStatus,
@@ -103,7 +103,7 @@ import {
brandLogoTargets,
brandNameTargets,
userPersonaList
} from "./app/dom.js?v=20260909-account-controls";
} from "./js/dom.js?v=20260909-account-controls";
const headerModelButton = document.getElementById("headerModelButton");
const webSearchButton = document.getElementById("webSearch");
+4 -4
View File
@@ -1,11 +1,11 @@
import {del, get, post, put} from "./app/api.js?v=20260909-account-controls";
import { del, get, post, put } from "./js/api.js?v=20260909-account-controls";
import {
applyTheme as applySharedTheme,
applyThemeMode as applySharedThemeMode,
initializeAppearance
} from "./app/themeController.js?v=20260909-account-controls";
import {clearStatus, setStatus} from "./app/statusNotice.js?v=20260909-account-controls";
import {populatePersonaForm, readPersonaForm} from "./app/personaForm.js?v=20260909-account-controls";
} from "./js/themeController.js?v=20260909-account-controls";
import { clearStatus, setStatus } from "./js/statusNotice.js?v=20260909-account-controls";
import { populatePersonaForm, readPersonaForm } from "./js/personaForm.js?v=20260909-account-controls";
const $ = (id) => document.getElementById(id);
const settingsNotice = $("settingsNotice");
+17 -10
View File
@@ -1,16 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Elsewhere</title>
<link rel="icon" href="/favicon.svg">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/pages.css?v=20260909-account-controls">
<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 rel="icon" href="/img/favicon.svg">
<script src="/js/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/css/style.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/pages.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/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">
</head>
<body class="site-page landing-page">
<!-- site-header -->
<main id="main" class="landing-entry">
@@ -20,13 +24,16 @@
<h1>Take a thought<br>somewhere.</h1>
<p>Talk it through. Make something up.<br>Meet a character and see where it goes.</p>
<div class="site-actions">
<a data-start-link class="primary-action" href="/register">Step inside <span aria-hidden="true">&rarr;</span></a>
<a data-start-link class="primary-action" href="/register">Step inside <span
aria-hidden="true">&rarr;</span></a>
<a href="/market">Meet the personas</a>
</div>
</div>
</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>
<script type="module" src="/pages.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<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="/js/pages.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+41 -21
View File
@@ -1,17 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
<title>Elsewhere Discover</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<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">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.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">
<script src="/js/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/css/style.css?v=20260909-account-controls" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/shell.css?v=20260909-account-controls">
</head>
<body class="site-page market-page" data-theme="fakegpt" data-theme-mode="light">
<!-- site-header -->
<main id="main" class="market-shell page-content">
@@ -20,7 +24,8 @@
<p class="eyebrow">Shared personas</p>
<h1>Discover</h1>
</div>
<div class="market-header-actions"><button id="refreshMarket" class="secondary-action" type="button">Refresh</button></div>
<div class="market-header-actions"><button id="refreshMarket" class="secondary-action"
type="button">Refresh</button></div>
</header>
<div class="market-search">
@@ -32,8 +37,10 @@
<section class="settings-layout market-settings-layout">
<aside class="settings-nav" aria-label="Market sections" role="tablist">
<button class="settings-nav-item active" type="button" role="tab" aria-selected="true" data-market-view="ai-characters">Characters</button>
<button class="settings-nav-item" type="button" role="tab" aria-selected="false" data-market-view="user-personas">User personas</button>
<button class="settings-nav-item active" type="button" role="tab" aria-selected="true"
data-market-view="ai-characters">Characters</button>
<button class="settings-nav-item" type="button" role="tab" aria-selected="false"
data-market-view="user-personas">User personas</button>
</aside>
<div class="settings-content">
@@ -56,7 +63,8 @@
<option value="alphabetical">A-Z</option>
</select>
</label>
<button id="marketCreateAiCharacter" type="button" class="secondary-action">Create AI Character</button>
<button id="marketCreateAiCharacter" type="button" class="secondary-action">Create AI
Character</button>
</div>
</div>
<div id="marketPersonaList" class="persona-list"></div>
@@ -82,7 +90,8 @@
<option value="alphabetical">A-Z</option>
</select>
</label>
<button id="marketCreateUserPersona" type="button" class="secondary-action">Create User Persona</button>
<button id="marketCreateUserPersona" type="button" class="secondary-action">Create User
Persona</button>
</div>
</div>
<div id="marketUserPersonaList" class="persona-list"></div>
@@ -99,7 +108,8 @@
<p class="eyebrow">Persona preview</p>
<h3 id="marketPreviewTitle">Persona details</h3>
<h4 id="marketPreviewName" class="market-preview-name">Persona details</h4>
<p id="marketPreviewIntro" class="subtitle">Review this persona and choose how you want to use it.</p>
<p id="marketPreviewIntro" class="subtitle">Review this persona and choose how you want to use it.
</p>
</div>
<button id="marketPreviewClose" type="button" class="icon-button">Close</button>
</div>
@@ -138,9 +148,11 @@
<div id="marketPreviewStepTwo" class="roleplay-starter-page hidden">
<label id="marketPreviewScenarioField" class="field market-preview-scenario-field">
<span>Scenario (optional)</span>
<textarea id="marketPreviewScenario" rows="4" placeholder="Describe the opening scene you want. Leave it empty and the AI Character will invent its own scenario."></textarea>
<textarea id="marketPreviewScenario" rows="4"
placeholder="Describe the opening scene you want. Leave it empty and the AI Character will invent its own scenario."></textarea>
</label>
<p class="subtitle">Leave this empty if you want the AI Character to invent the scenario on its own. No character limit.</p>
<p class="subtitle">Leave this empty if you want the AI Character to invent the scenario on its own.
No character limit.</p>
</div>
<div class="market-preview-footer">
<button id="marketPreviewConfirm" type="button" class="primary-action">Take persona</button>
@@ -160,7 +172,8 @@
</div>
<form id="marketPersonaForm" class="persona-form">
<div class="persona-form-intro">
<p class="persona-helper">Create or update your AI Characters for roleplay chats and marketplace publishing.</p>
<p class="persona-helper">Create or update your AI Characters for roleplay chats and marketplace
publishing.</p>
</div>
<p id="marketPersonaFormNotice" class="status hidden"></p>
<div class="persona-form-grid">
@@ -178,11 +191,13 @@
</label>
<label class="field field-wide">
<span>Background</span>
<textarea id="marketPersonaBackground" rows="3" placeholder="Backstory, history, role, or origins"></textarea>
<textarea id="marketPersonaBackground" rows="3"
placeholder="Backstory, history, role, or origins"></textarea>
</label>
<label class="field field-wide">
<span>Details</span>
<textarea id="marketPersonaDetails" rows="4" placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
<textarea id="marketPersonaDetails" rows="4"
placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
</label>
<label class="field field-wide">
<span>Example dialogues</span>
@@ -192,7 +207,8 @@
{{user}}: Hi, Marc!
{{character}}: Nice to meet you</pre>
</div>
<textarea id="marketPersonaExampleDialogues" rows="5" placeholder="Write example exchanges that define the character's tone, phrasing, pacing, and personality."></textarea>
<textarea id="marketPersonaExampleDialogues" rows="5"
placeholder="Write example exchanges that define the character's tone, phrasing, pacing, and personality."></textarea>
</label>
</div>
<div class="persona-actions">
@@ -202,7 +218,8 @@
</div>
</div>
<div id="marketActivityOverlay" class="chat-activity-overlay hidden" role="status" aria-live="polite" aria-atomic="true">
<div id="marketActivityOverlay" class="chat-activity-overlay hidden" role="status" aria-live="polite"
aria-atomic="true">
<div class="chat-activity-card">
<div class="chat-activity-mark" aria-hidden="true">
<span></span>
@@ -216,7 +233,8 @@
</div>
<div id="marketPopupModal" class="modal-overlay hidden">
<div class="modal-card popup-card" role="dialog" aria-modal="true" aria-labelledby="marketPopupTitle" aria-describedby="marketPopupDescription">
<div class="modal-card popup-card" role="dialog" aria-modal="true" aria-labelledby="marketPopupTitle"
aria-describedby="marketPopupDescription">
<div class="modal-header">
<div class="persona-modal-heading">
<p id="marketPopupEyebrow" class="eyebrow">Action</p>
@@ -237,6 +255,8 @@
</div>
</div>
</div>
<script type="module" src="/market.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script></body>
<script type="module" src="/js/market.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+17 -9
View File
@@ -1,16 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy – DeepFake</title>
<link rel="stylesheet" href="style.css?v=20260909-account-controls" />
<link rel="stylesheet" href="legal.css?v=20260909-account-controls" />
<script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<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 rel="stylesheet" href="/css/style.css?v=20260909-account-controls" />
<link rel="stylesheet" href="/css/legal.css?v=20260909-account-controls" />
<script src="/js/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/css/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">
</head>
<body class="site-page legal-page">
<!-- site-header -->
<main id="main" class="legal-content">
@@ -58,12 +62,14 @@
<section>
<h2>5. Data Retention</h2>
<p>We retain data as long as your account is active. If you request deletion, your data will be deleted unless we must retain it for legal reasons.</p>
<p>We retain data as long as your account is active. If you request deletion, your data will be deleted
unless we must retain it for legal reasons.</p>
</section>
<section>
<h2>6. Data Transfers</h2>
<p>Your data may be stored or processed where our servers reside. Switzerland has an adequacy decision with the EU, meaning transfers can occur without additional safeguards.</p>
<p>Your data may be stored or processed where our servers reside. Switzerland has an adequacy decision with
the EU, meaning transfers can occur without additional safeguards.</p>
</section>
<section>
@@ -81,7 +87,8 @@
<section>
<h2>8. Data Security</h2>
<p>We implement reasonable technical and organizational measures to protect your data — encryption for passwords and secure database access.</p>
<p>We implement reasonable technical and organizational measures to protect your data — encryption for
passwords and secure database access.</p>
</section>
<section>
@@ -89,6 +96,7 @@
<p>We may update this document. Changes will be posted with a “Last updated” date.</p>
</section>
</main>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+43 -22
View File
@@ -1,17 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
<meta charset="UTF-8" />
<title>Elsewhere Settings</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<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">
<script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/style.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">
<script src="/js/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/css/style.css?v=20260909-account-controls" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/shell.css?v=20260909-account-controls">
<link rel="stylesheet" href="/css/shell.css?v=20260909-account-controls">
</head>
<body class="site-page settings-page" data-theme="fakegpt" data-theme-mode="light">
<!-- site-header -->
<main id="main" class="settings-shell page-content">
@@ -26,10 +30,13 @@
<section class="settings-layout">
<aside class="settings-nav" aria-label="Settings sections" role="tablist">
<button class="settings-nav-item active" type="button" role="tab" aria-selected="true" data-settings-view="personal">Account</button>
<button class="settings-nav-item active" type="button" role="tab" aria-selected="true"
data-settings-view="personal">Account</button>
<button class="settings-nav-item" type="button" role="tab" aria-selected="false" data-settings-view="design">Appearance</button>
<button id="diagnosticsNavItem" class="settings-nav-item hidden" type="button" role="tab" aria-selected="false" data-settings-view="diagnostics">Diagnostics</button>
<button class="settings-nav-item" type="button" role="tab" aria-selected="false"
data-settings-view="design">Appearance</button>
<button id="diagnosticsNavItem" class="settings-nav-item hidden" type="button" role="tab"
aria-selected="false" data-settings-view="diagnostics">Diagnostics</button>
</aside>
<div class="settings-content">
@@ -87,12 +94,14 @@
<div>
<p class="eyebrow">Account data</p>
<h2>Export and sessions</h2>
<p class="subtitle">Download your account data or sign out every active browser session.</p>
<p class="subtitle">Download your account data or sign out every active browser session.
</p>
</div>
</div>
<div class="persona-actions">
<button id="exportAccount" type="button" class="secondary-action">Export account</button>
<button id="logoutAllSessions" type="button" class="secondary-action">Logout all sessions</button>
<button id="logoutAllSessions" type="button" class="secondary-action">Logout all
sessions</button>
</div>
</div>
@@ -101,16 +110,19 @@
<div>
<p class="eyebrow">Danger zone</p>
<h2>Delete account</h2>
<p class="subtitle">This permanently removes your account, chats, personas, templates, and market activity.</p>
<p class="subtitle">This permanently removes your account, chats, personas, templates,
and market activity.</p>
</div>
</div>
<div class="persona-actions">
<button id="deleteAccount" type="button" class="secondary-action danger">Delete account</button>
<button id="deleteAccount" type="button" class="secondary-action danger">Delete
account</button>
</div>
</div>
</section>
<section class="settings-view" role="tabpanel" aria-hidden="true" data-settings-panel="my-roleplay-companions">
<section class="settings-view" role="tabpanel" aria-hidden="true"
data-settings-panel="my-roleplay-companions">
<div class="settings-panel">
<div class="settings-section-header">
<div>
@@ -118,8 +130,10 @@
<h2>My personas</h2>
</div>
<div class="market-header-actions action-pair">
<button id="createAiCharacter" type="button" class="secondary-action">Create AI Character</button>
<button id="createUserPersona" type="button" class="secondary-action">Create User Persona</button>
<button id="createAiCharacter" type="button" class="secondary-action">Create AI
Character</button>
<button id="createUserPersona" type="button" class="secondary-action">Create User
Persona</button>
</div>
</div>
<h3>AI Characters</h3>
@@ -174,7 +188,8 @@
<div>
<p class="eyebrow">Admin</p>
<h2>Model diagnostics</h2>
<p class="subtitle">Backend status, model inventory, latency, and configuration warnings.</p>
<p class="subtitle">Backend status, model inventory, latency, and configuration
warnings.</p>
</div>
<button id="refreshDiagnostics" type="button" class="secondary-action">Refresh</button>
</div>
@@ -252,11 +267,13 @@
</label>
<label class="field field-wide">
<span>Background</span>
<textarea id="settingsPersonaBackground" rows="3" placeholder="Backstory, history, role, or origins"></textarea>
<textarea id="settingsPersonaBackground" rows="3"
placeholder="Backstory, history, role, or origins"></textarea>
</label>
<label class="field field-wide">
<span>Details</span>
<textarea id="settingsPersonaDetails" rows="4" placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
<textarea id="settingsPersonaDetails" rows="4"
placeholder="Personality traits, voice, goals, quirks, boundaries, style"></textarea>
</label>
<label id="settingsPersonaExamplesField" class="field field-wide hidden">
<span>Example dialogues</span>
@@ -266,7 +283,8 @@
{{user}}: Hi, Marc!
{{character}}: Nice to meet you</pre>
</div>
<textarea id="settingsPersonaExampleDialogues" rows="5" placeholder="Write example exchanges that define the character's tone, phrasing, pacing, and personality."></textarea>
<textarea id="settingsPersonaExampleDialogues" rows="5"
placeholder="Write example exchanges that define the character's tone, phrasing, pacing, and personality."></textarea>
</label>
</div>
<div class="persona-actions">
@@ -277,7 +295,8 @@
</div>
<div id="settingsPopupModal" class="modal-overlay hidden">
<div class="modal-card popup-card" role="dialog" aria-modal="true" aria-labelledby="settingsPopupTitle" aria-describedby="settingsPopupDescription">
<div class="modal-card popup-card" role="dialog" aria-modal="true" aria-labelledby="settingsPopupTitle"
aria-describedby="settingsPopupDescription">
<div class="modal-header">
<div class="persona-modal-heading">
<p id="settingsPopupEyebrow" class="eyebrow">Action</p>
@@ -298,6 +317,8 @@
</div>
</div>
</div>
<script type="module" src="/settings.js?v=20260909-account-controls"></script>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script></body>
<script type="module" src="/js/settings.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+20 -11
View File
@@ -1,16 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Terms of Service – DeepFake</title>
<link rel="stylesheet" href="style.css?v=20260909-account-controls" />
<link rel="stylesheet" href="legal.css?v=20260909-account-controls" />
<script src="/app/theme-bootstrap.js?v=20260909-account-controls"></script>
<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 rel="stylesheet" href="/css/style.css?v=20260909-account-controls" />
<link rel="stylesheet" href="/css/legal.css?v=20260909-account-controls" />
<script src="/js/theme-bootstrap.js?v=20260909-account-controls"></script>
<link rel="stylesheet" href="/css/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">
</head>
<body class="site-page legal-page">
<!-- site-header -->
<main id="main" class="legal-content">
@@ -24,22 +28,26 @@
<section>
<h2>User Conduct</h2>
<p>You are responsible for content you submit and for maintaining secure account credentials. Do not use DeepFake for unlawful purposes.</p>
<p>You are responsible for content you submit and for maintaining secure account credentials. Do not use
DeepFake for unlawful purposes.</p>
</section>
<section>
<h2>Data Processing</h2>
<p>By using the service, you consent to storing and processing your data as described in the <a href="/privacy.html">Privacy Policy</a>.</p>
<p>By using the service, you consent to storing and processing your data as described in the <a
href="/privacy.html">Privacy Policy</a>.</p>
</section>
<section>
<h2>Limitation of Liability</h2>
<p>We are not liable for service interruptions, data loss, or any indirect damages arising from your use of DeepFake AI.</p>
<p>We are not liable for service interruptions, data loss, or any indirect damages arising from your use of
DeepFake AI.</p>
</section>
<section>
<h2>Governing Law</h2>
<p>These Terms are governed by applicable laws in your jurisdiction (e.g., Switzerland or EU Member States).</p>
<p>These Terms are governed by applicable laws in your jurisdiction (e.g., Switzerland or EU Member States).
</p>
</section>
<section>
@@ -47,6 +55,7 @@
<p>We may modify these Terms at any time. Updates will be reflected with a new “Last updated” date.</p>
</section>
</main>
<script type="module" src="/app/workspaceNavigation.js?v=20260909-account-controls"></script>
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
</body>
</html>
+11 -11
View File
@@ -6,7 +6,7 @@ const files = [
"src/services/attachmentService.js",
"src/services/visionAnalysis.js",
"src/routes/uploads.js",
"public/app/attachments.js",
"public/js/attachments.js",
"server.js",
"src/app.js",
"src/pageLayout.js",
@@ -32,18 +32,18 @@ const files = [
"src/services/personaService.js",
"public/script.js",
"public/pages.js",
"public/app/workspaceNavigation.js",
"public/js/workspaceNavigation.js",
"public/settings.js",
"public/market.js",
"public/app/api.js",
"public/app/regeneration.js",
"public/app/webSearch.js",
"public/app/reasoning.js",
"public/app/modelCapabilities.js",
"public/app/chatDrawer.js",
"public/app/constants.js",
"public/app/dom.js",
"public/app/modelProfiles.js"
"public/js/api.js",
"public/js/regeneration.js",
"public/js/webSearch.js",
"public/js/reasoning.js",
"public/js/modelCapabilities.js",
"public/js/chatDrawer.js",
"public/js/constants.js",
"public/js/dom.js",
"public/js/modelProfiles.js"
];
for (const file of files) {
+1 -1
View File
@@ -19,7 +19,7 @@ import {
} from "./chatMessageHelpers.js";
import { SearXNGSearchProvider, WebSearchError, buildWebSearchContext, retrieveWebSearch } from "./webSearchService.js";
import {supportsReasoning} from "../../public/app/modelCapabilities.js";
import { supportsReasoning } from "../../public/js/modelCapabilities.js";
export function createChatService(repositories, modelService, config, webSearchProvider = new SearXNGSearchProvider(config), attachmentService = null) {
const formatChatMessage = row => {
+1 -1
View File
@@ -1,4 +1,4 @@
import {supportsReasoning} from "../../public/app/modelCapabilities.js";
import { supportsReasoning } from "../../public/js/modelCapabilities.js";
import { AttachmentError } from "./attachmentService.js";
class ModelServiceError extends Error {
+1 -1
View File
@@ -1,6 +1,6 @@
import test from "node:test";
import assert from "node:assert/strict";
import {appendAttachments, createAttachmentComposer} from "../public/app/attachments.js";
import { appendAttachments, createAttachmentComposer } from "../public/js/attachments.js";
class Element {
constructor() { this.children = []; this.listeners = {}; this.classList = { add() { }, remove() { } }; }
+20 -10
View File
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
import { createServer } from "node:http";
import { createModelService } from "../src/services/modelService.js";
import { createApp } from "../src/app.js";
import {stream} from "../public/app/api.js";
import { stream } from "../public/js/api.js";
const config = {
modelApiBaseUrl: "http://fixture", modelRequestTimeoutMs: 20,
@@ -17,13 +17,15 @@ test("active tokens outlive the legacy total timeout and arrive before completio
let first;
const firstChunk = new Promise(resolve => { first = resolve; });
let interval;
const service = createModelService(config, {fetch: async () => new Response(new ReadableStream({
const service = createModelService(config, {
fetch: async () => new Response(new ReadableStream({
start(controller) {
interval = setInterval(() => controller.enqueue(line({ message: { content: "x" } })), 15);
finish = () => { clearInterval(interval); controller.enqueue(line({ done: true })); controller.close(); };
},
cancel() { clearInterval(interval); }
}))});
}))
});
let completed = false;
const pending = service.streamReply("qwen3:14b", [], () => first()).then(reply => { completed = true; return reply; });
try {
@@ -45,7 +47,8 @@ test("connection, inactivity and overall deadlines abort generation", async (t)
modelConnectTimeoutMs: kind === "connection" ? 25 : 1000,
modelInactivityTimeoutMs: kind === "inactivity" ? 25 : 1000,
modelMaxGenerationMs: kind === "overall" ? 70 : 1000
}, {fetch: async (_url, {signal}) => {
}, {
fetch: async (_url, { signal }) => {
if (kind === "connection") return new Promise((_resolve, reject) => {
signal.addEventListener("abort", () => { cancelled = true; reject(new DOMException("Aborted", "AbortError")); });
});
@@ -55,7 +58,8 @@ test("connection, inactivity and overall deadlines abort generation", async (t)
},
cancel() { cancelled = true; clearInterval(interval); }
}));
}});
}
});
await assert.rejects(service.streamReply("qwen3:14b", [], () => { }), { code: "MODEL_TIMEOUT" });
assert.equal(cancelled, true);
});
@@ -88,17 +92,23 @@ test("browser disconnect aborts Ollama and incomplete output is not persisted",
let signal;
let releaseAbort;
const aborted = new Promise(resolve => { releaseAbort = resolve; });
const app = createApp({config: {
const app = createApp({
config: {
dbPath: ":memory:", testMode: true, sessionSecret: "test-only",
chatSummaryUpdateEveryMessages: 0, structuredRequestLoggingEnabled: false,
eventLoopLagMonitorEnabled: false, slowRequestLoggingEnabled: false
}, modelOverrides: {fetch: async (_url, options) => {
}, modelOverrides: {
fetch: async (_url, options) => {
signal = options.signal;
signal.addEventListener("abort", releaseAbort, { once: true });
return new Response(new ReadableStream({start(controller) {
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(line({ message: { content: "partial" } }));
}}));
}}});
}
}));
}
}
});
const server = createServer(app);
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
t.after(async () => {
+15 -9
View File
@@ -3,9 +3,9 @@ import assert from "node:assert/strict";
import { createServer } from "node:http";
import { createApp } from "../src/app.js";
import { createModelService } from "../src/services/modelService.js";
import {stream} from "../public/app/api.js";
import {createReasoningToggle, finishReasoning, updateReasoning} from "../public/app/reasoning.js";
import {getModelFamily, supportsReasoning} from "../public/app/modelCapabilities.js";
import { stream } from "../public/js/api.js";
import { createReasoningToggle, finishReasoning, updateReasoning } from "../public/js/reasoning.js";
import { getModelFamily, supportsReasoning } from "../public/js/modelCapabilities.js";
test("thinking streams separately and survives reload, retry, branch and import", async (t) => {
const requests = [];
@@ -19,24 +19,30 @@ test("thinking streams separately and survives reload, retry, branch and import"
t.after(() => new Promise(resolve => { searchServer.closeAllConnections(); searchServer.close(resolve); }));
const modelService = createModelService({
modelApiBaseUrl: "http://fixture", modelRequestTimeoutMs: 1000, modelUnloadAfterMs: 60000
}, {fetch: async (_url, options) => {
}, {
fetch: async (_url, options) => {
requests.push(JSON.parse(options.body));
const chunks = [
'{"message":{"thinking":"Consider "}}\n{"message":{"thi',
'nking":"carefully.","content":""}}\n',
'{"message":{"content":"Answer"},"done":true}'
];
return new Response(new ReadableStream({start(controller) {
return new Response(new ReadableStream({
start(controller) {
for (const chunk of chunks) controller.enqueue(new TextEncoder().encode(chunk));
controller.close();
}}));
}});
const app = createApp({modelService, config: {
}
}));
}
});
const app = createApp({
modelService, config: {
dbPath: ":memory:", testMode: true, sessionSecret: "test-only",
searxngUrl: `http://127.0.0.1:${searchServer.address().port}`,
chatSummaryUpdateEveryMessages: 0, eventLoopLagMonitorEnabled: false,
structuredRequestLoggingEnabled: false, slowRequestLoggingEnabled: false
}});
}
});
const server = createServer(app);
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
t.after(async () => {
+18 -10
View File
@@ -7,8 +7,8 @@ import {runMigrations} from "../src/db/schema.js";
import { SearXNGSearchProvider, WebSearchError, buildWebSearchContext, buildWebSearchQuery, decideWebSearch, selectSearchResults, retrieveWebSearch } from "../src/services/webSearchService.js";
import { createModelService } from "../src/services/modelService.js";
import { formatChatMessage } from "../src/services/chatMessageHelpers.js";
import {appendSources, createWebSearchToggle} from "../public/app/webSearch.js";
import {stream} from "../public/app/api.js";
import { appendSources, createWebSearchToggle } from "../public/js/webSearch.js";
import { stream } from "../public/js/api.js";
// Local protocol fixtures are test-only; production always uses the configured provider.
const result = { title: "Major events on September 10, 2025", url: "https://example.org/news", content: "A news report covering major events on September 10, 2025." };
@@ -26,10 +26,12 @@ test("SearXNG encodes queries, limits results and treats result data as untruste
const upstream = await listen(t, (req, res) => {
received = new URL(req.url, "http://localhost");
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({results: [
res.end(JSON.stringify({
results: [
{ title: "bad", url: "javascript:alert(1)" },
...Array.from({ length: 9 }, (_, index) => ({ title: `${"T".repeat(290)} ${index} ${"T".repeat(100)}`, url: `https://example.org/${index}`, content: "S".repeat(2000) }))
]}));
]
}));
});
const provider = new SearXNGSearchProvider({ searxngUrl: `${upstream.url}/base`, webSearchTimeoutMs: 1000 });
const query = `NVIDIA & heute? # ü ${"long ".repeat(200)}`;
@@ -151,12 +153,14 @@ test("chat search integrates with streaming, persistence, personas, settings, re
},
mapError(error) { return { status: 500, body: { error: error.message } }; }
};
const app = createApp({modelService, config: {
const app = createApp({
modelService, config: {
dbPath: ":memory:", testMode: true, sessionSecret: "test-only", searxngUrl: upstream.url,
webSearchTimeoutMs: 1000, chatSummaryUpdateEveryMessages: 0, eventLoopLagMonitorEnabled: false,
structuredRequestLoggingEnabled: false, slowRequestLoggingEnabled: false,
now: () => new Date("2026-09-21T12:00:00.000Z")
}});
}
});
t.after(() => app.locals.close());
const backend = await listen(t, app);
let cookie;
@@ -329,22 +333,26 @@ test("date evidence admits individual events, rejects empty/wrong dates, and bou
assert.equal(selected.length, 2);
assert.deepEqual(selected.map(source => source.snippet), useful.map(item => item.content));
const queries = [];
const search = await retrieveWebSearch(question, {async search(query, options) {
const search = await retrieveWebSearch(question, {
async search(query, options) {
queries.push(query);
assert.equal(options.category, undefined);
return queries.length === 1 ? weak : [...useful, useful[0]];
}});
}
});
assert.equal(search.state, "succeeded");
assert.equal(queries.length, 2);
assert.ok(queries.every(query => query.includes("2025")));
assert.deepEqual(search.sources, selected);
let duplicateCalls = 0;
const refreshed = await retrieveWebSearch(question, {async search() {
const refreshed = await retrieveWebSearch(question, {
async search() {
duplicateCalls++;
return duplicateCalls === 1
? [{ ...useful[0], content: "Browse the latest headlines and on this day pages." }]
: [useful[0]];
}});
}
});
assert.equal(refreshed.sources[0].snippet, useful[0].content);
let calls = 0;
await retrieveWebSearch(question, { async search() { calls++; return useful; } });