Revamp workspace archive handling with async support, input validation improvements and better session management
This commit is contained in:
+415
-367
@@ -2,457 +2,505 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta charset="UTF-8"/>
|
||||
<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">
|
||||
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=20261012-chat-usability" />
|
||||
<link rel="stylesheet" href="/css/style.css?v=20261012-chat-usability"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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">
|
||||
<section id="chat" class="card chat-card chat-focus-card hidden" aria-label="AI chat workspace">
|
||||
<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>
|
||||
<label class="field compact-field">
|
||||
<span>Search</span>
|
||||
<input id="chatSearch" type="search" placeholder="Search chats" />
|
||||
</label>
|
||||
<!-- site-header -->
|
||||
<div class="app-shell">
|
||||
<section id="chat" class="card chat-card chat-focus-card hidden" aria-label="AI chat workspace">
|
||||
<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-middle">
|
||||
<div id="chatList" class="chat-list chat-list-grouped"></div>
|
||||
<div id="chatListLoading" class="chat-loading hidden" role="status" aria-live="polite">
|
||||
<span class="chat-loading-dot" aria-hidden="true"></span>
|
||||
<span id="chatListLoadingText">Loading chats...</span>
|
||||
</div>
|
||||
<label class="field compact-field">
|
||||
<span>Search</span>
|
||||
<input id="chatSearch" type="search" placeholder="Search chats"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="sidebar-middle">
|
||||
<div id="chatList" class="chat-list chat-list-grouped"></div>
|
||||
<div id="chatListLoading" class="chat-loading hidden" role="status" aria-live="polite">
|
||||
<span class="chat-loading-dot" aria-hidden="true"></span>
|
||||
<span id="chatListLoadingText">Loading chats...</span>
|
||||
</div>
|
||||
</aside>
|
||||
<button id="chatSidebarBackdrop" class="chat-sidebar-backdrop" type="button"
|
||||
</div>
|
||||
</aside>
|
||||
<button id="chatSidebarBackdrop" class="chat-sidebar-backdrop" type="button"
|
||||
aria-label="Close conversation history" hidden></button>
|
||||
|
||||
<section id="main" class="chat-main chat-main-focus" aria-label="Active conversation">
|
||||
<div class="chat-heading">
|
||||
<p id="workspaceNotice" class="status hidden" role="status" aria-live="polite"></p>
|
||||
<header class="chat-header chat-header-focus">
|
||||
<button id="chatSidebarToggle" class="icon-button sidebar-toggle" type="button"
|
||||
<section id="main" class="chat-main chat-main-focus" aria-label="Active conversation">
|
||||
<div class="chat-heading">
|
||||
<p id="workspaceNotice" class="status 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"
|
||||
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>
|
||||
<label class="chat-context-item"><span>Chat with</span><select id="personalitySelect" aria-label="Conversation personality"></select></label>
|
||||
</div>
|
||||
id="headerModelName">Loading...</strong></button>
|
||||
<label class="chat-context-item"><span>Chat with</span><select id="personalitySelect"
|
||||
aria-label="Conversation personality"></select></label>
|
||||
</div>
|
||||
<div class="chat-header-actions">
|
||||
<div class="chat-actions-menu">
|
||||
<button id="chatDrawerButton" class="secondary-action overflow-trigger"
|
||||
</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">
|
||||
<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 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>
|
||||
<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 class="chat-drawer-panel">
|
||||
<div class="chat-drawer-header">
|
||||
<div>
|
||||
<p class="eyebrow">Conversation</p>
|
||||
<h2 id="chatDrawerTitle">Settings</h2>
|
||||
</div>
|
||||
<div class="chat-drawer-body">
|
||||
<div class="chat-drawer-summary">
|
||||
<div class="chat-drawer-pills">
|
||||
<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
|
||||
class="context-pill hidden">Character: none</span>
|
||||
<span id="chatUserPersonaPill" class="context-pill hidden">User
|
||||
persona: none</span>
|
||||
</div>
|
||||
</div>
|
||||
<details
|
||||
</div>
|
||||
<details
|
||||
class="chat-drawer-section chat-drawer-disclosure chat-drawer-section--model"
|
||||
open>
|
||||
<summary class="chat-drawer-row">
|
||||
<summary class="chat-drawer-row">
|
||||
<span class="chat-drawer-row-label">
|
||||
<span class="eyebrow">AI model</span>
|
||||
<strong>Model</strong>
|
||||
</span>
|
||||
<span class="chat-drawer-row-meta">
|
||||
<span class="chat-drawer-row-meta">
|
||||
<strong id="modelBadgeName">Loading...</strong>
|
||||
<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 class="model-select">
|
||||
<div class="model-select-header">
|
||||
<label for="model">Model</label>
|
||||
<span id="modelCount" class="model-count">Loading</span>
|
||||
</div>
|
||||
<div class="model-select-control">
|
||||
<select id="model">
|
||||
<option disabled selected>Loading models...</option>
|
||||
</select>
|
||||
<span class="model-select-chevron"
|
||||
aria-hidden="true"></span>
|
||||
</div>
|
||||
</summary>
|
||||
<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>
|
||||
<span id="modelCount" class="model-count">Loading</span>
|
||||
</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>
|
||||
</div>
|
||||
<p id="modelHelpSummary" class="model-help-summary">Pick a
|
||||
model to see what it is best suited for.</p>
|
||||
<div class="model-select-control">
|
||||
<select id="model">
|
||||
<option disabled selected>Loading models...</option>
|
||||
</select>
|
||||
<span class="model-select-chevron"
|
||||
aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<details class="chat-drawer-section generation-advanced">
|
||||
<summary class="chat-drawer-row"><span class="chat-drawer-row-label"><strong>Generation settings</strong></span><span class="chat-drawer-row-hint">Advanced</span></summary>
|
||||
<div class="generation-settings-grid chat-drawer-disclosure-panel">
|
||||
<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 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>
|
||||
</div>
|
||||
</details>
|
||||
<div class="chat-drawer-section">
|
||||
<p class="dropdown-section-label">Conversation actions</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>
|
||||
<p id="modelHelpSummary" class="model-help-summary">Pick a
|
||||
model to see what it is best suited for.</p>
|
||||
</div>
|
||||
</div>
|
||||
<details class="chat-drawer-section chat-drawer-more">
|
||||
<summary class="chat-drawer-row chat-drawer-row--compact">
|
||||
</details>
|
||||
<details class="chat-drawer-section generation-advanced">
|
||||
<summary class="chat-drawer-row"><span
|
||||
class="chat-drawer-row-label"><strong>Generation settings</strong></span><span
|
||||
class="chat-drawer-row-hint">Advanced</span></summary>
|
||||
<div class="generation-settings-grid chat-drawer-disclosure-panel">
|
||||
<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>
|
||||
</details>
|
||||
<div class="chat-drawer-section">
|
||||
<p class="dropdown-section-label">Conversation actions</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>
|
||||
</div>
|
||||
</div>
|
||||
<details class="chat-drawer-section chat-drawer-more">
|
||||
<summary class="chat-drawer-row chat-drawer-row--compact">
|
||||
<span class="chat-drawer-row-label">
|
||||
<strong>Conversation tools</strong>
|
||||
</span>
|
||||
<span class="chat-drawer-row-hint">Show</span>
|
||||
</summary>
|
||||
<div class="chat-drawer-action-list chat-drawer-disclosure-panel">
|
||||
<button id="moveChatFolder"
|
||||
<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"
|
||||
data-chat-action="folder">Move to folder
|
||||
</button>
|
||||
<button id="manageChatMemory"
|
||||
class="secondary-action chat-drawer-action" type="button"
|
||||
data-chat-action="memory">Conversation memory</button>
|
||||
<button id="exploreBranches" type="button" class="secondary-action chat-drawer-action" data-chat-action="branches">Branches & compare</button>
|
||||
<button id="managePersonaState"
|
||||
class="secondary-action chat-drawer-action" type="button"
|
||||
data-chat-action="personaState">Roleplay state</button>
|
||||
</div>
|
||||
</details>
|
||||
<details class="chat-drawer-section chat-drawer-more">
|
||||
<summary class="chat-drawer-row chat-drawer-row--compact"><span class="chat-drawer-row-label"><strong>Workspace files</strong></span><span class="chat-drawer-row-hint">Show</span></summary>
|
||||
<div class="chat-drawer-action-list chat-drawer-disclosure-panel">
|
||||
<button id="backupWorkspace"
|
||||
data-chat-action="memory">Conversation memory
|
||||
</button>
|
||||
<button id="exploreBranches" type="button"
|
||||
class="secondary-action chat-drawer-action"
|
||||
type="button">Download workspace archive</button>
|
||||
<button id="importWorkspace"
|
||||
data-chat-action="branches">Branches & compare
|
||||
</button>
|
||||
<button id="managePersonaState"
|
||||
class="secondary-action chat-drawer-action" type="button"
|
||||
data-chat-action="personaState">Roleplay state
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
<details class="chat-drawer-section chat-drawer-more">
|
||||
<summary class="chat-drawer-row chat-drawer-row--compact"><span
|
||||
class="chat-drawer-row-label"><strong>Workspace files</strong></span><span
|
||||
class="chat-drawer-row-hint">Show</span></summary>
|
||||
<div class="chat-drawer-action-list chat-drawer-disclosure-panel">
|
||||
<button id="backupWorkspace"
|
||||
class="secondary-action chat-drawer-action"
|
||||
type="button">Import workspace</button>
|
||||
<input id="importWorkspaceFile" class="hidden" type="file"
|
||||
accept="application/json,.json,.tar.gz,application/gzip" />
|
||||
</div>
|
||||
</details>
|
||||
<details class="chat-drawer-section chat-drawer-danger">
|
||||
<summary class="chat-drawer-row chat-drawer-row--compact">
|
||||
type="button">Download workspace archive
|
||||
</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,.tar.gz,application/gzip"/>
|
||||
</div>
|
||||
</details>
|
||||
<details class="chat-drawer-section chat-drawer-danger">
|
||||
<summary class="chat-drawer-row chat-drawer-row--compact">
|
||||
<span class="chat-drawer-row-label">
|
||||
<strong>Delete chat content</strong>
|
||||
</span>
|
||||
<span class="chat-drawer-row-hint">Show</span>
|
||||
</summary>
|
||||
<div class="chat-drawer-action-list chat-drawer-disclosure-panel">
|
||||
<button id="clearChat"
|
||||
<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>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
type="button" data-chat-action="clear">Clear chat
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<p id="chatActivityEyebrow" class="eyebrow">Please wait</p>
|
||||
<h3 id="chatActivityTitle">Preparing chat</h3>
|
||||
<p id="chatActivityDetail" class="subtitle">The assistant is still working.</p>
|
||||
<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>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<p id="chatActivityEyebrow" class="eyebrow">Please wait</p>
|
||||
<h3 id="chatActivityTitle">Preparing chat</h3>
|
||||
<p id="chatActivityDetail" class="subtitle">The assistant is still working.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="messages-wrap">
|
||||
<div class="history-controls">
|
||||
<button id="loadOlderMessages" class="secondary-action" type="button" hidden>Load older messages</button>
|
||||
<button id="loadLatestMessages" class="secondary-action" type="button" hidden>Back to latest messages</button>
|
||||
</div>
|
||||
<button id="conversationSearchToggle" class="conversation-search-toggle secondary-action" type="button" aria-controls="conversationSearchBar" aria-expanded="false">Find in this chat</button>
|
||||
<div id="conversationSearchBar" class="conversation-search" role="search" aria-label="Search this conversation">
|
||||
<label for="conversationSearch">Find in this chat</label>
|
||||
<input id="conversationSearch" type="search" placeholder="Search messages" autocomplete="off" maxlength="200">
|
||||
<span id="conversationSearchCount" role="status" aria-live="polite">0 matches</span>
|
||||
<button id="conversationSearchPrevious" class="secondary-action" type="button" aria-label="Previous match" disabled>Previous</button>
|
||||
<button id="conversationSearchNext" class="secondary-action" type="button" aria-label="Next match" disabled>Next</button>
|
||||
</div>
|
||||
<div id="messages" class="messages-panel messages-panel-focus" role="log" aria-live="off"></div>
|
||||
<div id="replyAnnouncement" class="visually-hidden" role="status" aria-live="polite" aria-atomic="true"></div>
|
||||
<button id="jumpToLatest" class="jump-to-latest secondary-action hidden" type="button">Jump to latest</button>
|
||||
<div class="messages-wrap">
|
||||
<div class="history-controls">
|
||||
<button id="loadOlderMessages" class="secondary-action" type="button" hidden>Load older
|
||||
messages
|
||||
</button>
|
||||
<button id="loadLatestMessages" class="secondary-action" type="button" hidden>Back to latest
|
||||
messages
|
||||
</button>
|
||||
</div>
|
||||
<button id="conversationSearchToggle" class="conversation-search-toggle secondary-action"
|
||||
type="button" aria-controls="conversationSearchBar" aria-expanded="false">Find in this chat
|
||||
</button>
|
||||
<div id="conversationSearchBar" class="conversation-search" role="search"
|
||||
aria-label="Search this conversation">
|
||||
<label for="conversationSearch">Find in this chat</label>
|
||||
<input id="conversationSearch" type="search" placeholder="Search messages" autocomplete="off"
|
||||
maxlength="200">
|
||||
<span id="conversationSearchCount" role="status" aria-live="polite">0 matches</span>
|
||||
<button id="conversationSearchPrevious" class="secondary-action" type="button"
|
||||
aria-label="Previous match" disabled>Previous
|
||||
</button>
|
||||
<button id="conversationSearchNext" class="secondary-action" type="button"
|
||||
aria-label="Next match" disabled>Next
|
||||
</button>
|
||||
</div>
|
||||
<div id="messages" class="messages-panel messages-panel-focus" role="log" aria-live="off"></div>
|
||||
<div id="replyAnnouncement" class="visually-hidden" role="status" aria-live="polite"
|
||||
aria-atomic="true"></div>
|
||||
<button id="jumpToLatest" class="jump-to-latest secondary-action hidden" type="button">Jump to
|
||||
latest
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="input-shell input-shell-focus">
|
||||
<div class="input-meta">
|
||||
<p id="composerHint" class="composer-hint">Enter to send · Shift+Enter for new line</p>
|
||||
</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"
|
||||
<div class="input-shell input-shell-focus">
|
||||
<div class="input-meta">
|
||||
<p id="composerHint" class="composer-hint">Enter to send · Shift+Enter for new line</p>
|
||||
</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><span class="composer-tool-label">Attach</span>
|
||||
</button>
|
||||
<button id="webSearch" class="icon-button" type="button" aria-label="Web search allowed. The assistant searches when useful. Click to turn off."
|
||||
title="Web search allowed. The assistant searches when useful. Click to turn off." aria-pressed="true">
|
||||
<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><span class="composer-tool-label">Web search</span>
|
||||
</button>
|
||||
<button id="reasoning" class="icon-button" type="button" aria-label="Enable reasoning"
|
||||
<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>
|
||||
<span class="composer-tool-label">Attach</span>
|
||||
</button>
|
||||
<button id="webSearch" class="icon-button" type="button"
|
||||
aria-label="Web search allowed. The assistant searches when useful. Click to turn off."
|
||||
title="Web search allowed. The assistant searches when useful. Click to turn off."
|
||||
aria-pressed="true">
|
||||
<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>
|
||||
<span class="composer-tool-label">Web search</span>
|
||||
</button>
|
||||
<button id="reasoning" class="icon-button" type="button" aria-label="Enable reasoning"
|
||||
title="Enable reasoning" aria-pressed="false" disabled hidden>
|
||||
<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><span class="composer-tool-label">Reasoning</span>
|
||||
</button>
|
||||
<label class="visually-hidden" for="msgInput">Message</label>
|
||||
<textarea id="msgInput" rows="1" placeholder="Type your message..."></textarea>
|
||||
<button id="send" type="button" aria-label="Send message"></button>
|
||||
<button id="stopGeneration" class="secondary-action" type="button" aria-label="Stop generating reply" hidden>Stop</button>
|
||||
</div>
|
||||
<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>
|
||||
<span class="composer-tool-label">Reasoning</span>
|
||||
</button>
|
||||
<label class="visually-hidden" for="msgInput">Message</label>
|
||||
<textarea id="msgInput" rows="1" placeholder="Type your message..."></textarea>
|
||||
<button id="send" type="button" aria-label="Send message"></button>
|
||||
<button id="stopGeneration" class="secondary-action" type="button"
|
||||
aria-label="Stop generating reply" hidden>Stop
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="personaModal" class="modal-overlay side-panel-overlay hidden">
|
||||
<div class="modal-card persona-modal-card side-panel-card" role="dialog" aria-modal="true" aria-labelledby="personaFormTitle" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p class="eyebrow">Persona editor</p>
|
||||
<h3 id="personaFormTitle">Create persona</h3>
|
||||
</div>
|
||||
<button id="personaClose" type="button" class="icon-button">Close</button>
|
||||
<div id="personaModal" class="modal-overlay side-panel-overlay hidden">
|
||||
<div class="modal-card persona-modal-card side-panel-card" role="dialog" aria-modal="true"
|
||||
aria-labelledby="personaFormTitle" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p class="eyebrow">Persona editor</p>
|
||||
<h3 id="personaFormTitle">Create persona</h3>
|
||||
</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>
|
||||
</div>
|
||||
<p id="personaFormNotice" class="status hidden"></p>
|
||||
<div class="persona-form-grid">
|
||||
<label class="field field-wide">
|
||||
<span>Persona for</span>
|
||||
<select id="personaType">
|
||||
<option value="assistant">AI Character</option>
|
||||
<option value="user">Your persona</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Persona name</span>
|
||||
<input id="personaName" placeholder="Persona name" required />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Persona pronouns</span>
|
||||
<input id="personaPronouns" placeholder="e.g. she/her, they/them" />
|
||||
</label>
|
||||
<label class="field field-wide">
|
||||
<span>Appearance</span>
|
||||
<textarea id="personaAppearance" rows="3" placeholder="Describe their look"></textarea>
|
||||
</label>
|
||||
<label class="field field-wide">
|
||||
<span>Background</span>
|
||||
<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>
|
||||
</label>
|
||||
<label id="personaExamplesField" class="field field-wide hidden">
|
||||
<span>Example dialogues</span>
|
||||
<div class="dialogue-format-card" aria-hidden="true">
|
||||
<p class="dialogue-format-label">Format</p>
|
||||
<pre class="dialogue-format-example">{{character}}: Hello, my name is marc
|
||||
<button id="personaClose" type="button" class="icon-button">Close</button>
|
||||
</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>
|
||||
</div>
|
||||
<p id="personaFormNotice" class="status hidden"></p>
|
||||
<div class="persona-form-grid">
|
||||
<label class="field field-wide">
|
||||
<span>Persona for</span>
|
||||
<select id="personaType">
|
||||
<option value="assistant">AI Character</option>
|
||||
<option value="user">Your persona</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Persona name</span>
|
||||
<input id="personaName" placeholder="Persona name" required/>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Persona pronouns</span>
|
||||
<input id="personaPronouns" placeholder="e.g. she/her, they/them"/>
|
||||
</label>
|
||||
<label class="field field-wide">
|
||||
<span>Appearance</span>
|
||||
<textarea id="personaAppearance" rows="3" placeholder="Describe their look"></textarea>
|
||||
</label>
|
||||
<label class="field field-wide">
|
||||
<span>Background</span>
|
||||
<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>
|
||||
</label>
|
||||
<label id="personaExamplesField" class="field field-wide hidden">
|
||||
<span>Example dialogues</span>
|
||||
<div class="dialogue-format-card" aria-hidden="true">
|
||||
<p class="dialogue-format-label">Format</p>
|
||||
<pre class="dialogue-format-example">{{character}}: Hello, my name is marc
|
||||
{{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>
|
||||
</label>
|
||||
</div>
|
||||
<div class="persona-actions">
|
||||
<button id="personaSave" type="submit" class="primary-action">Save persona</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="roleplayStarterModal" class="modal-overlay side-panel-overlay hidden">
|
||||
<div class="modal-card roleplay-starter-card side-panel-card" role="dialog" aria-modal="true" aria-labelledby="roleplayStarterTitle" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p class="eyebrow">Start roleplay</p>
|
||||
<h3 id="roleplayStarterTitle">Choose your roleplay</h3>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<p id="roleplayStarterNotice" class="status hidden"></p>
|
||||
<div class="roleplay-starter-actions">
|
||||
<a id="roleplayNewPersona" class="secondary-action" href="/app/personas">Browse Personalities</a>
|
||||
</div>
|
||||
<div id="roleplayStarterStepOne" class="persona-form-grid">
|
||||
<label class="field field-wide">
|
||||
<span>1. Character</span>
|
||||
<select id="roleplayCharacterSelect"></select>
|
||||
</label>
|
||||
<label class="field field-wide">
|
||||
<span>2. You in the scene</span>
|
||||
<select id="roleplayUserPersonaSelect"></select>
|
||||
</div>
|
||||
<textarea id="personaExampleDialogues" rows="5"
|
||||
placeholder="Write example exchanges that define the AI's tone, phrasing, pacing, and personality."></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<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>
|
||||
</label>
|
||||
<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 class="persona-actions">
|
||||
<button id="personaSave" type="submit" class="primary-action">Save persona</button>
|
||||
</div>
|
||||
<div class="popup-actions">
|
||||
<button id="roleplayStarterCancel" type="button" class="secondary-action">Cancel</button>
|
||||
<button id="roleplayStarterBack" type="button" class="secondary-action hidden">Back</button>
|
||||
<button id="roleplayStarterConfirm" type="button">Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="roleplayStarterModal" class="modal-overlay side-panel-overlay hidden">
|
||||
<div class="modal-card roleplay-starter-card side-panel-card" role="dialog" aria-modal="true"
|
||||
aria-labelledby="roleplayStarterTitle" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p class="eyebrow">Start roleplay</p>
|
||||
<h3 id="roleplayStarterTitle">Choose your roleplay</h3>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<p id="roleplayStarterNotice" class="status hidden"></p>
|
||||
<div class="roleplay-starter-actions">
|
||||
<a id="roleplayNewPersona" class="secondary-action" href="/app/personas">Browse Personalities</a>
|
||||
</div>
|
||||
<div id="roleplayStarterStepOne" class="persona-form-grid">
|
||||
<label class="field field-wide">
|
||||
<span>1. Character</span>
|
||||
<select id="roleplayCharacterSelect"></select>
|
||||
</label>
|
||||
<label class="field field-wide">
|
||||
<span>2. You in the scene</span>
|
||||
<select id="roleplayUserPersonaSelect"></select>
|
||||
</label>
|
||||
</div>
|
||||
<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>
|
||||
</label>
|
||||
<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">
|
||||
<button id="roleplayStarterCancel" type="button" class="secondary-action">Cancel</button>
|
||||
<button id="roleplayStarterBack" type="button" class="secondary-action hidden">Back</button>
|
||||
<button id="roleplayStarterConfirm" type="button">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</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-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p id="popupEyebrow" class="eyebrow">Action</p>
|
||||
<h3 id="popupTitle">Confirm action</h3>
|
||||
</div>
|
||||
<button id="popupClose" type="button" class="icon-button">Close</button>
|
||||
</div>
|
||||
<div class="popup-content">
|
||||
<p id="popupDescription" class="subtitle">Please confirm this action.</p>
|
||||
<label id="popupField" class="field hidden">
|
||||
<span id="popupInputLabel">Value</span>
|
||||
<input id="popupInput" type="text" autocomplete="off" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="popup-actions">
|
||||
<button id="popupCancel" type="button" class="secondary-action">Cancel</button>
|
||||
<button id="popupConfirm" type="button">Confirm</button>
|
||||
<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-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p id="popupEyebrow" class="eyebrow">Action</p>
|
||||
<h3 id="popupTitle">Confirm action</h3>
|
||||
</div>
|
||||
<button id="popupClose" type="button" class="icon-button">Close</button>
|
||||
</div>
|
||||
<div class="popup-content">
|
||||
<p id="popupDescription" class="subtitle">Please confirm this action.</p>
|
||||
<label id="popupField" class="field hidden">
|
||||
<span id="popupInputLabel">Value</span>
|
||||
<input id="popupInput" type="text" autocomplete="off"/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="popup-actions">
|
||||
<button id="popupCancel" type="button" class="secondary-action">Cancel</button>
|
||||
<button id="popupConfirm" type="button">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="onboardingModal" class="modal-overlay hidden">
|
||||
<div class="modal-card onboarding-card" role="dialog" aria-modal="true" aria-labelledby="onboardingTitle" aria-describedby="onboardingSubtitle" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p class="eyebrow">Get started</p>
|
||||
<h3 id="onboardingTitle">Where shall we start?</h3>
|
||||
</div>
|
||||
<button id="onboardingSkip" type="button" class="icon-button">Skip</button>
|
||||
</div>
|
||||
<p id="onboardingSubtitle" class="subtitle">Ask a question, explore an idea, or step into a story.</p>
|
||||
<div id="onboardingChoices" class="onboarding-choices"></div>
|
||||
<div class="popup-actions">
|
||||
<button id="onboardingBack" type="button" class="secondary-action hidden">Back</button>
|
||||
<button id="onboardingContinue" type="button">Continue</button>
|
||||
<div id="onboardingModal" class="modal-overlay hidden">
|
||||
<div class="modal-card onboarding-card" role="dialog" aria-modal="true" aria-labelledby="onboardingTitle"
|
||||
aria-describedby="onboardingSubtitle" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<div class="persona-modal-heading">
|
||||
<p class="eyebrow">Get started</p>
|
||||
<h3 id="onboardingTitle">Where shall we start?</h3>
|
||||
</div>
|
||||
<button id="onboardingSkip" type="button" class="icon-button">Skip</button>
|
||||
</div>
|
||||
<p id="onboardingSubtitle" class="subtitle">Ask a question, explore an idea, or step into a story.</p>
|
||||
<div id="onboardingChoices" class="onboarding-choices"></div>
|
||||
<div class="popup-actions">
|
||||
<button id="onboardingBack" type="button" class="secondary-action hidden">Back</button>
|
||||
<button id="onboardingContinue" type="button">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/vendor/marked.min.js" defer></script>
|
||||
<script src="/vendor/dompurify.js" defer></script>
|
||||
<script type="module" src="/js/script.js?v=20261012-chat-usability"></script>
|
||||
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
|
||||
<script src="/vendor/marked.min.js" defer></script>
|
||||
<script src="/vendor/dompurify.js" defer></script>
|
||||
<script type="module" src="/js/script.js?v=20261012-chat-usability"></script>
|
||||
<script type="module" src="/js/workspaceNavigation.js?v=20260909-account-controls"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user