clay-server 2.39.0 → 2.40.0-beta.1
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/public/app.js +4 -0
- package/lib/public/css/user-settings.css +118 -30
- package/lib/public/index.html +7 -19
- package/lib/public/modules/app-panels.js +1 -0
- package/lib/public/modules/header-tui-font.js +233 -0
- package/lib/public/modules/session-tui-view.js +4 -0
- package/lib/public/modules/user-settings.js +8 -65
- package/lib/yoke/adapters/codex.js +6 -5
- package/package.json +1 -1
package/lib/public/app.js
CHANGED
|
@@ -65,6 +65,7 @@ import { initLoopWizard, openRalphWizard as _loopOpenRalphWizard, closeRalphWiza
|
|
|
65
65
|
import { initAppNotifications, handleNotificationsState as _notifHandleState, handleNotificationCreated as _notifHandleCreated, handleNotificationDismissed as _notifHandleDismissed, handleNotificationDismissedAll as _notifHandleDismissedAll } from './modules/app-notifications.js';
|
|
66
66
|
import { initWhatsNew, handleWhatsNewState as _wnHandleState, handleWhatsNewSeenResult as _wnHandleSeenResult } from './modules/whats-new.js';
|
|
67
67
|
import { initWhatsNewArticle, openArticle as openWhatsNewArticle } from './modules/whats-new-article.js';
|
|
68
|
+
import { initHeaderTuiFont } from './modules/header-tui-font.js';
|
|
68
69
|
import { createStore, store } from './modules/store.js';
|
|
69
70
|
import { initPanels, updateConfigChip as _panUpdateConfigChip, getModelEffortLevels as _panGetModelEffortLevels, accumulateUsage as _panAccumulateUsage, updateUsagePanel as _panUpdateUsagePanel, resetUsage as _panResetUsage, toggleUsagePanel as _panToggleUsagePanel, formatTokens as _panFormatTokens, updateStatusPanel as _panUpdateStatusPanel, requestProcessStats as _panRequestProcessStats, toggleStatusPanel as _panToggleStatusPanel, accumulateContext as _panAccumulateContext, updateContextPanel as _panUpdateContextPanel, resetContext as _panResetContext, resetContextData as _panResetContextData, minimizeContext as _panMinimizeContext, expandContext as _panExpandContext, toggleContextPanel as _panToggleContextPanel, getContextView as _panGetContextView, renderCtxPopover as _panRenderCtxPopover, hideCtxPopover as _panHideCtxPopover, formatBytes as _panFormatBytes, formatUptime as _panFormatUptime, getModelSupportsEffort as _panGetModelSupportsEffort, getSessionUsage, setSessionUsage, getContextData, setContextData, setContextView as _panSetContextView, applyContextView as _panApplyContextView } from './modules/app-panels.js';
|
|
70
71
|
import { initProjects, updateProjectList as _projUpdateProjectList, renderProjectList as _projRenderProjectList, renderTopbarPresence as _projRenderTopbarPresence, switchProject as _projSwitchProject, resetClientState as _projResetClientState, confirmRemoveProject as _projConfirmRemoveProject, handleRemoveProjectCheckResult as _projHandleRemoveProjectCheckResult, handleRemoveProjectResult as _projHandleRemoveProjectResult, openAddProjectModal as _projOpenAddProjectModal, closeAddProjectModal as _projCloseAddProjectModal, handleBrowseDirResult as _projHandleBrowseDirResult, handleAddProjectResult as _projHandleAddProjectResult, handleCloneProgress as _projHandleCloneProgress, showUpdateAvailable as _projShowUpdateAvailable, getCachedProjects, setCachedProjects, getCachedProjectCount, getCachedRemovedProjects, setCachedRemovedProjects } from './modules/app-projects.js';
|
|
@@ -604,6 +605,9 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
604
605
|
// --- Panels module ---
|
|
605
606
|
initPanels();
|
|
606
607
|
|
|
608
|
+
// --- Header TUI font controls (visible only when TUI session active) ---
|
|
609
|
+
initHeaderTuiFont();
|
|
610
|
+
|
|
607
611
|
// --- Rendering module ---
|
|
608
612
|
initRendering();
|
|
609
613
|
|
|
@@ -860,45 +860,133 @@
|
|
|
860
860
|
}
|
|
861
861
|
}
|
|
862
862
|
|
|
863
|
-
/*
|
|
864
|
-
|
|
863
|
+
/* Header TUI font icon button - single trigger that opens a popover
|
|
864
|
+
with the full font picker + size stepper. Visible only when a TUI
|
|
865
|
+
session is active. Inherits title-bar icon button styling so it
|
|
866
|
+
blends with the existing toolbar. */
|
|
867
|
+
.header-tui-font-btn.hidden { display: none; }
|
|
868
|
+
|
|
869
|
+
/* Floating popover that holds the font menu + size row. Anchored
|
|
870
|
+
under the title-bar button via JS positioning. */
|
|
871
|
+
.header-tui-font-popover {
|
|
872
|
+
position: fixed;
|
|
873
|
+
min-width: 220px;
|
|
874
|
+
max-height: 70vh;
|
|
875
|
+
overflow-y: auto;
|
|
876
|
+
padding: 8px;
|
|
877
|
+
background: var(--bg-alt);
|
|
878
|
+
border: 1px solid var(--border);
|
|
879
|
+
border-radius: 10px;
|
|
880
|
+
box-shadow: 0 8px 28px rgba(var(--shadow-rgb), 0.45);
|
|
881
|
+
z-index: 200;
|
|
882
|
+
opacity: 0;
|
|
883
|
+
transform: translateY(-4px);
|
|
884
|
+
pointer-events: none;
|
|
885
|
+
transition: opacity 0.12s ease, transform 0.12s ease;
|
|
886
|
+
}
|
|
887
|
+
.header-tui-font-popover.visible {
|
|
888
|
+
opacity: 1;
|
|
889
|
+
transform: translateY(0);
|
|
890
|
+
pointer-events: auto;
|
|
891
|
+
}
|
|
892
|
+
.header-tui-font-popover-label {
|
|
893
|
+
padding: 4px 8px 6px;
|
|
894
|
+
font-size: 11px;
|
|
895
|
+
font-weight: 600;
|
|
896
|
+
color: var(--text-dimmer);
|
|
897
|
+
text-transform: uppercase;
|
|
898
|
+
letter-spacing: 0.04em;
|
|
899
|
+
}
|
|
900
|
+
.header-tui-font-popover-list {
|
|
901
|
+
display: flex;
|
|
902
|
+
flex-direction: column;
|
|
903
|
+
gap: 1px;
|
|
904
|
+
}
|
|
905
|
+
.header-tui-font-popover-item {
|
|
865
906
|
display: flex;
|
|
866
907
|
align-items: center;
|
|
867
908
|
gap: 8px;
|
|
868
|
-
|
|
869
|
-
}
|
|
870
|
-
.us-term-font-family {
|
|
871
|
-
flex: 1 1 auto;
|
|
872
|
-
min-width: 180px;
|
|
909
|
+
width: 100%;
|
|
873
910
|
padding: 7px 10px;
|
|
874
|
-
border:
|
|
875
|
-
|
|
876
|
-
background: var(--input-bg);
|
|
911
|
+
border: none;
|
|
912
|
+
background: transparent;
|
|
877
913
|
color: var(--text);
|
|
878
914
|
font-size: 13px;
|
|
915
|
+
text-align: left;
|
|
916
|
+
cursor: pointer;
|
|
917
|
+
border-radius: 6px;
|
|
879
918
|
}
|
|
880
|
-
.
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
919
|
+
.header-tui-font-popover-item:hover {
|
|
920
|
+
background: rgba(var(--overlay-rgb), 0.06);
|
|
921
|
+
}
|
|
922
|
+
.header-tui-font-popover-check {
|
|
923
|
+
display: inline-flex;
|
|
924
|
+
width: 14px;
|
|
925
|
+
height: 14px;
|
|
926
|
+
align-items: center;
|
|
927
|
+
justify-content: center;
|
|
928
|
+
color: var(--accent);
|
|
929
|
+
opacity: 0;
|
|
930
|
+
flex-shrink: 0;
|
|
931
|
+
}
|
|
932
|
+
.header-tui-font-popover-item.active .header-tui-font-popover-check { opacity: 1; }
|
|
933
|
+
.header-tui-font-popover-item-label {
|
|
934
|
+
flex: 1 1 auto;
|
|
935
|
+
overflow: hidden;
|
|
936
|
+
text-overflow: ellipsis;
|
|
937
|
+
white-space: nowrap;
|
|
938
|
+
}
|
|
939
|
+
.header-tui-font-popover-divider {
|
|
940
|
+
height: 1px;
|
|
941
|
+
background: var(--border-subtle);
|
|
942
|
+
margin: 6px 4px;
|
|
943
|
+
}
|
|
944
|
+
.header-tui-font-popover-size {
|
|
945
|
+
display: flex;
|
|
946
|
+
align-items: center;
|
|
947
|
+
justify-content: space-between;
|
|
948
|
+
gap: 12px;
|
|
949
|
+
padding: 4px 10px 4px;
|
|
950
|
+
}
|
|
951
|
+
.header-tui-font-popover-size-label {
|
|
887
952
|
font-size: 13px;
|
|
888
|
-
|
|
953
|
+
color: var(--text-secondary);
|
|
889
954
|
}
|
|
890
|
-
.
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
border: 1px solid var(--border);
|
|
895
|
-
border-radius:
|
|
896
|
-
|
|
955
|
+
.header-tui-font-popover-size-stepper {
|
|
956
|
+
display: inline-flex;
|
|
957
|
+
align-items: center;
|
|
958
|
+
height: 26px;
|
|
959
|
+
border: 1px solid var(--border-subtle);
|
|
960
|
+
border-radius: 6px;
|
|
961
|
+
overflow: hidden;
|
|
962
|
+
}
|
|
963
|
+
.header-tui-font-popover-size-stepper button {
|
|
964
|
+
width: 26px;
|
|
965
|
+
height: 24px;
|
|
966
|
+
border: none;
|
|
967
|
+
background: transparent;
|
|
968
|
+
color: var(--text-secondary);
|
|
969
|
+
font-size: 14px;
|
|
970
|
+
font-weight: 600;
|
|
971
|
+
cursor: pointer;
|
|
972
|
+
display: inline-flex;
|
|
973
|
+
align-items: center;
|
|
974
|
+
justify-content: center;
|
|
975
|
+
padding: 0;
|
|
976
|
+
}
|
|
977
|
+
.header-tui-font-popover-size-stepper button:hover {
|
|
978
|
+
background: rgba(var(--overlay-rgb), 0.06);
|
|
897
979
|
color: var(--text);
|
|
898
|
-
font-size: 13px;
|
|
899
|
-
text-align: center;
|
|
900
980
|
}
|
|
901
|
-
.
|
|
902
|
-
|
|
903
|
-
|
|
981
|
+
.header-tui-font-popover-size-stepper button:disabled {
|
|
982
|
+
opacity: 0.35;
|
|
983
|
+
cursor: default;
|
|
984
|
+
}
|
|
985
|
+
.header-tui-font-popover-size-val {
|
|
986
|
+
min-width: 28px;
|
|
987
|
+
text-align: center;
|
|
988
|
+
font-size: 12px;
|
|
989
|
+
color: var(--text);
|
|
990
|
+
font-variant-numeric: tabular-nums;
|
|
991
|
+
user-select: none;
|
|
904
992
|
}
|
package/lib/public/index.html
CHANGED
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
19
19
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
20
20
|
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500;700&family=Nunito:wght@700;800&display=swap" rel="stylesheet">
|
|
21
|
+
<!-- Monospace web fonts used by the TUI font picker in the title bar.
|
|
22
|
+
SF Mono is Apple-system-only (no Google Fonts entry) and
|
|
23
|
+
ui-monospace is a system keyword - both rely on the user's OS. -->
|
|
24
|
+
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Fira+Code:wght@400;500;700&family=Cascadia+Code:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500;700&family=Source+Code+Pro:wght@400;500;700&display=swap" rel="stylesheet">
|
|
21
25
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5/css/xterm.min.css">
|
|
22
26
|
<script>
|
|
23
27
|
(function(){try{var k="clay-theme-vars",v=localStorage.getItem(k),r=document.documentElement;if(v){var o=JSON.parse(v),p;for(p in o)r.style.setProperty(p,o[p]);var vt=localStorage.getItem(k.replace("-vars","-variant"));if(vt==="light"){r.classList.add("light-theme");r.classList.remove("dark-theme")}else{r.classList.add("dark-theme");r.classList.remove("light-theme")}var m=document.querySelector('meta[name="theme-color"]');if(m&&o["--bg"])m.setAttribute("content",o["--bg"])}else{var sl=window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches;if(sl){r.classList.add("light-theme");r.classList.remove("dark-theme")}}}catch(e){}})();
|
|
@@ -367,6 +371,9 @@
|
|
|
367
371
|
<div id="ralph-sticky" class="hidden"></div>
|
|
368
372
|
<div id="debate-sticky" class="hidden"></div>
|
|
369
373
|
<div class="status">
|
|
374
|
+
<button type="button" id="header-tui-font-btn" class="header-tui-font-btn hidden" title="Terminal font" aria-label="Terminal font">
|
|
375
|
+
<i data-lucide="type"></i>
|
|
376
|
+
</button>
|
|
370
377
|
<button id="debate-pdf-btn" class="hidden" title="Export debate as PDF"><i data-lucide="download"></i></button>
|
|
371
378
|
<button id="find-in-session-btn" title="Search in session (Ctrl+F)"><i data-lucide="search"></i></button>
|
|
372
379
|
<button id="terminal-toggle-btn" title="Terminal"><i data-lucide="square-terminal"></i><span id="terminal-count" class="hidden"></span></button>
|
|
@@ -1001,25 +1008,6 @@
|
|
|
1001
1008
|
<!-- Appearance section -->
|
|
1002
1009
|
<div class="us-section" data-section="us-appearance">
|
|
1003
1010
|
<h2>Appearance</h2>
|
|
1004
|
-
<div class="settings-card">
|
|
1005
|
-
<div class="settings-label" style="margin-bottom:10px;">Terminal font</div>
|
|
1006
|
-
<div class="us-term-font-row">
|
|
1007
|
-
<select id="us-term-font-family" class="us-term-font-family">
|
|
1008
|
-
<option value="'SF Mono', Menlo, Monaco, 'Courier New', monospace">SF Mono / Menlo (default)</option>
|
|
1009
|
-
<option value="'JetBrains Mono', 'SF Mono', Menlo, monospace">JetBrains Mono</option>
|
|
1010
|
-
<option value="'Fira Code', 'SF Mono', Menlo, monospace">Fira Code</option>
|
|
1011
|
-
<option value="'Cascadia Code', 'SF Mono', Menlo, monospace">Cascadia Code</option>
|
|
1012
|
-
<option value="'IBM Plex Mono', 'SF Mono', Menlo, monospace">IBM Plex Mono</option>
|
|
1013
|
-
<option value="'Source Code Pro', 'SF Mono', Menlo, monospace">Source Code Pro</option>
|
|
1014
|
-
<option value="'Roboto Mono', 'SF Mono', Menlo, monospace">Roboto Mono</option>
|
|
1015
|
-
<option value="ui-monospace, monospace">System monospace</option>
|
|
1016
|
-
<option value="__custom__">Custom...</option>
|
|
1017
|
-
</select>
|
|
1018
|
-
<input type="text" id="us-term-font-family-custom" class="us-term-font-custom hidden" placeholder="e.g. 'My Font', monospace" maxlength="200">
|
|
1019
|
-
<input type="number" id="us-term-font-size" class="us-term-font-size" min="9" max="32" step="1">
|
|
1020
|
-
<span class="us-term-font-unit">px</span>
|
|
1021
|
-
</div>
|
|
1022
|
-
</div>
|
|
1023
1011
|
<div class="settings-card">
|
|
1024
1012
|
<div class="settings-label" style="margin-bottom:10px;">Chat layout</div>
|
|
1025
1013
|
<div class="layout-switcher" id="us-layout-switcher">
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// header-tui-font.js
|
|
2
|
+
//
|
|
3
|
+
// Single icon button in the title bar (visible only while a TUI session
|
|
4
|
+
// is active) that opens a popover with terminal font settings: family
|
|
5
|
+
// picker (with per-item font preview) + size stepper. Values live in
|
|
6
|
+
// terminal-prefs.js and persist server-side via
|
|
7
|
+
// PUT /api/user/terminal-font.
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
applyTerminalFont,
|
|
11
|
+
getTerminalFontFamily,
|
|
12
|
+
getTerminalFontSize,
|
|
13
|
+
onTerminalFontChange,
|
|
14
|
+
} from './terminal-prefs.js';
|
|
15
|
+
|
|
16
|
+
var FONT_OPTIONS = [
|
|
17
|
+
{ label: "SF Mono", family: "'SF Mono', Menlo, Monaco, 'Courier New', monospace" },
|
|
18
|
+
{ label: "JetBrains Mono", family: "'JetBrains Mono', 'SF Mono', Menlo, monospace" },
|
|
19
|
+
{ label: "Fira Code", family: "'Fira Code', 'SF Mono', Menlo, monospace" },
|
|
20
|
+
{ label: "Cascadia Code", family: "'Cascadia Code', 'SF Mono', Menlo, monospace" },
|
|
21
|
+
{ label: "IBM Plex Mono", family: "'IBM Plex Mono', 'SF Mono', Menlo, monospace" },
|
|
22
|
+
{ label: "Source Code Pro", family: "'Source Code Pro', 'SF Mono', Menlo, monospace" },
|
|
23
|
+
{ label: "Roboto Mono", family: "'Roboto Mono', 'SF Mono', Menlo, monospace" },
|
|
24
|
+
{ label: "System mono", family: "ui-monospace, monospace" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
var MIN_SIZE = 9;
|
|
28
|
+
var MAX_SIZE = 32;
|
|
29
|
+
|
|
30
|
+
var btnEl = null;
|
|
31
|
+
var popoverEl = null;
|
|
32
|
+
var sizeValEl = null;
|
|
33
|
+
var sizeDecEl = null;
|
|
34
|
+
var sizeIncEl = null;
|
|
35
|
+
var menuItemEls = [];
|
|
36
|
+
var popoverOpen = false;
|
|
37
|
+
var outsideHandler = null;
|
|
38
|
+
var keyHandler = null;
|
|
39
|
+
var initialized = false;
|
|
40
|
+
|
|
41
|
+
function escapeHtml(s) {
|
|
42
|
+
return String(s)
|
|
43
|
+
.replace(/&/g, '&')
|
|
44
|
+
.replace(/</g, '<')
|
|
45
|
+
.replace(/>/g, '>')
|
|
46
|
+
.replace(/"/g, '"');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function persistTermFont(family, size) {
|
|
50
|
+
fetch('/api/user/terminal-font', {
|
|
51
|
+
method: 'PUT',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify({ family: family, size: size }),
|
|
54
|
+
}).catch(function () {});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function syncSize() {
|
|
58
|
+
if (!sizeValEl) return;
|
|
59
|
+
var sz = getTerminalFontSize();
|
|
60
|
+
sizeValEl.textContent = String(sz);
|
|
61
|
+
if (sizeDecEl) sizeDecEl.disabled = sz <= MIN_SIZE;
|
|
62
|
+
if (sizeIncEl) sizeIncEl.disabled = sz >= MAX_SIZE;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function syncActiveFamily() {
|
|
66
|
+
var fam = getTerminalFontFamily();
|
|
67
|
+
for (var i = 0; i < menuItemEls.length; i++) {
|
|
68
|
+
var el = menuItemEls[i];
|
|
69
|
+
el.classList.toggle('active', el.dataset.family === fam);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function buildPopover() {
|
|
74
|
+
if (popoverEl) return popoverEl;
|
|
75
|
+
popoverEl = document.createElement('div');
|
|
76
|
+
popoverEl.className = 'header-tui-font-popover';
|
|
77
|
+
|
|
78
|
+
var label = document.createElement('div');
|
|
79
|
+
label.className = 'header-tui-font-popover-label';
|
|
80
|
+
label.textContent = 'Font';
|
|
81
|
+
popoverEl.appendChild(label);
|
|
82
|
+
|
|
83
|
+
var list = document.createElement('div');
|
|
84
|
+
list.className = 'header-tui-font-popover-list';
|
|
85
|
+
for (var i = 0; i < FONT_OPTIONS.length; i++) {
|
|
86
|
+
var opt = FONT_OPTIONS[i];
|
|
87
|
+
var item = document.createElement('button');
|
|
88
|
+
item.type = 'button';
|
|
89
|
+
item.className = 'header-tui-font-popover-item';
|
|
90
|
+
item.dataset.family = opt.family;
|
|
91
|
+
item.style.fontFamily = opt.family;
|
|
92
|
+
item.innerHTML =
|
|
93
|
+
'<span class="header-tui-font-popover-check"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></span>' +
|
|
94
|
+
'<span class="header-tui-font-popover-item-label">' + escapeHtml(opt.label) + '</span>';
|
|
95
|
+
(function (family) {
|
|
96
|
+
item.addEventListener('click', function (e) {
|
|
97
|
+
e.stopPropagation();
|
|
98
|
+
applyTerminalFont(family, undefined);
|
|
99
|
+
persistTermFont(family, undefined);
|
|
100
|
+
});
|
|
101
|
+
})(opt.family);
|
|
102
|
+
list.appendChild(item);
|
|
103
|
+
menuItemEls.push(item);
|
|
104
|
+
}
|
|
105
|
+
popoverEl.appendChild(list);
|
|
106
|
+
|
|
107
|
+
var divider = document.createElement('div');
|
|
108
|
+
divider.className = 'header-tui-font-popover-divider';
|
|
109
|
+
popoverEl.appendChild(divider);
|
|
110
|
+
|
|
111
|
+
var sizeRow = document.createElement('div');
|
|
112
|
+
sizeRow.className = 'header-tui-font-popover-size';
|
|
113
|
+
sizeRow.innerHTML =
|
|
114
|
+
'<span class="header-tui-font-popover-size-label">Size</span>' +
|
|
115
|
+
'<div class="header-tui-font-popover-size-stepper">' +
|
|
116
|
+
'<button type="button" data-step="-1" title="Smaller">−</button>' +
|
|
117
|
+
'<span class="header-tui-font-popover-size-val">13</span>' +
|
|
118
|
+
'<button type="button" data-step="1" title="Larger">+</button>' +
|
|
119
|
+
'</div>';
|
|
120
|
+
popoverEl.appendChild(sizeRow);
|
|
121
|
+
|
|
122
|
+
sizeValEl = sizeRow.querySelector('.header-tui-font-popover-size-val');
|
|
123
|
+
var stepBtns = sizeRow.querySelectorAll('button');
|
|
124
|
+
sizeDecEl = stepBtns[0];
|
|
125
|
+
sizeIncEl = stepBtns[1];
|
|
126
|
+
sizeDecEl.addEventListener('click', function (e) {
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
var next = Math.max(MIN_SIZE, getTerminalFontSize() - 1);
|
|
129
|
+
applyTerminalFont(undefined, next);
|
|
130
|
+
persistTermFont(undefined, next);
|
|
131
|
+
});
|
|
132
|
+
sizeIncEl.addEventListener('click', function (e) {
|
|
133
|
+
e.stopPropagation();
|
|
134
|
+
var next = Math.min(MAX_SIZE, getTerminalFontSize() + 1);
|
|
135
|
+
applyTerminalFont(undefined, next);
|
|
136
|
+
persistTermFont(undefined, next);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
document.body.appendChild(popoverEl);
|
|
140
|
+
return popoverEl;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function positionPopover() {
|
|
144
|
+
if (!popoverEl || !btnEl) return;
|
|
145
|
+
var r = btnEl.getBoundingClientRect();
|
|
146
|
+
popoverEl.style.left = '0px';
|
|
147
|
+
popoverEl.style.top = '0px';
|
|
148
|
+
var pw = popoverEl.offsetWidth || 220;
|
|
149
|
+
var ph = popoverEl.offsetHeight || 340;
|
|
150
|
+
var vw = window.innerWidth;
|
|
151
|
+
var vh = window.innerHeight;
|
|
152
|
+
var left = r.right - pw;
|
|
153
|
+
if (left < 8) left = 8;
|
|
154
|
+
if (left + pw > vw - 8) left = vw - pw - 8;
|
|
155
|
+
var top = r.bottom + 8;
|
|
156
|
+
if (top + ph > vh - 8) {
|
|
157
|
+
var flipped = r.top - ph - 8;
|
|
158
|
+
if (flipped >= 8) top = flipped;
|
|
159
|
+
else top = Math.max(8, vh - ph - 8);
|
|
160
|
+
}
|
|
161
|
+
popoverEl.style.left = left + 'px';
|
|
162
|
+
popoverEl.style.top = top + 'px';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function openPopover() {
|
|
166
|
+
if (popoverOpen) return;
|
|
167
|
+
buildPopover();
|
|
168
|
+
syncActiveFamily();
|
|
169
|
+
syncSize();
|
|
170
|
+
popoverEl.classList.add('visible');
|
|
171
|
+
positionPopover();
|
|
172
|
+
popoverOpen = true;
|
|
173
|
+
if (btnEl) btnEl.classList.add('active');
|
|
174
|
+
|
|
175
|
+
outsideHandler = function (ev) {
|
|
176
|
+
if (!popoverEl) return;
|
|
177
|
+
if (popoverEl.contains(ev.target)) return;
|
|
178
|
+
if (btnEl && btnEl.contains(ev.target)) return;
|
|
179
|
+
closePopover();
|
|
180
|
+
};
|
|
181
|
+
document.addEventListener('mousedown', outsideHandler, true);
|
|
182
|
+
|
|
183
|
+
keyHandler = function (ev) {
|
|
184
|
+
if (ev.key === 'Escape') closePopover();
|
|
185
|
+
};
|
|
186
|
+
document.addEventListener('keydown', keyHandler, true);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function closePopover() {
|
|
190
|
+
if (!popoverOpen) return;
|
|
191
|
+
if (popoverEl) popoverEl.classList.remove('visible');
|
|
192
|
+
if (btnEl) btnEl.classList.remove('active');
|
|
193
|
+
popoverOpen = false;
|
|
194
|
+
if (outsideHandler) {
|
|
195
|
+
document.removeEventListener('mousedown', outsideHandler, true);
|
|
196
|
+
outsideHandler = null;
|
|
197
|
+
}
|
|
198
|
+
if (keyHandler) {
|
|
199
|
+
document.removeEventListener('keydown', keyHandler, true);
|
|
200
|
+
keyHandler = null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function initHeaderTuiFont() {
|
|
205
|
+
if (initialized) return;
|
|
206
|
+
btnEl = document.getElementById('header-tui-font-btn');
|
|
207
|
+
if (!btnEl) return;
|
|
208
|
+
btnEl.addEventListener('click', function (e) {
|
|
209
|
+
e.stopPropagation();
|
|
210
|
+
if (popoverOpen) closePopover();
|
|
211
|
+
else openPopover();
|
|
212
|
+
});
|
|
213
|
+
// Reflect external changes back into the popover (re-renders only if
|
|
214
|
+
// it's currently open).
|
|
215
|
+
onTerminalFontChange(function () {
|
|
216
|
+
if (popoverOpen) {
|
|
217
|
+
syncActiveFamily();
|
|
218
|
+
syncSize();
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
initialized = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function showHeaderTuiFont() {
|
|
225
|
+
if (!btnEl) return;
|
|
226
|
+
btnEl.classList.remove('hidden');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function hideHeaderTuiFont() {
|
|
230
|
+
closePopover();
|
|
231
|
+
if (!btnEl) return;
|
|
232
|
+
btnEl.classList.add('hidden');
|
|
233
|
+
}
|
|
@@ -18,6 +18,7 @@ import { getWs } from './ws-ref.js';
|
|
|
18
18
|
import { store } from './store.js';
|
|
19
19
|
import { getTerminalTheme } from './theme.js';
|
|
20
20
|
import { getTerminalFontFamily, getTerminalFontSize, onTerminalFontChange } from './terminal-prefs.js';
|
|
21
|
+
import { showHeaderTuiFont, hideHeaderTuiFont } from './header-tui-font.js';
|
|
21
22
|
import { openArticle as openWhatsNewArticle } from './whats-new-article.js';
|
|
22
23
|
|
|
23
24
|
// Stable id of the canonical "Why TUI mode?" article in
|
|
@@ -377,6 +378,7 @@ export function attachTuiView(terminalId) {
|
|
|
377
378
|
if (currentTermId === terminalId && xterm) {
|
|
378
379
|
if (hostEl) hostEl.style.display = "flex";
|
|
379
380
|
hideGuiChrome(true);
|
|
381
|
+
showHeaderTuiFont();
|
|
380
382
|
fitNow();
|
|
381
383
|
try { xterm.focus(); } catch (e) {}
|
|
382
384
|
return;
|
|
@@ -388,6 +390,7 @@ export function attachTuiView(terminalId) {
|
|
|
388
390
|
if (!ensureHostEl()) return;
|
|
389
391
|
hostEl.style.display = "flex";
|
|
390
392
|
hideGuiChrome(true);
|
|
393
|
+
showHeaderTuiFont();
|
|
391
394
|
syncHostBounds();
|
|
392
395
|
|
|
393
396
|
currentTermId = terminalId;
|
|
@@ -437,6 +440,7 @@ export function detachTuiView() {
|
|
|
437
440
|
teardownXterm();
|
|
438
441
|
if (hostEl) hostEl.style.display = "none";
|
|
439
442
|
hideGuiChrome(false);
|
|
443
|
+
hideHeaderTuiFont();
|
|
440
444
|
}
|
|
441
445
|
|
|
442
446
|
// Route a term_output frame to the embedded xterm if it belongs to the
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { refreshIcons } from './icons.js';
|
|
5
5
|
import { showToast } from './utils.js';
|
|
6
6
|
import { getChatLayout, setChatLayout } from './theme.js';
|
|
7
|
-
import { applyTerminalFont
|
|
7
|
+
import { applyTerminalFont } from './terminal-prefs.js';
|
|
8
8
|
import { showEmailSetupModal, getEmailAccountListCache } from './context-sources.js';
|
|
9
9
|
import { setSTTLang } from './stt.js';
|
|
10
10
|
import { userAvatarUrl } from './avatar.js';
|
|
@@ -234,47 +234,10 @@ export function initUserSettings(appCtx) {
|
|
|
234
234
|
// Theme picker lives on the user island button now (palette icon).
|
|
235
235
|
// No appearance-section mount required.
|
|
236
236
|
|
|
237
|
-
// Terminal font
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
var tfCustom = document.getElementById('us-term-font-family-custom');
|
|
242
|
-
var tfSize = document.getElementById('us-term-font-size');
|
|
243
|
-
|
|
244
|
-
function persistTermFont(family, size) {
|
|
245
|
-
fetch('/api/user/terminal-font', {
|
|
246
|
-
method: 'PUT',
|
|
247
|
-
headers: { 'Content-Type': 'application/json' },
|
|
248
|
-
body: JSON.stringify({ family: family, size: size }),
|
|
249
|
-
}).catch(function () {});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (tfSelect && tfCustom && tfSize) {
|
|
253
|
-
tfSelect.addEventListener('change', function () {
|
|
254
|
-
if (tfSelect.value === '__custom__') {
|
|
255
|
-
tfCustom.classList.remove('hidden');
|
|
256
|
-
tfCustom.focus();
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
tfCustom.classList.add('hidden');
|
|
260
|
-
applyTerminalFont(tfSelect.value, undefined);
|
|
261
|
-
persistTermFont(tfSelect.value, undefined);
|
|
262
|
-
});
|
|
263
|
-
tfCustom.addEventListener('change', function () {
|
|
264
|
-
var v = tfCustom.value.trim();
|
|
265
|
-
if (!v) return;
|
|
266
|
-
applyTerminalFont(v, undefined);
|
|
267
|
-
persistTermFont(v, undefined);
|
|
268
|
-
});
|
|
269
|
-
tfSize.addEventListener('change', function () {
|
|
270
|
-
var n = Number(tfSize.value);
|
|
271
|
-
if (!isFinite(n)) return;
|
|
272
|
-
n = Math.max(9, Math.min(32, Math.round(n)));
|
|
273
|
-
tfSize.value = String(n);
|
|
274
|
-
applyTerminalFont(undefined, n);
|
|
275
|
-
persistTermFont(undefined, n);
|
|
276
|
-
});
|
|
277
|
-
}
|
|
237
|
+
// Terminal font picker lives in the title bar (visible only while a
|
|
238
|
+
// TUI session is active). See lib/public/modules/header-tui-font.js.
|
|
239
|
+
// We still seed terminal-prefs from /api/profile below so the
|
|
240
|
+
// controls populate correctly when they appear.
|
|
278
241
|
|
|
279
242
|
// Layout switcher (Bubble / Channel)
|
|
280
243
|
var layoutSwitcher = document.getElementById('us-layout-switcher');
|
|
@@ -489,32 +452,12 @@ function populateAccount() {
|
|
|
489
452
|
// Hide account section in single-user mode (no username)
|
|
490
453
|
var accountNav = settingsEl.querySelector('[data-section="us-account"]');
|
|
491
454
|
if (accountNav) accountNav.style.display = data.username ? '' : 'none';
|
|
492
|
-
// Terminal font: seed in-memory prefs from server
|
|
455
|
+
// Terminal font: seed in-memory prefs from server. Header-mounted
|
|
456
|
+
// controls (header-tui-font.js) listen to applyTerminalFont and
|
|
457
|
+
// sync their UI - we don't touch any DOM here.
|
|
493
458
|
if (data.terminalFont && typeof data.terminalFont === "object") {
|
|
494
459
|
applyTerminalFont(data.terminalFont.family, data.terminalFont.size);
|
|
495
460
|
}
|
|
496
|
-
var tfSelectEl = document.getElementById('us-term-font-family');
|
|
497
|
-
var tfCustomEl = document.getElementById('us-term-font-family-custom');
|
|
498
|
-
var tfSizeEl = document.getElementById('us-term-font-size');
|
|
499
|
-
if (tfSelectEl && tfCustomEl) {
|
|
500
|
-
var fam = getTerminalFontFamily();
|
|
501
|
-
var matched = false;
|
|
502
|
-
for (var oi = 0; oi < tfSelectEl.options.length; oi++) {
|
|
503
|
-
if (tfSelectEl.options[oi].value === fam) {
|
|
504
|
-
tfSelectEl.value = fam;
|
|
505
|
-
matched = true;
|
|
506
|
-
break;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
if (!matched) {
|
|
510
|
-
tfSelectEl.value = '__custom__';
|
|
511
|
-
tfCustomEl.value = fam;
|
|
512
|
-
tfCustomEl.classList.remove('hidden');
|
|
513
|
-
} else {
|
|
514
|
-
tfCustomEl.classList.add('hidden');
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (tfSizeEl) tfSizeEl.value = String(getTerminalFontSize());
|
|
518
461
|
// Auto-continue toggle
|
|
519
462
|
var acToggle = document.getElementById('us-auto-continue');
|
|
520
463
|
if (acToggle) acToggle.checked = !!data.autoContinueOnRateLimit;
|
|
@@ -586,7 +586,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
586
586
|
done: false,
|
|
587
587
|
aborted: false,
|
|
588
588
|
loopStarted: false,
|
|
589
|
-
model: queryOpts.model || "gpt-5.
|
|
589
|
+
model: queryOpts.model || "gpt-5.5",
|
|
590
590
|
// Track incremental text deltas
|
|
591
591
|
textBlocks: {}, // itemId -> true (text_start sent)
|
|
592
592
|
textLengths: {}, // itemId -> last sent length
|
|
@@ -831,7 +831,7 @@ function createCodexQueryHandle(appServer, queryOpts) {
|
|
|
831
831
|
|
|
832
832
|
// Start or resume thread
|
|
833
833
|
var threadParams = {
|
|
834
|
-
model: queryOpts.model || "gpt-5.
|
|
834
|
+
model: queryOpts.model || "gpt-5.5",
|
|
835
835
|
sandbox: queryOpts.sandboxMode || "workspace-write",
|
|
836
836
|
approvalPolicy: queryOpts.approvalPolicy || "on-failure",
|
|
837
837
|
cwd: queryOpts.cwd,
|
|
@@ -1085,7 +1085,7 @@ function createCodexAdapter(opts) {
|
|
|
1085
1085
|
function buildReadyResponse(skillNames) {
|
|
1086
1086
|
return {
|
|
1087
1087
|
models: _cachedModels,
|
|
1088
|
-
defaultModel: "gpt-5.
|
|
1088
|
+
defaultModel: "gpt-5.5",
|
|
1089
1089
|
skills: skillNames || [],
|
|
1090
1090
|
slashCommands: skillNames || [],
|
|
1091
1091
|
fastModeState: null,
|
|
@@ -1297,9 +1297,10 @@ function createCodexAdapter(opts) {
|
|
|
1297
1297
|
throw createShutdownError();
|
|
1298
1298
|
}
|
|
1299
1299
|
|
|
1300
|
-
console.log("[codex] App-server initialized, models: gpt-5.4, gpt-5.4-mini, gpt-5.3-codex, gpt-5.3-codex-spark, gpt-5.2");
|
|
1300
|
+
console.log("[codex] App-server initialized, models: gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex, gpt-5.3-codex-spark, gpt-5.2");
|
|
1301
1301
|
|
|
1302
1302
|
_cachedModels = [
|
|
1303
|
+
"gpt-5.5",
|
|
1303
1304
|
"gpt-5.4",
|
|
1304
1305
|
"gpt-5.4-mini",
|
|
1305
1306
|
"gpt-5.3-codex",
|
|
@@ -1387,7 +1388,7 @@ function createCodexAdapter(opts) {
|
|
|
1387
1388
|
throw new Error("[yoke/codex] Adapter not initialized. Call init() first.");
|
|
1388
1389
|
}
|
|
1389
1390
|
|
|
1390
|
-
var model = queryOpts.model || "gpt-5.
|
|
1391
|
+
var model = queryOpts.model || "gpt-5.5";
|
|
1391
1392
|
var ac = queryOpts.abortController || new AbortController();
|
|
1392
1393
|
var activeEntry = {
|
|
1393
1394
|
abort: function() {
|
package/package.json
CHANGED