clay-server 2.15.2 → 2.16.0-beta.2
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/lib/daemon.js +9 -0
- package/lib/project.js +156 -15
- package/lib/public/app.js +391 -86
- package/lib/public/css/command-palette.css +43 -3
- package/lib/public/css/filebrowser.css +28 -3
- package/lib/public/css/home-hub.css +77 -0
- package/lib/public/css/icon-strip.css +15 -1
- package/lib/public/css/input.css +1 -1
- package/lib/public/css/mates.css +207 -3
- package/lib/public/css/messages.css +46 -3
- package/lib/public/css/mobile-nav.css +412 -7
- package/lib/public/css/rewind.css +13 -0
- package/lib/public/css/server-settings.css +21 -5
- package/lib/public/css/session-search.css +1 -1
- package/lib/public/css/sidebar.css +20 -2
- package/lib/public/css/title-bar.css +1 -23
- package/lib/public/index.html +57 -6
- package/lib/public/modules/longpress.js +74 -0
- package/lib/public/modules/mate-knowledge.js +30 -0
- package/lib/public/modules/mate-sidebar.js +14 -9
- package/lib/public/modules/notifications.js +10 -0
- package/lib/public/modules/project-settings.js +14 -0
- package/lib/public/modules/server-settings.js +17 -0
- package/lib/public/modules/sidebar.js +681 -31
- package/lib/public/modules/stt.js +5 -1
- package/lib/public/modules/terminal.js +28 -1
- package/lib/public/modules/tools.js +5 -1
- package/lib/sdk-bridge.js +7 -6
- package/lib/server.js +53 -3
- package/package.json +1 -1
package/lib/public/index.html
CHANGED
|
@@ -83,6 +83,8 @@
|
|
|
83
83
|
<p id="hub-greeting-date"></p>
|
|
84
84
|
</div>
|
|
85
85
|
|
|
86
|
+
<div id="home-hub-mates" class="home-hub-mates hidden"></div>
|
|
87
|
+
|
|
86
88
|
<div class="hub-columns">
|
|
87
89
|
<div class="hub-card hub-upcoming">
|
|
88
90
|
<div class="hub-card-header">
|
|
@@ -198,6 +200,7 @@
|
|
|
198
200
|
<img id="mate-sidebar-avatar" class="mate-sidebar-avatar" alt="">
|
|
199
201
|
<span id="mate-sidebar-name" class="mate-sidebar-name"></span>
|
|
200
202
|
<div id="mate-sidebar-seed-tooltip" class="mate-seed-tooltip hidden"></div>
|
|
203
|
+
<button id="mate-sidebar-toggle-btn" class="sidebar-collapse-btn" title="Collapse sidebar"><i data-lucide="panel-left-close"></i></button>
|
|
201
204
|
</div>
|
|
202
205
|
<div id="mate-sidebar-tools">
|
|
203
206
|
<button id="mate-knowledge-btn"><i data-lucide="book-open"></i> <span>Knowledge</span><span id="mate-knowledge-count" class="sidebar-badge hidden"></span></button>
|
|
@@ -246,8 +249,12 @@
|
|
|
246
249
|
<button id="mate-knowledge-editor-save" class="mate-knowledge-header-btn" disabled title="Save"><i data-lucide="save"></i></button>
|
|
247
250
|
<button id="mate-knowledge-close-btn" class="mate-knowledge-header-btn" title="Close"><i data-lucide="x"></i></button>
|
|
248
251
|
</div>
|
|
252
|
+
<div class="mate-knowledge-tab-bar">
|
|
253
|
+
<button class="mate-knowledge-tab-btn active" data-tab="edit">Edit</button>
|
|
254
|
+
<button class="mate-knowledge-tab-btn" data-tab="preview">Preview</button>
|
|
255
|
+
</div>
|
|
249
256
|
<div class="mate-knowledge-body">
|
|
250
|
-
<div class="mate-knowledge-editor-pane">
|
|
257
|
+
<div class="mate-knowledge-editor-pane mobile-active">
|
|
251
258
|
<div class="mate-editor-highlight-wrap">
|
|
252
259
|
<pre class="mate-editor-highlight-pre" aria-hidden="true"><code id="mate-knowledge-editor-highlight" class="language-markdown"></code></pre>
|
|
253
260
|
<textarea id="mate-knowledge-editor-content" placeholder="Select a file from the sidebar..." spellcheck="false"></textarea>
|
|
@@ -265,10 +272,19 @@
|
|
|
265
272
|
<div class="title-bar-content">
|
|
266
273
|
<div id="header-left">
|
|
267
274
|
<button id="sidebar-expand-btn" title="Open sidebar"><i data-lucide="panel-left-open"></i></button>
|
|
275
|
+
<div id="mate-collapsed-info" class="mate-collapsed-info">
|
|
276
|
+
<img id="mate-collapsed-avatar" class="mate-collapsed-avatar" alt="">
|
|
277
|
+
<span id="mate-collapsed-name" class="mate-collapsed-name"></span>
|
|
278
|
+
</div>
|
|
268
279
|
<button id="hamburger-btn" aria-label="Toggle sidebar" title="Toggle sidebar"><i data-lucide="menu"></i></button>
|
|
269
280
|
<span class="header-title" id="header-title">Connecting...</span>
|
|
270
281
|
<button id="header-info-btn" type="button" title="Session info"><i data-lucide="info"></i></button>
|
|
271
282
|
<button id="header-rename-btn" type="button" title="Rename session"><i data-lucide="pencil"></i></button>
|
|
283
|
+
<div id="mate-mobile-title" class="mate-mobile-title hidden">
|
|
284
|
+
<button id="mate-mobile-back" class="mate-mobile-back" type="button" aria-label="Back"><i data-lucide="chevron-left"></i></button>
|
|
285
|
+
<img id="mate-mobile-avatar" class="mate-mobile-avatar" alt="">
|
|
286
|
+
<span id="mate-mobile-name" class="mate-mobile-name"></span>
|
|
287
|
+
</div>
|
|
272
288
|
</div>
|
|
273
289
|
<div id="todo-sticky" class="hidden"></div>
|
|
274
290
|
<div id="ralph-sticky" class="hidden"></div>
|
|
@@ -444,11 +460,11 @@
|
|
|
444
460
|
|
|
445
461
|
<!-- Mobile bottom tab bar -->
|
|
446
462
|
<div id="mobile-tab-bar">
|
|
447
|
-
<button class="mobile-tab" data-tab="
|
|
448
|
-
<button class="mobile-tab" data-tab="
|
|
449
|
-
<button class="mobile-tab-home" id="mobile-home-btn"><img src="/icon-banded-76.png" alt="
|
|
450
|
-
<button class="mobile-tab" data-tab="
|
|
451
|
-
<button class="mobile-tab" data-tab="
|
|
463
|
+
<button class="mobile-tab" data-tab="chat" id="mob-tab-chat"><i data-lucide="message-square"></i><span class="mobile-tab-label">Chat</span></button>
|
|
464
|
+
<button class="mobile-tab" data-tab="search" id="mob-tab-search"><i data-lucide="search"></i><span class="mobile-tab-label">Search</span></button>
|
|
465
|
+
<button class="mobile-tab-home" id="mobile-home-btn"><img src="/icon-banded-76.png" alt="Home" class="mobile-home-icon"></button>
|
|
466
|
+
<button class="mobile-tab" data-tab="tools" id="mob-tab-tools"><i data-lucide="wrench"></i><span class="mobile-tab-label">Tools</span></button>
|
|
467
|
+
<button class="mobile-tab" data-tab="settings" id="mob-tab-settings"><i data-lucide="settings"></i><span class="mobile-tab-label">Settings</span></button>
|
|
452
468
|
</div>
|
|
453
469
|
|
|
454
470
|
<div id="file-viewer" class="hidden">
|
|
@@ -481,6 +497,16 @@
|
|
|
481
497
|
<div class="project-settings-nav">
|
|
482
498
|
<div class="project-settings-nav-inner">
|
|
483
499
|
<div class="server-settings-nav-header" id="ps-nav-title">-</div>
|
|
500
|
+
<select id="ps-nav-dropdown" class="settings-nav-dropdown">
|
|
501
|
+
<optgroup label="General">
|
|
502
|
+
<option value="profile" selected>Profile</option>
|
|
503
|
+
<option value="defaults">Model</option>
|
|
504
|
+
</optgroup>
|
|
505
|
+
<optgroup label="Config">
|
|
506
|
+
<option value="instructions">Instructions</option>
|
|
507
|
+
<option value="environment">Environment</option>
|
|
508
|
+
</optgroup>
|
|
509
|
+
</select>
|
|
484
510
|
<div class="server-settings-nav-items">
|
|
485
511
|
<div class="settings-nav-category">General</div>
|
|
486
512
|
<button class="settings-nav-item active" data-section="profile"><span>Profile</span></button>
|
|
@@ -662,6 +688,11 @@
|
|
|
662
688
|
<button class="term-key term-key-arrow" data-key="down">▼</button>
|
|
663
689
|
<button class="term-key term-key-arrow" data-key="left">◀</button>
|
|
664
690
|
<button class="term-key term-key-arrow" data-key="right">▶</button>
|
|
691
|
+
<span class="term-key-spacer"></span>
|
|
692
|
+
<button class="term-key term-key-toggle" data-key="alt">Alt</button>
|
|
693
|
+
<button class="term-key" data-key="pipe">|</button>
|
|
694
|
+
<button class="term-key" data-key="slash">/</button>
|
|
695
|
+
<button class="term-key" data-key="tilde">~</button>
|
|
665
696
|
</div>
|
|
666
697
|
<div id="terminal-body"></div>
|
|
667
698
|
</div>
|
|
@@ -759,6 +790,7 @@
|
|
|
759
790
|
<div class="settings-nav-category">Config</div>
|
|
760
791
|
<button class="settings-nav-item" data-section="claudemd"><span>Instructions</span></button>
|
|
761
792
|
<button class="settings-nav-item" data-section="environment"><span>Environment</span></button>
|
|
793
|
+
<button class="settings-nav-item" data-section="storage"><span>Storage</span></button>
|
|
762
794
|
<div class="settings-nav-separator settings-admin-only"></div>
|
|
763
795
|
<div class="settings-nav-category settings-admin-only">Admin</div>
|
|
764
796
|
<button class="settings-nav-item settings-admin-only" data-section="admin-users"><span>Users</span></button>
|
|
@@ -988,6 +1020,25 @@
|
|
|
988
1020
|
<!-- rows rendered by JS -->
|
|
989
1021
|
</div>
|
|
990
1022
|
</div>
|
|
1023
|
+
<div class="server-settings-section" data-section="storage">
|
|
1024
|
+
<h2>Storage</h2>
|
|
1025
|
+
<div class="settings-card">
|
|
1026
|
+
<label class="settings-toggle-row">
|
|
1027
|
+
<div>
|
|
1028
|
+
<span class="settings-label">Image retention</span>
|
|
1029
|
+
<div class="settings-hint">Automatically delete chat images older than this period.</div>
|
|
1030
|
+
</div>
|
|
1031
|
+
<select id="settings-image-retention" class="settings-select">
|
|
1032
|
+
<option value="0">Forever</option>
|
|
1033
|
+
<option value="3">3 days</option>
|
|
1034
|
+
<option value="7" selected>7 days</option>
|
|
1035
|
+
<option value="14">14 days</option>
|
|
1036
|
+
<option value="30">30 days</option>
|
|
1037
|
+
<option value="90">90 days</option>
|
|
1038
|
+
</select>
|
|
1039
|
+
</label>
|
|
1040
|
+
</div>
|
|
1041
|
+
</div>
|
|
991
1042
|
<div class="server-settings-section hidden" data-section="keep-awake" id="settings-keep-awake-section">
|
|
992
1043
|
<h2>Keep Awake</h2>
|
|
993
1044
|
<div class="settings-card">
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Long-press to synthesize contextmenu events on touch devices.
|
|
2
|
+
// All existing contextmenu listeners automatically work with this.
|
|
3
|
+
|
|
4
|
+
var LONG_PRESS_MS = 500;
|
|
5
|
+
var MOVE_THRESHOLD = 10;
|
|
6
|
+
|
|
7
|
+
var _timer = null;
|
|
8
|
+
var _startX = 0;
|
|
9
|
+
var _startY = 0;
|
|
10
|
+
var _fired = false;
|
|
11
|
+
var _targetEl = null;
|
|
12
|
+
|
|
13
|
+
function cancelTimer() {
|
|
14
|
+
if (_timer) {
|
|
15
|
+
clearTimeout(_timer);
|
|
16
|
+
_timer = null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function initLongPress() {
|
|
21
|
+
if (!("ontouchstart" in window)) return;
|
|
22
|
+
|
|
23
|
+
document.addEventListener("touchstart", function (e) {
|
|
24
|
+
if (e.touches.length !== 1) {
|
|
25
|
+
cancelTimer();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
var touch = e.touches[0];
|
|
29
|
+
_startX = touch.clientX;
|
|
30
|
+
_startY = touch.clientY;
|
|
31
|
+
_fired = false;
|
|
32
|
+
_targetEl = e.target;
|
|
33
|
+
|
|
34
|
+
_timer = setTimeout(function () {
|
|
35
|
+
_timer = null;
|
|
36
|
+
_fired = true;
|
|
37
|
+
|
|
38
|
+
// Haptic feedback if available
|
|
39
|
+
if (navigator.vibrate) navigator.vibrate(30);
|
|
40
|
+
|
|
41
|
+
var evt = new MouseEvent("contextmenu", {
|
|
42
|
+
bubbles: true,
|
|
43
|
+
cancelable: true,
|
|
44
|
+
clientX: _startX,
|
|
45
|
+
clientY: _startY
|
|
46
|
+
});
|
|
47
|
+
_targetEl.dispatchEvent(evt);
|
|
48
|
+
}, LONG_PRESS_MS);
|
|
49
|
+
}, { passive: true });
|
|
50
|
+
|
|
51
|
+
document.addEventListener("touchmove", function (e) {
|
|
52
|
+
if (!_timer) return;
|
|
53
|
+
var touch = e.touches[0];
|
|
54
|
+
var dx = touch.clientX - _startX;
|
|
55
|
+
var dy = touch.clientY - _startY;
|
|
56
|
+
if (dx * dx + dy * dy > MOVE_THRESHOLD * MOVE_THRESHOLD) {
|
|
57
|
+
cancelTimer();
|
|
58
|
+
}
|
|
59
|
+
}, { passive: true });
|
|
60
|
+
|
|
61
|
+
document.addEventListener("touchend", function (e) {
|
|
62
|
+
cancelTimer();
|
|
63
|
+
// Suppress tap/click after a long-press fired
|
|
64
|
+
if (_fired) {
|
|
65
|
+
_fired = false;
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
}
|
|
68
|
+
}, { passive: false });
|
|
69
|
+
|
|
70
|
+
document.addEventListener("touchcancel", function () {
|
|
71
|
+
cancelTimer();
|
|
72
|
+
_fired = false;
|
|
73
|
+
}, { passive: true });
|
|
74
|
+
}
|
|
@@ -161,6 +161,36 @@ export function initMateKnowledge(mateWsGetter) {
|
|
|
161
161
|
editorContentEl.addEventListener("scroll", syncHighlightScroll);
|
|
162
162
|
initFormatPopover(editorContentEl);
|
|
163
163
|
}
|
|
164
|
+
|
|
165
|
+
// --- Mobile tab switching (Edit / Preview) ---
|
|
166
|
+
var tabBar = document.querySelector(".mate-knowledge-tab-bar");
|
|
167
|
+
if (tabBar) {
|
|
168
|
+
var tabBtns = tabBar.querySelectorAll(".mate-knowledge-tab-btn");
|
|
169
|
+
var editorPane = document.querySelector(".mate-knowledge-editor-pane");
|
|
170
|
+
var previewPane = document.querySelector(".mate-knowledge-preview-pane");
|
|
171
|
+
for (var ti = 0; ti < tabBtns.length; ti++) {
|
|
172
|
+
(function (btn) {
|
|
173
|
+
btn.addEventListener("click", function () {
|
|
174
|
+
for (var j = 0; j < tabBtns.length; j++) {
|
|
175
|
+
tabBtns[j].classList.remove("active");
|
|
176
|
+
}
|
|
177
|
+
btn.classList.add("active");
|
|
178
|
+
var tab = btn.dataset.tab;
|
|
179
|
+
if (editorPane && previewPane) {
|
|
180
|
+
if (tab === "edit") {
|
|
181
|
+
editorPane.classList.add("mobile-active");
|
|
182
|
+
previewPane.classList.remove("mobile-active");
|
|
183
|
+
} else {
|
|
184
|
+
editorPane.classList.remove("mobile-active");
|
|
185
|
+
previewPane.classList.add("mobile-active");
|
|
186
|
+
// Ensure preview is up to date
|
|
187
|
+
if (typeof updatePreview === "function") updatePreview();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
})(tabBtns[ti]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
164
194
|
}
|
|
165
195
|
|
|
166
196
|
// --- Mode switching ---
|
|
@@ -139,11 +139,16 @@ export function showMateSidebar(mateId, mateData) {
|
|
|
139
139
|
|
|
140
140
|
var mateColor = profile.avatarColor || mateData.avatarColor || "#7c3aed";
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
142
|
+
var avatarUrl = "https://api.dicebear.com/9.x/" + encodeURIComponent(avatarStyle) + "/svg?seed=" + encodeURIComponent(avatarSeed) + "&size=32";
|
|
143
|
+
if (avatarEl) avatarEl.src = avatarUrl;
|
|
145
144
|
if (nameEl) nameEl.textContent = displayName;
|
|
146
145
|
|
|
146
|
+
// Also populate collapsed header info
|
|
147
|
+
var collapsedAvatar = document.getElementById("mate-collapsed-avatar");
|
|
148
|
+
var collapsedName = document.getElementById("mate-collapsed-name");
|
|
149
|
+
if (collapsedAvatar) collapsedAvatar.src = avatarUrl;
|
|
150
|
+
if (collapsedName) collapsedName.textContent = displayName;
|
|
151
|
+
|
|
147
152
|
// Apply mate color to sidebar
|
|
148
153
|
var headerEl = columnEl.querySelector(".mate-sidebar-header");
|
|
149
154
|
if (headerEl) headerEl.style.background = mateColor;
|
|
@@ -375,14 +380,14 @@ function renderMateSessionItem(s) {
|
|
|
375
380
|
el.className = "mate-session-item" + (s.active ? " active" : "");
|
|
376
381
|
el.dataset.sessionId = s.id;
|
|
377
382
|
|
|
383
|
+
var processingDot = document.createElement("span");
|
|
384
|
+
processingDot.className = "session-processing";
|
|
385
|
+
if (!s.isProcessing) processingDot.style.display = "none";
|
|
386
|
+
el.appendChild(processingDot);
|
|
387
|
+
|
|
378
388
|
var textSpan = document.createElement("span");
|
|
379
389
|
textSpan.className = "mate-session-item-text";
|
|
380
|
-
|
|
381
|
-
if (s.isProcessing) {
|
|
382
|
-
html += '<span class="mate-session-processing"></span>';
|
|
383
|
-
}
|
|
384
|
-
html += escapeHtml(s.title || "New Session");
|
|
385
|
-
textSpan.innerHTML = html;
|
|
390
|
+
textSpan.textContent = s.title || "New Session";
|
|
386
391
|
el.appendChild(textSpan);
|
|
387
392
|
|
|
388
393
|
// Relative time
|
|
@@ -71,10 +71,20 @@ export function initNotifications(_ctx) {
|
|
|
71
71
|
// --- Mobile viewport (iOS keyboard handling) ---
|
|
72
72
|
if (window.visualViewport) {
|
|
73
73
|
var layout = $("layout");
|
|
74
|
+
var mobileTabBar = document.getElementById("mobile-tab-bar");
|
|
74
75
|
function onViewportChange() {
|
|
75
76
|
layout.style.height = window.visualViewport.height + "px";
|
|
76
77
|
document.documentElement.scrollTop = 0;
|
|
77
78
|
ctx.scrollToBottom();
|
|
79
|
+
// Hide tab bar when software keyboard is open
|
|
80
|
+
if (mobileTabBar) {
|
|
81
|
+
var keyboardOpen = window.visualViewport.height < window.innerHeight * 0.75;
|
|
82
|
+
if (keyboardOpen) {
|
|
83
|
+
mobileTabBar.classList.add("keyboard-hidden");
|
|
84
|
+
} else {
|
|
85
|
+
mobileTabBar.classList.remove("keyboard-hidden");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
89
|
window.visualViewport.addEventListener("resize", onViewportChange);
|
|
80
90
|
window.visualViewport.addEventListener("scroll", onViewportChange);
|
|
@@ -33,6 +33,14 @@ export function initProjectSettings(appCtx, emojiCategories) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Mobile dropdown nav
|
|
37
|
+
var psNavDropdown = document.getElementById("ps-nav-dropdown");
|
|
38
|
+
if (psNavDropdown) {
|
|
39
|
+
psNavDropdown.addEventListener("change", function () {
|
|
40
|
+
switchSection(psNavDropdown.value);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
// Close button
|
|
37
45
|
var closeBtn = document.getElementById("project-settings-close");
|
|
38
46
|
if (closeBtn) {
|
|
@@ -201,6 +209,12 @@ function switchSection(name) {
|
|
|
201
209
|
sections[j].classList.toggle("active", active2);
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
// Sync mobile dropdown
|
|
213
|
+
var psNavDropdown = document.getElementById("ps-nav-dropdown");
|
|
214
|
+
if (psNavDropdown && psNavDropdown.value !== name) {
|
|
215
|
+
psNavDropdown.value = name;
|
|
216
|
+
}
|
|
217
|
+
|
|
204
218
|
// Lazy-load section data
|
|
205
219
|
if (name === "defaults") populateDefaults();
|
|
206
220
|
if (name === "instructions") loadInstructions();
|
|
@@ -155,6 +155,17 @@ export function initServerSettings(appCtx) {
|
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// Image retention select
|
|
159
|
+
var imageRetentionSelect = document.getElementById("settings-image-retention");
|
|
160
|
+
if (imageRetentionSelect) {
|
|
161
|
+
imageRetentionSelect.addEventListener("change", function () {
|
|
162
|
+
var ws = ctx.ws;
|
|
163
|
+
if (ws && ws.readyState === 1) {
|
|
164
|
+
ws.send(JSON.stringify({ type: "set_image_retention", days: parseInt(this.value, 10) }));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
158
169
|
// Global CLAUDE.md: save button
|
|
159
170
|
var ssClaudeMdSave = document.getElementById("ss-claudemd-save");
|
|
160
171
|
if (ssClaudeMdSave) {
|
|
@@ -468,6 +479,12 @@ export function updateDaemonConfig(config) {
|
|
|
468
479
|
var keepAwakeToggle = document.getElementById("settings-keep-awake");
|
|
469
480
|
if (keepAwakeToggle) keepAwakeToggle.checked = !!config.keepAwake;
|
|
470
481
|
|
|
482
|
+
// Image retention
|
|
483
|
+
var imageRetentionSelect = document.getElementById("settings-image-retention");
|
|
484
|
+
if (imageRetentionSelect && config.imageRetentionDays !== undefined) {
|
|
485
|
+
imageRetentionSelect.value = String(config.imageRetentionDays);
|
|
486
|
+
}
|
|
487
|
+
|
|
471
488
|
// Early Access toggle
|
|
472
489
|
var channelToggle = document.getElementById("settings-update-channel");
|
|
473
490
|
if (channelToggle) {
|