clay-server 5.1.0-beta.1 → 5.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/README.md +4 -0
- package/LICENSES/dicebear-avatars.md +16 -0
- package/bin/cli.js +9 -91
- package/lib/builtin-mates.js +7 -7
- package/lib/daemon-project-access.js +7 -1
- package/lib/daemon-settings.js +11 -0
- package/lib/daemon.js +10 -37
- package/lib/dicebear-avatar.js +62 -0
- package/lib/knowledge-db-service.js +175 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-document-index.js +49 -0
- package/lib/knowledge-draft-store.js +56 -0
- package/lib/knowledge-file-store.js +199 -0
- package/lib/knowledge-markdown.js +128 -0
- package/lib/linear-store.js +35 -8
- package/lib/mate-identity-file.js +39 -0
- package/lib/mate-knowledge-mcp-server.js +29 -6
- package/lib/mate-knowledge-service.js +23 -2
- package/lib/mate-knowledge-sync.js +37 -22
- package/lib/mate-ready-creation.js +2 -1
- package/lib/mate-workspace-color.js +4 -0
- package/lib/mates-identity.js +4 -4
- package/lib/mates-prompts.js +4 -3
- package/lib/mates.js +30 -11
- package/lib/os-users.js +5 -16
- package/lib/project-connection.js +36 -9
- package/lib/project-debate-proposal.js +6 -0
- package/lib/project-debate-workspace.js +43 -0
- package/lib/project-debate.js +4 -2
- package/lib/project-filesystem.js +21 -39
- package/lib/project-knowledge.js +315 -136
- package/lib/project-mate-creation-proposal.js +1 -0
- package/lib/project-mate-interaction.js +7 -6
- package/lib/project-server-settings.js +31 -0
- package/lib/project-sessions.js +92 -182
- package/lib/project-user-knowledge.js +60 -0
- package/lib/project.js +62 -21
- package/lib/public/app.js +79 -71
- package/lib/public/clay-studio-wordmark-bold.svg +20 -0
- package/lib/public/css/clay-workspace.css +105 -0
- package/lib/public/css/debates-workbench.css +47 -0
- package/lib/public/css/home-debate-live.css +26 -0
- package/lib/public/css/home-hub.css +3 -1
- package/lib/public/css/home-mate-model-picker.css +15 -131
- package/lib/public/css/home-mate-settings.css +168 -16
- package/lib/public/css/icon-strip.css +249 -32
- package/lib/public/css/input.css +3 -1
- package/lib/public/css/mate-conversation.css +176 -0
- package/lib/public/css/mate-knowledge-workbench.css +118 -0
- package/lib/public/css/mate-workspace-color.css +16 -0
- package/lib/public/css/mates.css +28 -28
- package/lib/public/css/menus.css +3 -0
- package/lib/public/css/messages.css +1 -114
- package/lib/public/css/mobile-nav.css +16 -16
- package/lib/public/css/overlays.css +15 -0
- package/lib/public/css/project-logs.css +7 -0
- package/lib/public/css/project-mate-creation.css +105 -0
- package/lib/public/css/project-settings-modal.css +56 -0
- package/lib/public/css/right-workbench.css +3 -3
- package/lib/public/css/server-settings-system.css +125 -0
- package/lib/public/css/session-folders.css +80 -21
- package/lib/public/css/session-github.css +25 -16
- package/lib/public/css/settings-ui.css +247 -22
- package/lib/public/css/shared-browser.css +7 -3
- package/lib/public/css/sidebar.css +183 -107
- package/lib/public/css/title-bar.css +156 -1
- package/lib/public/css/worker-delegation.css +47 -0
- package/lib/public/css/worker-proposal.css +72 -150
- package/lib/public/css/you.css +74 -0
- package/lib/public/index.html +206 -548
- package/lib/public/modules/account-island.js +232 -0
- package/lib/public/modules/admin-settings-email.js +197 -0
- package/lib/public/modules/admin-settings-invitations.js +183 -0
- package/lib/public/modules/admin-settings-projects.js +198 -0
- package/lib/public/modules/admin-settings-ui.js +100 -0
- package/lib/public/modules/admin-settings-users.js +331 -0
- package/lib/public/modules/admin.js +19 -949
- package/lib/public/modules/app-cursors.js +19 -43
- package/lib/public/modules/app-debate-ui.js +4 -3
- package/lib/public/modules/app-dm.js +0 -2
- package/lib/public/modules/app-header.js +106 -0
- package/lib/public/modules/app-message-router.js +12 -3
- package/lib/public/modules/app-messages.js +47 -61
- package/lib/public/modules/app-notifications.js +4 -7
- package/lib/public/modules/app-projects.js +5 -38
- package/lib/public/modules/app-rate-limit.js +70 -12
- package/lib/public/modules/app-rendering.js +12 -13
- package/lib/public/modules/avatar.js +30 -9
- package/lib/public/modules/chat-render-runtime.js +16 -4
- package/lib/public/modules/command-palette.js +2 -15
- package/lib/public/modules/context-sources.js +27 -18
- package/lib/public/modules/cursor-sharing-preference.js +3 -3
- package/lib/public/modules/debate-controls.js +299 -0
- package/lib/public/modules/debate.js +2 -0
- package/lib/public/modules/debates-workbench.js +339 -0
- package/lib/public/modules/default-ai.js +88 -65
- package/lib/public/modules/header-tui-font.js +10 -20
- package/lib/public/modules/home-debate-controls.js +18 -289
- package/lib/public/modules/home-mate-avatar-editor.js +228 -0
- package/lib/public/modules/home-mate-color-picker.js +66 -0
- package/lib/public/modules/home-mate-creation.js +1 -1
- package/lib/public/modules/home-mate-instructions.js +90 -0
- package/lib/public/modules/home-mate-model-picker.js +80 -271
- package/lib/public/modules/home-mate-profile-editor.js +72 -0
- package/lib/public/modules/home-mate-settings.js +118 -125
- package/lib/public/modules/home-shell.js +3 -0
- package/lib/public/modules/home-surface-boot.js +1 -9
- package/lib/public/modules/icon-strip-tooltip.js +95 -0
- package/lib/public/modules/knowledge-db-edit-state.js +35 -0
- package/lib/public/modules/knowledge-db-workbench.js +234 -0
- package/lib/public/modules/knowledge-history-state.js +55 -0
- package/lib/public/modules/knowledge-markdown-editor.js +57 -0
- package/lib/public/modules/knowledge-preview-links.js +70 -0
- package/lib/public/modules/knowledge-workbench-shell.js +21 -0
- package/lib/public/modules/linear-settings.js +3 -2
- package/lib/public/modules/markdown.js +5 -6
- package/lib/public/modules/mate-knowledge-workbench-state.js +174 -0
- package/lib/public/modules/mate-knowledge-workbench.js +500 -0
- package/lib/public/modules/mate-knowledge.js +11 -3
- package/lib/public/modules/mate-management.js +54 -10
- package/lib/public/modules/mate-sidebar.js +11 -1
- package/lib/public/modules/mate-workspace-color.js +33 -0
- package/lib/public/modules/mention.js +5 -13
- package/lib/public/modules/notification-settings.js +81 -0
- package/lib/public/modules/notifications.js +44 -28
- package/lib/public/modules/personal-settings-dialog.js +51 -0
- package/lib/public/modules/profile.js +66 -24
- package/lib/public/modules/project-chat-identity.js +79 -0
- package/lib/public/modules/project-debate-planning-workspace.js +250 -0
- package/lib/public/modules/project-debate-workspace.js +185 -0
- package/lib/public/modules/project-header-identity.js +127 -0
- package/lib/public/modules/project-logs-render.js +9 -4
- package/lib/public/modules/project-mate-creation-workspace.js +414 -0
- package/lib/public/modules/project-mate-navigation.js +238 -0
- package/lib/public/modules/project-session-navigation.js +29 -0
- package/lib/public/modules/project-settings-environment.js +106 -0
- package/lib/public/modules/project-settings.js +72 -628
- package/lib/public/modules/project-switcher.js +6 -8
- package/lib/public/modules/projects-hub-view.js +5 -6
- package/lib/public/modules/projects-hub.js +2 -6
- package/lib/public/modules/qrcode.js +27 -8
- package/lib/public/modules/search-clay-chat.js +8 -11
- package/lib/public/modules/server-settings-controls.js +316 -0
- package/lib/public/modules/server-settings-environment.js +194 -0
- package/lib/public/modules/server-settings-navigation.js +39 -0
- package/lib/public/modules/server-settings-state.js +54 -0
- package/lib/public/modules/server-settings.js +116 -788
- package/lib/public/modules/server-storage-settings.js +84 -0
- package/lib/public/modules/session-activity-tooltip.js +12 -0
- package/lib/public/modules/session-create-form.js +86 -14
- package/lib/public/modules/session-create.js +50 -7
- package/lib/public/modules/session-github.js +2 -1
- package/lib/public/modules/session-list-visibility.js +41 -0
- package/lib/public/modules/session-presence-ui.js +3 -0
- package/lib/public/modules/session-title-marquee.js +51 -0
- package/lib/public/modules/settings-env-values.js +34 -0
- package/lib/public/modules/settings-select.js +121 -0
- package/lib/public/modules/shared-browser.js +22 -12
- package/lib/public/modules/sidebar-mates.js +3 -50
- package/lib/public/modules/sidebar-mobile.js +63 -60
- package/lib/public/modules/sidebar-projects.js +84 -97
- package/lib/public/modules/sidebar-session-folders-ui.js +2 -2
- package/lib/public/modules/sidebar-session-hierarchy.js +149 -70
- package/lib/public/modules/sidebar-sessions.js +49 -50
- package/lib/public/modules/sidebar.js +11 -13
- package/lib/public/modules/stt-picker.js +14 -5
- package/lib/public/modules/terminal-prefs.js +22 -0
- package/lib/public/modules/theme.js +88 -52
- package/lib/public/modules/thinking-lifecycle.js +7 -6
- package/lib/public/modules/tool-palette-order.js +4 -3
- package/lib/public/modules/tool-palette.js +13 -3
- package/lib/public/modules/tools.js +22 -22
- package/lib/public/modules/user-settings-account.js +318 -0
- package/lib/public/modules/user-settings-appearance.js +55 -0
- package/lib/public/modules/user-settings-editors.js +24 -0
- package/lib/public/modules/user-settings-integrations.js +104 -0
- package/lib/public/modules/user-settings-layout.js +80 -0
- package/lib/public/modules/user-settings-preferences.js +48 -0
- package/lib/public/modules/user-settings-profile.js +53 -0
- package/lib/public/modules/user-settings-themes.js +38 -0
- package/lib/public/modules/user-settings-voice.js +75 -0
- package/lib/public/modules/user-settings.js +53 -480
- package/lib/public/modules/worker-proposal.js +42 -8
- package/lib/public/modules/worker-runtime-picker.js +115 -0
- package/lib/public/modules/you-activity.js +121 -0
- package/lib/public/modules/you.js +251 -0
- package/lib/public/style.css +20 -8
- package/lib/public/vendor/codemirror/LICENSE +21 -0
- package/lib/public/vendor/codemirror/codemirror.css +344 -0
- package/lib/public/vendor/codemirror/codemirror.js +9884 -0
- package/lib/public/vendor/codemirror/continuelist.js +101 -0
- package/lib/public/vendor/codemirror/markdown.js +886 -0
- package/lib/public/vendor/codemirror/show-hint.css +37 -0
- package/lib/public/vendor/codemirror/show-hint.js +523 -0
- package/lib/sdk-bridge.js +15 -3
- package/lib/sdk-message-processor.js +16 -4
- package/lib/sdk-quota-snapshot.js +37 -0
- package/lib/server-admin.js +11 -0
- package/lib/server-appearance-settings.js +71 -0
- package/lib/server-home-chat-events.js +1 -1
- package/lib/server-home-chat.js +25 -3
- package/lib/server-home-debate-planning.js +4 -1
- package/lib/server-home-debates.js +2 -0
- package/lib/server-home-mate-creation.js +4 -1
- package/lib/server-linear.js +14 -8
- package/lib/server-mate-instructions.js +44 -0
- package/lib/server-mates.js +14 -4
- package/lib/server-notification-settings.js +67 -0
- package/lib/server-palette.js +1 -1
- package/lib/server-settings.js +69 -1
- package/lib/server-storage.js +57 -0
- package/lib/server-user-knowledge.js +119 -0
- package/lib/server.js +31 -3
- package/lib/session-visibility.js +7 -2
- package/lib/sessions.js +35 -10
- package/lib/startup-projects.js +40 -0
- package/lib/storage-native-walk.js +85 -0
- package/lib/storage-usage.js +144 -0
- package/lib/user-knowledge-service.js +167 -0
- package/lib/users-permissions.js +7 -0
- package/lib/users-preferences.js +0 -74
- package/lib/users-project-mate-filter-preferences.js +39 -0
- package/lib/users.js +6 -4
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +52 -4
- package/lib/yoke/acp-process-manager.js +2 -2
- package/lib/yoke/adapters/codex.js +12 -52
- package/lib/yoke/codex-app-server.js +2 -2
- package/lib/yoke/codex-model-catalog.js +52 -0
- package/lib/yoke/codex-rate-limits.js +26 -0
- package/package.json +12 -3
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
import { store } from './store.js';
|
|
17
17
|
import { refreshIcons } from './icons.js';
|
|
18
18
|
import { getCachedProjects, switchProject } from './app-projects.js';
|
|
19
|
-
import {
|
|
20
|
-
import { openHomeChat } from './home-mate-chat.js';
|
|
19
|
+
import { openMateWorkspace, mateForWorkspace } from './project-mate-navigation.js';
|
|
21
20
|
import { mateAvatarUrl } from './avatar.js';
|
|
22
21
|
import { parseEmojis } from './markdown.js';
|
|
23
22
|
import { isExternalWorktree, externalWorktreeTooltip, appendExternalWorktreeBadge } from './worktree-location.js';
|
|
@@ -34,7 +33,6 @@ var _entries = [];
|
|
|
34
33
|
|
|
35
34
|
export function openSwitcherForMode(mode) {
|
|
36
35
|
if (mode !== 'project' && mode !== 'mate') return;
|
|
37
|
-
if (mode === 'mate' && !isHomeHubVisible()) return;
|
|
38
36
|
if (_open) closeSwitcher(false);
|
|
39
37
|
openSwitcher(mode);
|
|
40
38
|
}
|
|
@@ -145,7 +143,6 @@ function handleKeyDown(e) {
|
|
|
145
143
|
// same shortcut cycles.
|
|
146
144
|
if (isMod && (k === 'e' || k === 'm')) {
|
|
147
145
|
var requestedMode = k === 'e' ? 'project' : 'mate';
|
|
148
|
-
if (requestedMode === 'mate' && !isHomeHubVisible()) return;
|
|
149
146
|
e.preventDefault();
|
|
150
147
|
e.stopPropagation();
|
|
151
148
|
if (!_open) {
|
|
@@ -246,7 +243,8 @@ function pickInitialIndex() {
|
|
|
246
243
|
|
|
247
244
|
function currentActiveKey() {
|
|
248
245
|
if (_mode === 'mate') {
|
|
249
|
-
|
|
246
|
+
var mate = mateForWorkspace(store.snap());
|
|
247
|
+
return mate ? mate.id : null;
|
|
250
248
|
}
|
|
251
249
|
return store.get('currentSlug');
|
|
252
250
|
}
|
|
@@ -277,7 +275,8 @@ function buildProjectEntries() {
|
|
|
277
275
|
|
|
278
276
|
function buildMateEntries() {
|
|
279
277
|
var mates = store.get('cachedMatesList') || [];
|
|
280
|
-
var
|
|
278
|
+
var current = mateForWorkspace(store.snap());
|
|
279
|
+
var currentMateId = current ? current.id : null;
|
|
281
280
|
// Natural list order; MRU is only for choosing the initial highlight.
|
|
282
281
|
return mates.map(function (m) {
|
|
283
282
|
return {
|
|
@@ -302,8 +301,7 @@ function closeSwitcher(commit) {
|
|
|
302
301
|
if (!entry || entry.disabled) return;
|
|
303
302
|
if (_mode === 'mate') {
|
|
304
303
|
if (entry.key !== currentActiveKey()) {
|
|
305
|
-
|
|
306
|
-
openHomeChat(entry.key);
|
|
304
|
+
openMateWorkspace(entry.key);
|
|
307
305
|
}
|
|
308
306
|
} else {
|
|
309
307
|
if (entry.key !== store.get('currentSlug')) switchProject(entry.key);
|
|
@@ -18,12 +18,12 @@ function setupAction(mode, icon, title, text, disabled) {
|
|
|
18
18
|
|
|
19
19
|
export function projectsHubContent(state) {
|
|
20
20
|
var projects = ordinaryProjects(state.projectsHubList);
|
|
21
|
-
var ready = state.projectListLoaded
|
|
21
|
+
var ready = state.projectListLoaded;
|
|
22
22
|
var welcome = ready && (state.projectsShowGuide || (!projects.length && (state.onboardingStep || 'welcome') === 'welcome'));
|
|
23
23
|
var allowed = state.projectsAccessLoaded && (!state.permissions || state.permissions.createProject === true);
|
|
24
24
|
var available = allowed && state.connected && !state.pendingHomeProjectSlug;
|
|
25
|
-
var html = '<header class="projects-header"><a class="projects-brand" href="/
|
|
26
|
-
'<nav aria-label="Workspace navigation">' + button('
|
|
25
|
+
var html = '<header class="projects-header"><a class="projects-brand" href="/projects"><img src="/clay-studio-symbol.png" alt="">Clay Studio</a>' +
|
|
26
|
+
'<nav aria-label="Workspace navigation">' + button('settings', 'Settings', 'projects-button-quiet') + '</nav></header>';
|
|
27
27
|
html += '<main class="projects-main" aria-labelledby="projects-title">';
|
|
28
28
|
if (!ready) return html + '<div class="projects-loading" role="status"><h1 id="projects-title" tabindex="-1">Your projects</h1><p>Loading your workspace…</p></div></main>';
|
|
29
29
|
if (welcome) {
|
|
@@ -31,9 +31,8 @@ export function projectsHubContent(state) {
|
|
|
31
31
|
'<h1 id="projects-title" tabindex="-1">A place for<br>your next idea.</h1>' +
|
|
32
32
|
'<p class="projects-lead">Bring a project. Describe what you want to build.<br>Your AI Driver helps you take it from there.</p>' +
|
|
33
33
|
'<div class="projects-intro-grid"><div><span>01 / PROJECTS</span><h2>Work on something real.</h2><p>Open a folder, start fresh, or clone a repository. Your conversations and tools stay with the project.</p></div>' +
|
|
34
|
-
'<div><span>02 /
|
|
35
|
-
'<div class="projects-welcome-actions">' + button('start', projects.length ? 'Explore your projects ' + iconHtml('arrow-right') : 'Set up your first project ' + iconHtml('arrow-right'), 'projects-button-primary') +
|
|
36
|
-
button('skip', 'Explore Home first', 'projects-button-quiet') + '</div>' +
|
|
34
|
+
'<div><span>02 / MATES</span><h2>Bring in the right perspective.</h2><p>Your Mates live beside projects and open their conversations in the same focused workspace.</p></div></div>' +
|
|
35
|
+
'<div class="projects-welcome-actions">' + button('start', projects.length ? 'Explore your projects ' + iconHtml('arrow-right') : 'Set up your first project ' + iconHtml('arrow-right'), 'projects-button-primary') + '</div>' +
|
|
37
36
|
'<p class="projects-footnote">You can revisit this guide from Projects at any time.</p></section></main>';
|
|
38
37
|
}
|
|
39
38
|
html += '<div class="projects-heading"><div><div class="projects-eyebrow">YOUR WORKSPACE</div><h1 id="projects-title" tabindex="-1">' + (projects.length ? 'Projects' : 'Start with a project.') + '</h1>' +
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
2
|
import { getWs } from './ws-ref.js';
|
|
3
|
-
import { hideHomeHub, showHomeHub } from './app-home-hub.js';
|
|
4
3
|
import { openAddProjectModal, switchProject } from './app-projects.js';
|
|
5
4
|
import { openUserSettings } from './user-settings.js';
|
|
6
5
|
import { rememberHomePrimarySurface, updateHomeSurfacePreference } from './home-surface.js';
|
|
@@ -37,7 +36,6 @@ function focusHeading() {
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
export function showProjectsHub(fromHistory) {
|
|
40
|
-
hideHomeHub();
|
|
41
39
|
store.set({ projectsHubVisible: true, pendingHomeProjectSlug: null });
|
|
42
40
|
document.body.classList.add('projects-active');
|
|
43
41
|
root().classList.remove('hidden');
|
|
@@ -67,14 +65,12 @@ function handleClick(event) {
|
|
|
67
65
|
if (!button || button.disabled) return;
|
|
68
66
|
event.preventDefault();
|
|
69
67
|
var action = button.dataset.projectsAction;
|
|
70
|
-
if (action === 'home') { showHomeHub(); return; }
|
|
71
68
|
if (action === 'settings') { openUserSettings(); return; }
|
|
72
69
|
if (action === 'guide') { store.set({ projectsShowGuide: true }); render(); focusHeading(); return; }
|
|
73
|
-
if (action === 'start'
|
|
70
|
+
if (action === 'start') {
|
|
74
71
|
store.set({ projectsShowGuide: false });
|
|
75
72
|
updateHomeSurfacePreference({ onboardingStep: 'project' });
|
|
76
|
-
|
|
77
|
-
else { render(); focusHeading(); }
|
|
73
|
+
render(); focusHeading();
|
|
78
74
|
return;
|
|
79
75
|
}
|
|
80
76
|
var ws = getWs();
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import { copyToClipboard } from './utils.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
var activeShareUrl = null;
|
|
4
|
+
var activeShareTitle = null;
|
|
5
|
+
|
|
6
|
+
export function getShareUrl(projectSlug) {
|
|
7
|
+
var locationUrl = new URL(window.location.href);
|
|
8
|
+
if (projectSlug) {
|
|
9
|
+
var projectIndex = locationUrl.pathname.indexOf("/p/");
|
|
10
|
+
var prefix = projectIndex === -1 ? "" : locationUrl.pathname.substring(0, projectIndex);
|
|
11
|
+
locationUrl.pathname = prefix + "/p/" + encodeURIComponent(projectSlug) + "/";
|
|
12
|
+
locationUrl.search = "";
|
|
13
|
+
locationUrl.hash = "";
|
|
14
|
+
}
|
|
15
|
+
var url = locationUrl.href;
|
|
5
16
|
var h = window.location.hostname;
|
|
6
17
|
if ((h === "localhost" || h === "127.0.0.1") && window.__lanHost) {
|
|
7
18
|
url = url.replace(h + ":" + window.location.port, window.__lanHost);
|
|
@@ -9,13 +20,15 @@ function getShareUrl() {
|
|
|
9
20
|
return url;
|
|
10
21
|
}
|
|
11
22
|
|
|
12
|
-
export function triggerShare() {
|
|
13
|
-
|
|
23
|
+
export function triggerShare(options) {
|
|
24
|
+
options = options || {};
|
|
25
|
+
var url = getShareUrl(options.projectSlug);
|
|
26
|
+
var title = options.title || document.title || "Clay";
|
|
14
27
|
|
|
15
28
|
// Use Web Share API on mobile only
|
|
16
29
|
var isMobile = window.innerWidth <= 768 || /Mobi|Android/i.test(navigator.userAgent);
|
|
17
30
|
if (isMobile && navigator.share) {
|
|
18
|
-
navigator.share({ title:
|
|
31
|
+
navigator.share({ title: title, url: url }).catch(function () {});
|
|
19
32
|
return;
|
|
20
33
|
}
|
|
21
34
|
|
|
@@ -28,6 +41,8 @@ export function triggerShare() {
|
|
|
28
41
|
var qr = qrcode(0, "M");
|
|
29
42
|
qr.addData(url);
|
|
30
43
|
qr.make();
|
|
44
|
+
activeShareUrl = url;
|
|
45
|
+
activeShareTitle = title;
|
|
31
46
|
qrCanvas.innerHTML = qr.createSvgTag(5, 0);
|
|
32
47
|
qrUrl.innerHTML = url + '<span class="qr-hint">click to copy</span>';
|
|
33
48
|
|
|
@@ -49,7 +64,7 @@ export function initQrCode() {
|
|
|
49
64
|
|
|
50
65
|
// click URL to copy
|
|
51
66
|
qrUrl.addEventListener("click", function () {
|
|
52
|
-
var url = getShareUrl();
|
|
67
|
+
var url = activeShareUrl || getShareUrl();
|
|
53
68
|
copyToClipboard(url).then(function () {
|
|
54
69
|
qrUrl.innerHTML = "Copied!";
|
|
55
70
|
qrUrl.classList.add("copied");
|
|
@@ -62,6 +77,8 @@ export function initQrCode() {
|
|
|
62
77
|
|
|
63
78
|
qrOverlay.addEventListener("click", function () {
|
|
64
79
|
qrOverlay.classList.add("hidden");
|
|
80
|
+
activeShareUrl = null;
|
|
81
|
+
activeShareTitle = null;
|
|
65
82
|
});
|
|
66
83
|
|
|
67
84
|
// prevent closing when clicking the inner card
|
|
@@ -73,8 +90,8 @@ export function initQrCode() {
|
|
|
73
90
|
var qrShareBtn = document.getElementById("qr-share-btn");
|
|
74
91
|
if (qrShareBtn) {
|
|
75
92
|
qrShareBtn.addEventListener("click", function () {
|
|
76
|
-
var url = getShareUrl();
|
|
77
|
-
navigator.share({ title: document.title || "Clay", url: url }).catch(function () {});
|
|
93
|
+
var url = activeShareUrl || getShareUrl();
|
|
94
|
+
navigator.share({ title: activeShareTitle || document.title || "Clay", url: url }).catch(function () {});
|
|
78
95
|
});
|
|
79
96
|
}
|
|
80
97
|
|
|
@@ -82,6 +99,8 @@ export function initQrCode() {
|
|
|
82
99
|
document.addEventListener("keydown", function (e) {
|
|
83
100
|
if (e.key === "Escape" && !qrOverlay.classList.contains("hidden")) {
|
|
84
101
|
qrOverlay.classList.add("hidden");
|
|
102
|
+
activeShareUrl = null;
|
|
103
|
+
activeShareTitle = null;
|
|
85
104
|
}
|
|
86
105
|
});
|
|
87
106
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Compact Clay conversation hosted inside the global search drawer.
|
|
2
2
|
import { getWs } from './ws-ref.js';
|
|
3
|
-
import {
|
|
4
|
-
import { openHomeConversation } from './home-mate-chat.js';
|
|
3
|
+
import { openProjectSession } from './project-session-navigation.js';
|
|
5
4
|
import { refreshIcons } from './icons.js';
|
|
6
5
|
import { store } from './store.js';
|
|
7
6
|
import { mateAvatarUrl } from './avatar.js';
|
|
@@ -231,12 +230,9 @@ function respondToPermission(permission, decision) {
|
|
|
231
230
|
}
|
|
232
231
|
|
|
233
232
|
function expand() {
|
|
234
|
-
if (!state || !state.mateId || !state.
|
|
235
|
-
var mateId = state.mateId;
|
|
236
|
-
var sessionId = state.sessionId;
|
|
233
|
+
if (!state || !state.mateId || !state.localId) return;
|
|
237
234
|
if (closeHandler) closeHandler();
|
|
238
|
-
|
|
239
|
-
openHomeConversation(mateId, sessionId);
|
|
235
|
+
openProjectSession('mate-' + state.mateId, state.localId);
|
|
240
236
|
}
|
|
241
237
|
|
|
242
238
|
function render() {
|
|
@@ -259,7 +255,7 @@ function render() {
|
|
|
259
255
|
var actions = document.createElement("div");
|
|
260
256
|
actions.className = "search-clay-header-actions";
|
|
261
257
|
actions.appendChild(button("search", "Search", "Back to search", function () { detachSearchClayChat(); if (backHandler) backHandler(); }));
|
|
262
|
-
actions.appendChild(button("maximize-2", "Expand", "Open this conversation in
|
|
258
|
+
actions.appendChild(button("maximize-2", "Expand", "Open this conversation in project chat", expand));
|
|
263
259
|
header.appendChild(actions);
|
|
264
260
|
shell.appendChild(header);
|
|
265
261
|
transcript = document.createElement("div");
|
|
@@ -318,8 +314,8 @@ function render() {
|
|
|
318
314
|
sendButton.disabled = state.processing ? (!state.sessionId || state.stopping) : (!state.sessionId || !input.value.trim());
|
|
319
315
|
sendButton.setAttribute("aria-label", state.processing ? (state.stopping ? "Stopping Clay" : "Stop Clay") : "Send to Clay");
|
|
320
316
|
sendButton.innerHTML = state.processing ? '<i data-lucide="square" aria-hidden="true"></i>' : '<i data-lucide="arrow-up" aria-hidden="true"></i>';
|
|
321
|
-
var expandButton = shell.querySelector('.search-clay-icon-button[aria-label="Open this conversation in
|
|
322
|
-
if (expandButton) expandButton.disabled = !state.
|
|
317
|
+
var expandButton = shell.querySelector('.search-clay-icon-button[aria-label="Open this conversation in project chat"]');
|
|
318
|
+
if (expandButton) expandButton.disabled = !state.localId;
|
|
323
319
|
refreshIcons();
|
|
324
320
|
transcript.scrollTop = follow ? transcript.scrollHeight : previousTop;
|
|
325
321
|
}
|
|
@@ -331,7 +327,7 @@ export function startSearchClayChat(query, host, onBack, onClose) {
|
|
|
331
327
|
backHandler = onBack;
|
|
332
328
|
closeHandler = onClose;
|
|
333
329
|
var startedAt = Date.now();
|
|
334
|
-
state = { requestId: requestId(), mateId: null, sessionId: null, messages: [{ role: "user", text: clean }], queuedUsers: [], pendingTurns: 1, activities: [], permissions: [], activityExpanded: false, stream: "", draft: "", processing: true, stopping: false, awaitingCompletion: true, activity: "thinking", step: 0, startedAt: startedAt, updatedAt: startedAt, query: clean };
|
|
330
|
+
state = { requestId: requestId(), mateId: null, sessionId: null, localId: null, messages: [{ role: "user", text: clean }], queuedUsers: [], pendingTurns: 1, activities: [], permissions: [], activityExpanded: false, stream: "", draft: "", processing: true, stopping: false, awaitingCompletion: true, activity: "thinking", step: 0, startedAt: startedAt, updatedAt: startedAt, query: clean };
|
|
335
331
|
store.set({ searchClayChatState: state });
|
|
336
332
|
startProgressTimer();
|
|
337
333
|
render();
|
|
@@ -382,6 +378,7 @@ export function handleSearchClayMessage(msg) {
|
|
|
382
378
|
updateActivity(msg);
|
|
383
379
|
} else if (msg.type === "home_mate_history") {
|
|
384
380
|
state.sessionId = msg.sessionId || state.sessionId;
|
|
381
|
+
state.localId = typeof msg.localId === "number" ? msg.localId : state.localId;
|
|
385
382
|
state.messages = Array.isArray(msg.messages) ? msg.messages.filter(function (item) { return item && (item.role === "user" || item.role === "assistant"); }).map(function (item) { return { role: item.role, text: item.text || "" }; }) : state.messages;
|
|
386
383
|
state.processing = msg.isProcessing === true;
|
|
387
384
|
state.awaitingCompletion = state.processing;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { showToast } from './utils.js';
|
|
4
|
+
import { confirmSettingsAction, clearSettingsDraft, setSettingsDraft } from './server-settings-state.js';
|
|
5
|
+
import { enhanceSettingsSelects } from './settings-select.js';
|
|
6
|
+
|
|
7
|
+
function status(id, text) {
|
|
8
|
+
var el = document.getElementById(id);
|
|
9
|
+
if (el) { el.textContent = text; el.classList.remove('hidden'); }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function sendSetting(type, values, buttonId, statusId) {
|
|
13
|
+
var ws = getWs();
|
|
14
|
+
if (!ws || ws.readyState !== 1) { status(statusId, 'Disconnected. Your change has not been saved.'); return; }
|
|
15
|
+
var button = document.getElementById(buttonId);
|
|
16
|
+
if (button) button.disabled = true;
|
|
17
|
+
var input = buttonId === 'settings-pin-save-btn' ? document.getElementById('settings-pin-input') : buttonId.endsWith('-save') ? document.getElementById(buttonId.slice(0, -5)) : null;
|
|
18
|
+
if (input) { input.disabled = true; input.dispatchEvent(new Event('settings-sync')); }
|
|
19
|
+
status(statusId, 'Saving…');
|
|
20
|
+
var pending = Object.assign({}, store.get('serverSettingsPending') || {});
|
|
21
|
+
pending[type] = { buttonId: buttonId, statusId: statusId };
|
|
22
|
+
store.set({ serverSettingsPending: pending });
|
|
23
|
+
ws.send(JSON.stringify(Object.assign({ type: type }, values)));
|
|
24
|
+
setTimeout(function () {
|
|
25
|
+
if ((store.get('serverSettingsPending') || {})[type] !== pending[type]) return;
|
|
26
|
+
if (button) button.disabled = false;
|
|
27
|
+
if (input) { input.disabled = false; input.dispatchEvent(new Event('settings-sync')); }
|
|
28
|
+
status(statusId, 'No acknowledgement received. Reconnect and retry; the server may have saved this value.');
|
|
29
|
+
}, 12000);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function handleServerSettingResult(msg) {
|
|
33
|
+
var type = msg.type.replace(/_result$/, '');
|
|
34
|
+
var pending = Object.assign({}, store.get('serverSettingsPending') || {});
|
|
35
|
+
var request = pending[type];
|
|
36
|
+
if (!request) return;
|
|
37
|
+
delete pending[type];
|
|
38
|
+
store.set({ serverSettingsPending: pending });
|
|
39
|
+
var button = document.getElementById(request.buttonId);
|
|
40
|
+
if (button) { button.disabled = false; if (msg.ok && request.buttonId.endsWith('-save')) button.hidden = true; }
|
|
41
|
+
var input = request.buttonId === 'settings-pin-save-btn' ? document.getElementById('settings-pin-input') : request.buttonId.endsWith('-save') ? document.getElementById(request.buttonId.slice(0, -5)) : null;
|
|
42
|
+
if (input) { input.disabled = false; input.dispatchEvent(new Event('settings-sync')); }
|
|
43
|
+
status(request.statusId, msg.ok ? 'Saved. ' + (msg.timing || '') : (msg.error || 'Could not save. Retry your change.'));
|
|
44
|
+
if (msg.ok) clearSettingsDraft(type);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function finishSetting(msg, id, field) {
|
|
48
|
+
handleServerSettingResult(msg);
|
|
49
|
+
var el = document.getElementById(id);
|
|
50
|
+
var key = 'set_keep_awake';
|
|
51
|
+
if (!msg.type.endsWith('_result') && (store.get('serverSettingsDrafts') || {})[key]) return;
|
|
52
|
+
if (el && msg.ok !== false && msg[field] !== undefined) el.checked = !!msg[field];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function updatePlatformControls(config) {
|
|
56
|
+
store.set({ serverSettingsConfig: config });
|
|
57
|
+
document.getElementById('settings-keep-awake').disabled = config.platform !== 'darwin';
|
|
58
|
+
status('settings-awake-support', config.platform === 'darwin' ? 'Applies while Clay is running on this Mac.' : 'Unavailable on this platform. Keep awake requires macOS.');
|
|
59
|
+
var channel = document.getElementById('settings-update-channel');
|
|
60
|
+
if (channel && !(store.get('serverSettingsDrafts') || {}).set_update_channel) channel.checked = config.updateChannel === 'beta';
|
|
61
|
+
enhanceSettingsSelects(document.getElementById('server-settings'));
|
|
62
|
+
document.getElementById('settings-image-retention').dispatchEvent(new Event('settings-sync'));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function initServerControls() {
|
|
66
|
+
['settings-keep-awake', 'settings-image-retention', 'settings-update-channel', 'settings-pin-set-btn', 'settings-pin-remove-btn'].forEach(function (id) { document.getElementById(id).disabled = true; });
|
|
67
|
+
var check = document.getElementById('settings-update-check');
|
|
68
|
+
var update = document.createElement('button'); update.type = 'button'; update.className = 'settings-btn-sm'; update.id = 'settings-install-update'; update.hidden = true; update.textContent = 'Update and restart…';
|
|
69
|
+
check.insertAdjacentElement('afterend', update);
|
|
70
|
+
var receipt = document.createElement('p'); receipt.id = 'settings-update-status'; receipt.className = 'settings-hint'; receipt.setAttribute('role', 'status'); update.insertAdjacentElement('afterend', receipt);
|
|
71
|
+
update.onclick = function () {
|
|
72
|
+
confirmSettingsAction('Install the available release and restart Clay? Active work will be interrupted for everyone. Unsaved settings remain drafts in this tab.', 'Update and restart', function () {
|
|
73
|
+
var ws = getWs();
|
|
74
|
+
if (!ws || ws.readyState !== 1) { status('settings-update-status', 'Disconnected. Reconnect before updating.'); return; }
|
|
75
|
+
update.disabled = true; status('settings-update-status', 'Update requested. Waiting for the daemon…');
|
|
76
|
+
ws.send(JSON.stringify({ type: 'update_now' }));
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
document.getElementById('settings-pin-set-btn').onclick = showPinForm;
|
|
81
|
+
document.getElementById('settings-pin-cancel-btn').onclick = hidePinForm;
|
|
82
|
+
document.getElementById('settings-pin-save-btn').onclick = submitPin;
|
|
83
|
+
document.getElementById('settings-pin-input').oninput = function () { setSettingsDraft('pin', !!this.value); };
|
|
84
|
+
document.getElementById('settings-pin-remove-btn').onclick = function () {
|
|
85
|
+
confirmSettingsAction('Remove the server PIN? This changes sign-in protection for new connections.', 'Remove PIN', function () {
|
|
86
|
+
sendSetting('set_pin', { pin: null }, 'settings-pin-remove-btn', 'settings-pin-error');
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
[
|
|
90
|
+
['settings-keep-awake', 'set_keep_awake', 'value', 'Saved changes apply now.'],
|
|
91
|
+
['settings-image-retention', 'set_image_retention', 'days', 'Review retention. The next cleanup permanently deletes expired images.'],
|
|
92
|
+
['settings-update-channel', 'set_update_channel', 'channel', 'Choose whether future update checks include prereleases.']
|
|
93
|
+
].forEach(function (entry) {
|
|
94
|
+
var input = document.getElementById(entry[0]);
|
|
95
|
+
var receipt = document.createElement('p');
|
|
96
|
+
receipt.id = entry[0] + '-status'; receipt.className = 'settings-hint'; receipt.setAttribute('role', 'status');
|
|
97
|
+
input.closest('.settings-toggle-row').insertAdjacentElement('afterend', receipt);
|
|
98
|
+
var save = document.createElement('button'); save.type = 'button'; save.className = 'settings-btn-sm'; save.textContent = 'Save changes';
|
|
99
|
+
save.id = entry[0] + '-save'; save.hidden = true; receipt.insertAdjacentElement('beforebegin', save);
|
|
100
|
+
input.onchange = function () {
|
|
101
|
+
setSettingsDraft(entry[1], true); save.hidden = false; receipt.textContent = entry[3];
|
|
102
|
+
};
|
|
103
|
+
save.onclick = function () {
|
|
104
|
+
var values = {}; values[entry[2]] = entry[2] === 'days' ? Number(input.value) : entry[2] === 'channel' ? (input.checked ? 'beta' : 'stable') : input.checked;
|
|
105
|
+
sendSetting(entry[1], values, save.id, receipt.id);
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
document.getElementById('settings-restart-btn').onclick = function () {
|
|
109
|
+
confirmSettingsAction('Restart Clay for everyone? Active agent work will be interrupted. Clients will reconnect. Unsaved settings are not applied.', 'Restart server', function () {
|
|
110
|
+
sendSetting('restart_server', {}, 'settings-restart-btn', 'settings-restart-error');
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
var shutdown = document.getElementById('settings-shutdown-input');
|
|
114
|
+
var shutdownButton = document.getElementById('settings-shutdown-btn');
|
|
115
|
+
var reviewShutdown = document.getElementById('settings-shutdown-review');
|
|
116
|
+
var shutdownFlow = document.getElementById('settings-shutdown-flow');
|
|
117
|
+
reviewShutdown.onclick = function () {
|
|
118
|
+
store.set({ serverShutdownReview: true });
|
|
119
|
+
reviewShutdown.hidden = true;
|
|
120
|
+
reviewShutdown.setAttribute('aria-expanded', 'true');
|
|
121
|
+
shutdownFlow.hidden = false;
|
|
122
|
+
shutdown.focus();
|
|
123
|
+
};
|
|
124
|
+
document.getElementById('settings-shutdown-back').onclick = function () {
|
|
125
|
+
store.set({ serverShutdownReview: false });
|
|
126
|
+
shutdownFlow.hidden = true;
|
|
127
|
+
reviewShutdown.hidden = false;
|
|
128
|
+
reviewShutdown.setAttribute('aria-expanded', 'false');
|
|
129
|
+
shutdown.value = '';
|
|
130
|
+
shutdownButton.disabled = true;
|
|
131
|
+
reviewShutdown.focus();
|
|
132
|
+
};
|
|
133
|
+
shutdown.oninput = function () { shutdownButton.disabled = this.value.trim().toLowerCase() !== 'shutdown'; };
|
|
134
|
+
shutdownButton.onclick = function () {
|
|
135
|
+
if (!store.get('serverShutdownReview') || shutdown.value.trim().toLowerCase() !== 'shutdown') return;
|
|
136
|
+
confirmSettingsAction('Stop Clay for everyone? Active work stops and all clients disconnect. Start the service again from the host to recover.', 'Shut down server', function () {
|
|
137
|
+
sendSetting('shutdown_server', {}, shutdownButton.id, 'settings-shutdown-error');
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function updateDaemonConfig(config) {
|
|
143
|
+
if (config.error) { status('settings-access-status', config.error); return; }
|
|
144
|
+
store.set({ serverSettingsConfigLoaded: true });
|
|
145
|
+
['settings-image-retention', 'settings-update-channel', 'settings-pin-set-btn', 'settings-pin-remove-btn'].forEach(function (id) { document.getElementById(id).disabled = false; });
|
|
146
|
+
// Nav header: show hostname (strip .local suffix, lowercase)
|
|
147
|
+
var nameEl = document.getElementById("settings-server-name");
|
|
148
|
+
if (nameEl && config.hostname) {
|
|
149
|
+
var displayHost = config.hostname.replace(/\.local$/i, "").toLowerCase();
|
|
150
|
+
nameEl.textContent = displayHost;
|
|
151
|
+
nameEl.title = config.hostname;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Host
|
|
155
|
+
var hostnameEl = document.getElementById("settings-hostname");
|
|
156
|
+
var lanIpEl = document.getElementById("settings-lan-ip");
|
|
157
|
+
if (hostnameEl) hostnameEl.textContent = config.hostname || "-";
|
|
158
|
+
if (lanIpEl) lanIpEl.textContent = config.lanIp || "";
|
|
159
|
+
|
|
160
|
+
// Port
|
|
161
|
+
var portEl = document.getElementById("settings-port");
|
|
162
|
+
if (portEl) portEl.textContent = String(config.port || "-");
|
|
163
|
+
|
|
164
|
+
// TLS
|
|
165
|
+
var tlsEl = document.getElementById("settings-tls");
|
|
166
|
+
if (tlsEl) {
|
|
167
|
+
tlsEl.textContent = config.tls ? "Enabled" : "Disabled";
|
|
168
|
+
tlsEl.classList.toggle("settings-badge-green", !!config.tls);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Debug
|
|
172
|
+
var debugEl = document.getElementById("settings-debug");
|
|
173
|
+
if (debugEl) {
|
|
174
|
+
debugEl.textContent = config.debug ? "Enabled" : "Disabled";
|
|
175
|
+
debugEl.classList.toggle("settings-badge-on", !!config.debug);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// PIN status
|
|
179
|
+
updatePinStatus(!!config.pinEnabled);
|
|
180
|
+
|
|
181
|
+
// Auto-continue on rate limit
|
|
182
|
+
// Auto-continue is now per-user (User Settings > Behavior)
|
|
183
|
+
|
|
184
|
+
// Keep awake
|
|
185
|
+
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
186
|
+
if (keepAwakeToggle && !(store.get('serverSettingsDrafts') || {}).set_keep_awake) keepAwakeToggle.checked = !!config.keepAwake;
|
|
187
|
+
|
|
188
|
+
// Image retention
|
|
189
|
+
var imageRetentionSelect = document.getElementById("settings-image-retention");
|
|
190
|
+
if (imageRetentionSelect && !(store.get('serverSettingsDrafts') || {}).set_image_retention && config.imageRetentionDays !== undefined) {
|
|
191
|
+
imageRetentionSelect.value = String(config.imageRetentionDays);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
updatePlatformControls(config);
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function handleSetPinResult(msg) {
|
|
199
|
+
if (msg.ok) {
|
|
200
|
+
updatePinStatus(!!msg.pinEnabled);
|
|
201
|
+
hidePinForm();
|
|
202
|
+
showToast(msg.pinEnabled ? "PIN saved" : "PIN removed");
|
|
203
|
+
clearSettingsDraft("pin");
|
|
204
|
+
} else { status("settings-pin-error", msg.error || "Could not save PIN. Your draft is retained."); }
|
|
205
|
+
document.getElementById("settings-pin-save-btn").disabled = false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function handleKeepAwakeChanged(msg) {
|
|
209
|
+
finishSetting(msg, "settings-keep-awake", "keepAwake");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function handleAutoContinueChanged(msg) {
|
|
213
|
+
// Auto-continue is now per-user; server broadcast no longer updates UI
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function handleRestartResult(msg) {
|
|
217
|
+
var restartBtn = document.getElementById("settings-restart-btn");
|
|
218
|
+
var errorEl = document.getElementById("settings-restart-error");
|
|
219
|
+
|
|
220
|
+
if (msg.ok) {
|
|
221
|
+
if (restartBtn) { restartBtn.textContent = "Server restarting..."; restartBtn.disabled = true; }
|
|
222
|
+
showToast("Server is restarting...");
|
|
223
|
+
} else {
|
|
224
|
+
if (restartBtn) {
|
|
225
|
+
restartBtn.textContent = "Restart";
|
|
226
|
+
restartBtn.disabled = false;
|
|
227
|
+
}
|
|
228
|
+
if (errorEl) {
|
|
229
|
+
errorEl.textContent = msg.error || "Restart failed";
|
|
230
|
+
errorEl.classList.remove("hidden");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function handleShutdownResult(msg) {
|
|
236
|
+
var shutdownInput = document.getElementById("settings-shutdown-input");
|
|
237
|
+
var shutdownBtn = document.getElementById("settings-shutdown-btn");
|
|
238
|
+
var errorEl = document.getElementById("settings-shutdown-error");
|
|
239
|
+
|
|
240
|
+
if (msg.ok) {
|
|
241
|
+
if (shutdownBtn) { shutdownBtn.textContent = "Server stopping..."; shutdownBtn.disabled = true; }
|
|
242
|
+
showToast("Server is shutting down...");
|
|
243
|
+
} else {
|
|
244
|
+
if (shutdownBtn) {
|
|
245
|
+
shutdownBtn.textContent = "Shut down…";
|
|
246
|
+
shutdownBtn.disabled = false;
|
|
247
|
+
}
|
|
248
|
+
if (shutdownInput) shutdownInput.disabled = false;
|
|
249
|
+
if (errorEl) {
|
|
250
|
+
errorEl.textContent = msg.error || "Shutdown failed";
|
|
251
|
+
errorEl.classList.remove("hidden");
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// --- PIN form management ---
|
|
257
|
+
function showPinForm() {
|
|
258
|
+
var form = document.getElementById("settings-pin-form");
|
|
259
|
+
var input = document.getElementById("settings-pin-input");
|
|
260
|
+
var errorEl = document.getElementById("settings-pin-error");
|
|
261
|
+
if (form) form.classList.remove("hidden");
|
|
262
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
263
|
+
if (input) input.focus();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function hidePinForm() {
|
|
267
|
+
var form = document.getElementById("settings-pin-form");
|
|
268
|
+
var input = document.getElementById("settings-pin-input");
|
|
269
|
+
var errorEl = document.getElementById("settings-pin-error");
|
|
270
|
+
clearSettingsDraft("pin");
|
|
271
|
+
if (form) form.classList.add("hidden");
|
|
272
|
+
if (input) input.value = "";
|
|
273
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function submitPin() {
|
|
277
|
+
var input = document.getElementById("settings-pin-input");
|
|
278
|
+
var errorEl = document.getElementById("settings-pin-error");
|
|
279
|
+
if (!input) return;
|
|
280
|
+
var pin = input.value.trim();
|
|
281
|
+
if (!/^\d{6}$/.test(pin)) {
|
|
282
|
+
if (errorEl) { errorEl.textContent = "Enter exactly six digits."; errorEl.classList.remove("hidden"); }
|
|
283
|
+
input.focus();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (errorEl) errorEl.classList.add("hidden");
|
|
287
|
+
var ws = getWs();
|
|
288
|
+
if (ws && ws.readyState === 1) {
|
|
289
|
+
sendSetting("set_pin", { pin: pin }, "settings-pin-save-btn", "settings-pin-error");
|
|
290
|
+
} else { status("settings-pin-error", "Disconnected. Reconnect and save again."); }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function updatePinStatus(enabled) {
|
|
294
|
+
var statusEl = document.getElementById("settings-pin-status");
|
|
295
|
+
var setBtn = document.getElementById("settings-pin-set-btn");
|
|
296
|
+
var removeBtn = document.getElementById("settings-pin-remove-btn");
|
|
297
|
+
var actionLabel = document.getElementById("settings-pin-action-label");
|
|
298
|
+
|
|
299
|
+
if (statusEl) {
|
|
300
|
+
statusEl.textContent = enabled ? "Enabled" : "Disabled";
|
|
301
|
+
statusEl.classList.toggle("settings-badge-green", enabled);
|
|
302
|
+
}
|
|
303
|
+
if (setBtn) setBtn.textContent = enabled ? "Change PIN" : "Set PIN";
|
|
304
|
+
if (removeBtn) removeBtn.classList.toggle("hidden", !enabled);
|
|
305
|
+
if (actionLabel) actionLabel.textContent = enabled ? "Change PIN" : "Set PIN";
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
export function handleServerUpdate(msg) {
|
|
310
|
+
var button = document.getElementById('settings-install-update');
|
|
311
|
+
if (!button) return;
|
|
312
|
+
if (msg.type === 'update_available') { button.hidden = false; button.disabled = false; status('settings-update-status', 'Version ' + msg.version + ' is available. Review and restart when ready.'); }
|
|
313
|
+
if (msg.type === 'up_to_date') { button.hidden = true; status('settings-update-status', 'No newer release is available on this channel.'); }
|
|
314
|
+
if (msg.type === 'update_started') { button.disabled = true; status('settings-update-status', 'Updating. The connection will recover after the daemon restarts.'); }
|
|
315
|
+
if (msg.type === 'update_check_failed') { document.getElementById('settings-update-check').disabled = false; status('settings-update-status', msg.error || 'Update check failed. Retry when connected.'); }
|
|
316
|
+
}
|