iris-chatbot 4.1.0 → 5.0.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/package.json +1 -1
- package/template/docs/plan-projects-sidebar-ui.md +11 -0
- package/template/next-env.d.ts +1 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/src/app/globals.css +264 -10
- package/template/src/app/page.tsx +21 -6
- package/template/src/components/ChatView.tsx +178 -46
- package/template/src/components/Composer.tsx +84 -54
- package/template/src/components/MapView.tsx +21 -2
- package/template/src/components/MessageCard.tsx +1 -1
- package/template/src/components/SearchModal.tsx +1 -1
- package/template/src/components/SettingsModal.tsx +174 -104
- package/template/src/components/Sidebar.tsx +2 -4
- package/template/src/components/TopBar.tsx +4 -10
- package/template/src/lib/data.ts +5 -0
- package/template/src/lib/model-presets.ts +29 -0
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
ensureBuiltinConnections,
|
|
8
8
|
normalizeBaseUrl,
|
|
9
9
|
} from "../lib/connections";
|
|
10
|
+
import { DEFAULT_ACCENT_DARK, DEFAULT_ACCENT_LIGHT } from "../lib/data";
|
|
10
11
|
import { db } from "../lib/db";
|
|
11
12
|
import { useMemories } from "../lib/hooks";
|
|
12
13
|
import { normalizeMemoryKey } from "../lib/memory";
|
|
13
|
-
import { filterModelIdsForConnection, getConnectionModelPresets } from "../lib/model-presets";
|
|
14
|
+
import { filterModelIdsForConnection, getConnectionModelPresets, getModelDisplayLabel } from "../lib/model-presets";
|
|
14
15
|
import {
|
|
15
16
|
DEFAULT_MEMORY_SETTINGS,
|
|
16
17
|
DEFAULT_LOCAL_TOOLS_SETTINGS,
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
type ModelConnection,
|
|
25
26
|
type SafetyProfile,
|
|
26
27
|
type Settings,
|
|
27
|
-
type WebSearchBackend,
|
|
28
28
|
} from "../lib/types";
|
|
29
29
|
|
|
30
30
|
type TabId =
|
|
@@ -112,10 +112,10 @@ export default function SettingsModal({
|
|
|
112
112
|
const [showOpenAIKey, setShowOpenAIKey] = useState(false);
|
|
113
113
|
const [showAnthropicKey, setShowAnthropicKey] = useState(false);
|
|
114
114
|
const [showGeminiKey, setShowGeminiKey] = useState(false);
|
|
115
|
-
const [accentColor, setAccentColor] = useState(settings?.accentColor ||
|
|
115
|
+
const [accentColor, setAccentColor] = useState(settings?.accentColor || DEFAULT_ACCENT_DARK);
|
|
116
116
|
const [theme, setTheme] = useState<"dark" | "light">(settings?.theme || "dark");
|
|
117
117
|
const [font, setFont] = useState<"ibm" | "manrope" | "sora" | "space" | "poppins">(
|
|
118
|
-
settings?.font || "
|
|
118
|
+
settings?.font || "poppins",
|
|
119
119
|
);
|
|
120
120
|
|
|
121
121
|
const localTools = settings?.localTools ?? DEFAULT_LOCAL_TOOLS_SETTINGS;
|
|
@@ -132,8 +132,6 @@ export default function SettingsModal({
|
|
|
132
132
|
const [enableMail, setEnableMail] = useState(localTools.enableMail);
|
|
133
133
|
const [enableWorkflow, setEnableWorkflow] = useState(localTools.enableWorkflow);
|
|
134
134
|
const [enableSystem, setEnableSystem] = useState(localTools.enableSystem);
|
|
135
|
-
const [webSearchBackend, setWebSearchBackend] = useState<WebSearchBackend>(localTools.webSearchBackend);
|
|
136
|
-
const [dryRun, setDryRun] = useState(localTools.dryRun);
|
|
137
135
|
const memory = settings?.memory ?? DEFAULT_MEMORY_SETTINGS;
|
|
138
136
|
const [memoryEnabled, setMemoryEnabled] = useState(memory.enabled);
|
|
139
137
|
const [memoryAutoCapture, setMemoryAutoCapture] = useState(memory.autoCapture);
|
|
@@ -197,8 +195,8 @@ export default function SettingsModal({
|
|
|
197
195
|
|
|
198
196
|
const selectedDefaultModel = selectedDefaultConnection
|
|
199
197
|
? defaultModelByConnection[selectedDefaultConnection.id] ||
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
getConnectionModelPresets(selectedDefaultConnection)[0] ||
|
|
199
|
+
""
|
|
202
200
|
: "";
|
|
203
201
|
const selectedPresets = selectedDefaultConnection ? getConnectionModelPresets(selectedDefaultConnection) : [];
|
|
204
202
|
const selectedFetchedModels = selectedDefaultConnection ? fetchedModels[selectedDefaultConnection.id] ?? [] : [];
|
|
@@ -216,6 +214,49 @@ export default function SettingsModal({
|
|
|
216
214
|
return text.includes(query);
|
|
217
215
|
});
|
|
218
216
|
|
|
217
|
+
const memoryKindLabel: Record<MemoryKind, string> = {
|
|
218
|
+
profile: "Profile",
|
|
219
|
+
preference: "Preference",
|
|
220
|
+
person_alias: "Person alias",
|
|
221
|
+
music_alias: "Music alias",
|
|
222
|
+
note: "Note",
|
|
223
|
+
};
|
|
224
|
+
const memoryScopeLabel: Record<MemoryScope, string> = {
|
|
225
|
+
global: "All chats",
|
|
226
|
+
conversation: "This conversation",
|
|
227
|
+
};
|
|
228
|
+
const memorySourceLabel: Record<MemorySource, string> = {
|
|
229
|
+
auto: "Auto-captured",
|
|
230
|
+
explicit: "Explicit",
|
|
231
|
+
manual: "Manual",
|
|
232
|
+
};
|
|
233
|
+
function formatMemoryValue(value: string): string {
|
|
234
|
+
try {
|
|
235
|
+
const parsed = JSON.parse(value) as Record<string, unknown>;
|
|
236
|
+
if (parsed && typeof parsed === "object") {
|
|
237
|
+
const title = typeof parsed.title === "string" ? parsed.title : "";
|
|
238
|
+
const artist = typeof parsed.artist === "string" ? parsed.artist : "";
|
|
239
|
+
const query = typeof parsed.query === "string" ? parsed.query : "";
|
|
240
|
+
if (title && artist) return `${title} by ${artist}`;
|
|
241
|
+
if (query) return query;
|
|
242
|
+
if (title) return title;
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
// not JSON, use as-is
|
|
246
|
+
}
|
|
247
|
+
return value;
|
|
248
|
+
}
|
|
249
|
+
function formatMemoryMeta(entry: MemoryEntry): string {
|
|
250
|
+
const parts = [
|
|
251
|
+
memoryKindLabel[entry.kind],
|
|
252
|
+
entry.scope === "conversation" && entry.conversationId
|
|
253
|
+
? `${memoryScopeLabel[entry.scope]} (${entry.conversationId})`
|
|
254
|
+
: memoryScopeLabel[entry.scope],
|
|
255
|
+
memorySourceLabel[entry.source],
|
|
256
|
+
];
|
|
257
|
+
return parts.join(" · ");
|
|
258
|
+
}
|
|
259
|
+
|
|
219
260
|
const resetConnectionForm = () => {
|
|
220
261
|
setEditingConnectionId(null);
|
|
221
262
|
setConnectionFormName("");
|
|
@@ -486,7 +527,7 @@ export default function SettingsModal({
|
|
|
486
527
|
defaultModelByConnection: resolvedDefaultModelMap,
|
|
487
528
|
showExtendedOpenAIModels,
|
|
488
529
|
enableWebSources,
|
|
489
|
-
accentColor: accentColor ||
|
|
530
|
+
accentColor: accentColor || DEFAULT_ACCENT_DARK,
|
|
490
531
|
font,
|
|
491
532
|
theme,
|
|
492
533
|
localTools: {
|
|
@@ -504,8 +545,8 @@ export default function SettingsModal({
|
|
|
504
545
|
enableMail,
|
|
505
546
|
enableWorkflow,
|
|
506
547
|
enableSystem,
|
|
507
|
-
webSearchBackend,
|
|
508
|
-
dryRun,
|
|
548
|
+
webSearchBackend: DEFAULT_LOCAL_TOOLS_SETTINGS.webSearchBackend,
|
|
549
|
+
dryRun: localTools.dryRun,
|
|
509
550
|
},
|
|
510
551
|
memory: {
|
|
511
552
|
enabled: memoryEnabled,
|
|
@@ -518,7 +559,7 @@ export default function SettingsModal({
|
|
|
518
559
|
};
|
|
519
560
|
|
|
520
561
|
const accentPresets = [
|
|
521
|
-
{ id: "default", label: "Default", color:
|
|
562
|
+
{ id: "default", label: "Default", color: DEFAULT_ACCENT_DARK },
|
|
522
563
|
{ id: "blue", label: "Blue", color: "#2563eb" },
|
|
523
564
|
{ id: "green", label: "Green", color: "#16a34a" },
|
|
524
565
|
{ id: "yellow", label: "Yellow", color: "#f59e0b" },
|
|
@@ -545,11 +586,10 @@ export default function SettingsModal({
|
|
|
545
586
|
<button
|
|
546
587
|
key={tab.id}
|
|
547
588
|
onClick={() => setActiveTab(tab.id)}
|
|
548
|
-
className={`rounded-full px-3 py-1.5 text-xs ${
|
|
549
|
-
|
|
550
|
-
? "bg-[var(--accent)] text-white"
|
|
589
|
+
className={`rounded-full px-3 py-1.5 text-xs settings-tab ${activeTab === tab.id
|
|
590
|
+
? "settings-tab-active"
|
|
551
591
|
: "border border-[var(--border)] bg-[var(--panel-2)] text-[var(--text-secondary)]"
|
|
552
|
-
|
|
592
|
+
}`}
|
|
553
593
|
>
|
|
554
594
|
{tab.label}
|
|
555
595
|
</button>
|
|
@@ -581,7 +621,7 @@ export default function SettingsModal({
|
|
|
581
621
|
<div className="text-xs uppercase tracking-[0.2em] text-[var(--text-muted)]">
|
|
582
622
|
Default model for {selectedDefaultConnection.name}
|
|
583
623
|
</div>
|
|
584
|
-
<
|
|
624
|
+
<select
|
|
585
625
|
className="w-full rounded-lg border border-[var(--border)] bg-[var(--panel)] px-3 py-2 text-sm"
|
|
586
626
|
value={modelInput || selectedDefaultModel}
|
|
587
627
|
onChange={(event) => {
|
|
@@ -592,10 +632,25 @@ export default function SettingsModal({
|
|
|
592
632
|
[selectedDefaultConnection.id]: value,
|
|
593
633
|
}));
|
|
594
634
|
}}
|
|
595
|
-
|
|
596
|
-
|
|
635
|
+
>
|
|
636
|
+
{selectableModels.length === 0 ? (
|
|
637
|
+
<option value="">No models — fetch from Connections tab</option>
|
|
638
|
+
) : (
|
|
639
|
+
(() => {
|
|
640
|
+
const current = modelInput || selectedDefaultModel;
|
|
641
|
+
const ids = current && !selectableModels.includes(current)
|
|
642
|
+
? [current, ...selectableModels]
|
|
643
|
+
: selectableModels;
|
|
644
|
+
return ids.map((modelId) => (
|
|
645
|
+
<option key={modelId} value={modelId}>
|
|
646
|
+
{getModelDisplayLabel(modelId, selectedDefaultConnection)}
|
|
647
|
+
</option>
|
|
648
|
+
));
|
|
649
|
+
})()
|
|
650
|
+
)}
|
|
651
|
+
</select>
|
|
597
652
|
<div className="flex flex-wrap gap-2">
|
|
598
|
-
{selectableModels.
|
|
653
|
+
{selectableModels.map((preset) => (
|
|
599
654
|
<button
|
|
600
655
|
key={preset}
|
|
601
656
|
className="rounded-full border border-[var(--border)] bg-[var(--panel)] px-3 py-1 text-xs text-[var(--text-secondary)]"
|
|
@@ -607,7 +662,7 @@ export default function SettingsModal({
|
|
|
607
662
|
}));
|
|
608
663
|
}}
|
|
609
664
|
>
|
|
610
|
-
{preset}
|
|
665
|
+
{getModelDisplayLabel(preset, selectedDefaultConnection)}
|
|
611
666
|
</button>
|
|
612
667
|
))}
|
|
613
668
|
{selectableModels.length === 0 ? (
|
|
@@ -617,8 +672,8 @@ export default function SettingsModal({
|
|
|
617
672
|
) : null}
|
|
618
673
|
</div>
|
|
619
674
|
{selectedDefaultConnection?.kind === "builtin" &&
|
|
620
|
-
|
|
621
|
-
<label className="
|
|
675
|
+
selectedDefaultConnection?.provider === "openai" ? (
|
|
676
|
+
<label className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
|
622
677
|
<input
|
|
623
678
|
type="checkbox"
|
|
624
679
|
checked={showExtendedOpenAIModels}
|
|
@@ -766,37 +821,37 @@ export default function SettingsModal({
|
|
|
766
821
|
/>
|
|
767
822
|
Enabled
|
|
768
823
|
</label>
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
824
|
+
<span>
|
|
825
|
+
Tools: {(connection.supportsTools ?? true) ? "supported" : "disabled"}
|
|
826
|
+
</span>
|
|
772
827
|
{connection.kind === "builtin" ? (
|
|
773
828
|
<span>Keys configured in API Keys tab</span>
|
|
774
829
|
) : null}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
830
|
+
{connectionStatus[connection.id] ? <span>{connectionStatus[connection.id]}</span> : null}
|
|
831
|
+
</div>
|
|
832
|
+
</div>
|
|
833
|
+
))}
|
|
834
|
+
</div>
|
|
780
835
|
|
|
781
836
|
<div className="rounded-xl border border-[var(--border)] bg-[var(--panel-2)] p-4">
|
|
782
837
|
<div className="mb-3 text-xs uppercase tracking-[0.2em] text-[var(--text-muted)]">
|
|
783
838
|
{editingConnectionId ? "Edit Connection" : "Add Connection"}
|
|
784
839
|
</div>
|
|
785
|
-
|
|
840
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
786
841
|
<input
|
|
787
842
|
className="rounded-lg border border-[var(--border)] bg-[var(--panel)] px-3 py-2 text-sm"
|
|
788
843
|
placeholder="Connection name"
|
|
789
844
|
value={connectionFormName}
|
|
790
845
|
onChange={(event) => setConnectionFormName(event.target.value)}
|
|
791
846
|
/>
|
|
792
|
-
|
|
847
|
+
<select
|
|
793
848
|
className="rounded-lg border border-[var(--border)] bg-[var(--panel)] px-3 py-2 text-sm"
|
|
794
849
|
value={connectionFormKind}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
850
|
+
onChange={(event) => setConnectionFormKind(event.target.value as ConnectionKind)}
|
|
851
|
+
>
|
|
852
|
+
<option value="openai_compatible">OpenAI-compatible</option>
|
|
853
|
+
<option value="ollama">Ollama</option>
|
|
854
|
+
</select>
|
|
800
855
|
<input
|
|
801
856
|
className="rounded-lg border border-[var(--border)] bg-[var(--panel)] px-3 py-2 text-sm md:col-span-2"
|
|
802
857
|
placeholder="Base URL (e.g. http://localhost:11434)"
|
|
@@ -815,11 +870,11 @@ export default function SettingsModal({
|
|
|
815
870
|
value={connectionFormHeadersText}
|
|
816
871
|
onChange={(event) => setConnectionFormHeadersText(event.target.value)}
|
|
817
872
|
/>
|
|
818
|
-
|
|
873
|
+
</div>
|
|
819
874
|
{connectionStatus.form ? (
|
|
820
875
|
<div className="mt-2 text-xs text-[var(--danger)]">{connectionStatus.form}</div>
|
|
821
876
|
) : null}
|
|
822
|
-
|
|
877
|
+
<div className="mt-3 flex flex-wrap items-center gap-3">
|
|
823
878
|
<label className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
|
824
879
|
<input
|
|
825
880
|
type="checkbox"
|
|
@@ -852,7 +907,7 @@ export default function SettingsModal({
|
|
|
852
907
|
</div>
|
|
853
908
|
<div className="mt-3 flex gap-2">
|
|
854
909
|
<button
|
|
855
|
-
className="rounded-full
|
|
910
|
+
className="rounded-full px-4 py-2 text-xs settings-btn-accent"
|
|
856
911
|
onClick={upsertConnectionFromForm}
|
|
857
912
|
>
|
|
858
913
|
{editingConnectionId ? "Update Connection" : "Add Connection"}
|
|
@@ -919,41 +974,27 @@ export default function SettingsModal({
|
|
|
919
974
|
placeholder="~/iris-project ~/Desktop"
|
|
920
975
|
/>
|
|
921
976
|
</div>
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
))}
|
|
944
|
-
</div>
|
|
945
|
-
<div>
|
|
946
|
-
<label className="mb-2 block text-xs uppercase tracking-[0.2em] text-[var(--text-muted)]">
|
|
947
|
-
Web Search Backend
|
|
948
|
-
</label>
|
|
949
|
-
<select
|
|
950
|
-
className="w-full rounded-lg border border-[var(--border)] bg-[var(--panel)] px-3 py-2 text-sm"
|
|
951
|
-
value={webSearchBackend}
|
|
952
|
-
onChange={(event) => setWebSearchBackend(event.target.value as WebSearchBackend)}
|
|
953
|
-
>
|
|
954
|
-
<option value="no_key">No-key (default)</option>
|
|
955
|
-
<option value="hybrid">Hybrid</option>
|
|
956
|
-
</select>
|
|
977
|
+
<div className="grid gap-2 md:grid-cols-2">
|
|
978
|
+
{[
|
|
979
|
+
{ label: "Enable Apple Notes tools", checked: enableNotes, setter: setEnableNotes },
|
|
980
|
+
{ label: "Enable app open/focus tools", checked: enableApps, setter: setEnableApps },
|
|
981
|
+
{ label: "Enable Numbers tools", checked: enableNumbers, setter: setEnableNumbers },
|
|
982
|
+
{ label: "Enable web search/open tools", checked: enableWeb, setter: setEnableWeb },
|
|
983
|
+
{ label: "Enable music controls", checked: enableMusic, setter: setEnableMusic },
|
|
984
|
+
{ label: "Enable calendar/reminders tools", checked: enableCalendar, setter: setEnableCalendar },
|
|
985
|
+
{ label: "Enable mail/messages tools", checked: enableMail, setter: setEnableMail },
|
|
986
|
+
{ label: "Enable workflow tool", checked: enableWorkflow, setter: setEnableWorkflow },
|
|
987
|
+
{ label: "Enable system controls", checked: enableSystem, setter: setEnableSystem },
|
|
988
|
+
].map((item) => (
|
|
989
|
+
<label key={item.label} className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
|
990
|
+
<input
|
|
991
|
+
type="checkbox"
|
|
992
|
+
checked={item.checked}
|
|
993
|
+
onChange={(event) => item.setter(event.target.checked)}
|
|
994
|
+
/>
|
|
995
|
+
{item.label}
|
|
996
|
+
</label>
|
|
997
|
+
))}
|
|
957
998
|
</div>
|
|
958
999
|
</div>
|
|
959
1000
|
) : null}
|
|
@@ -1020,13 +1061,11 @@ export default function SettingsModal({
|
|
|
1020
1061
|
<div className="flex items-start justify-between gap-3">
|
|
1021
1062
|
<div>
|
|
1022
1063
|
<div className="text-sm font-medium text-[var(--text-primary)]">{entry.key}</div>
|
|
1023
|
-
<div className="mt-0.5 text-xs text-[var(--text-secondary)]">
|
|
1064
|
+
<div className="mt-0.5 text-xs text-[var(--text-secondary)]">
|
|
1065
|
+
{formatMemoryValue(entry.value)}
|
|
1066
|
+
</div>
|
|
1024
1067
|
<div className="mt-1 text-[11px] text-[var(--text-muted)]">
|
|
1025
|
-
{entry
|
|
1026
|
-
{entry.scope === "conversation" && entry.conversationId
|
|
1027
|
-
? ` | ${entry.conversationId}`
|
|
1028
|
-
: ""}
|
|
1029
|
-
{` | ${entry.source}`}
|
|
1068
|
+
{formatMemoryMeta(entry)}
|
|
1030
1069
|
</div>
|
|
1031
1070
|
</div>
|
|
1032
1071
|
<div className="flex shrink-0 gap-2">
|
|
@@ -1130,7 +1169,7 @@ export default function SettingsModal({
|
|
|
1130
1169
|
) : null}
|
|
1131
1170
|
<div className="flex gap-2">
|
|
1132
1171
|
<button
|
|
1133
|
-
className="rounded-full
|
|
1172
|
+
className="rounded-full px-4 py-2 text-xs settings-btn-accent"
|
|
1134
1173
|
onClick={() => {
|
|
1135
1174
|
void upsertMemoryEntry();
|
|
1136
1175
|
}}
|
|
@@ -1157,7 +1196,11 @@ export default function SettingsModal({
|
|
|
1157
1196
|
<select
|
|
1158
1197
|
className="w-full rounded-lg border border-[var(--border)] bg-[var(--panel-2)] px-3 py-2 text-sm"
|
|
1159
1198
|
value={theme}
|
|
1160
|
-
onChange={(event) =>
|
|
1199
|
+
onChange={(event) => {
|
|
1200
|
+
const value = event.target.value as "dark" | "light";
|
|
1201
|
+
setTheme(value);
|
|
1202
|
+
document.documentElement.dataset.theme = value;
|
|
1203
|
+
}}
|
|
1161
1204
|
>
|
|
1162
1205
|
<option value="dark">Dark</option>
|
|
1163
1206
|
<option value="light">Light</option>
|
|
@@ -1170,13 +1213,26 @@ export default function SettingsModal({
|
|
|
1170
1213
|
<select
|
|
1171
1214
|
className="w-full rounded-lg border border-[var(--border)] bg-[var(--panel-2)] px-3 py-2 text-sm"
|
|
1172
1215
|
value={font}
|
|
1173
|
-
onChange={(event) =>
|
|
1174
|
-
|
|
1175
|
-
|
|
1216
|
+
onChange={(event) => {
|
|
1217
|
+
const value = event.target.value as "ibm" | "manrope" | "sora" | "space" | "poppins";
|
|
1218
|
+
setFont(value);
|
|
1219
|
+
const fontVar =
|
|
1220
|
+
value === "manrope"
|
|
1221
|
+
? "var(--font-manrope)"
|
|
1222
|
+
: value === "poppins"
|
|
1223
|
+
? "var(--font-poppins)"
|
|
1224
|
+
: value === "sora"
|
|
1225
|
+
? "var(--font-sora)"
|
|
1226
|
+
: value === "space"
|
|
1227
|
+
? "var(--font-space)"
|
|
1228
|
+
: "var(--font-poppins)";
|
|
1229
|
+
document.documentElement.style.setProperty("--app-font", fontVar);
|
|
1230
|
+
document.body.style.fontFamily = fontVar;
|
|
1231
|
+
}}
|
|
1176
1232
|
>
|
|
1177
|
-
<option value="ibm">IBM Plex Sans
|
|
1233
|
+
<option value="ibm">IBM Plex Sans</option>
|
|
1178
1234
|
<option value="manrope">Manrope</option>
|
|
1179
|
-
<option value="poppins">Poppins</option>
|
|
1235
|
+
<option value="poppins">Poppins (Default)</option>
|
|
1180
1236
|
<option value="sora">Sora</option>
|
|
1181
1237
|
<option value="space">Space Grotesk</option>
|
|
1182
1238
|
</select>
|
|
@@ -1186,20 +1242,34 @@ export default function SettingsModal({
|
|
|
1186
1242
|
Accent Color
|
|
1187
1243
|
</label>
|
|
1188
1244
|
<div className="grid grid-cols-4 gap-2">
|
|
1189
|
-
{accentPresets.map((preset) =>
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
<
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1245
|
+
{accentPresets.map((preset) => {
|
|
1246
|
+
const isDefault = preset.id === "default";
|
|
1247
|
+
const displayColor =
|
|
1248
|
+
isDefault && theme === "light"
|
|
1249
|
+
? DEFAULT_ACCENT_LIGHT
|
|
1250
|
+
: preset.color;
|
|
1251
|
+
const isSelected = isDefault
|
|
1252
|
+
? accentColor === DEFAULT_ACCENT_DARK
|
|
1253
|
+
: accentColor === preset.color;
|
|
1254
|
+
return (
|
|
1255
|
+
<button
|
|
1256
|
+
key={preset.id}
|
|
1257
|
+
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs ${isSelected
|
|
1258
|
+
? "border-[var(--accent-ring)] text-[var(--text-primary)]"
|
|
1259
|
+
: "border-[var(--border)] text-[var(--text-secondary)]"
|
|
1260
|
+
}`}
|
|
1261
|
+
onClick={() =>
|
|
1262
|
+
setAccentColor(isDefault ? DEFAULT_ACCENT_DARK : preset.color)
|
|
1263
|
+
}
|
|
1264
|
+
>
|
|
1265
|
+
<span
|
|
1266
|
+
className="h-3 w-3 rounded-full"
|
|
1267
|
+
style={{ background: displayColor }}
|
|
1268
|
+
/>
|
|
1269
|
+
{preset.label}
|
|
1270
|
+
</button>
|
|
1271
|
+
);
|
|
1272
|
+
})}
|
|
1203
1273
|
</div>
|
|
1204
1274
|
</div>
|
|
1205
1275
|
</div>
|
|
@@ -1248,7 +1318,7 @@ export default function SettingsModal({
|
|
|
1248
1318
|
Cancel
|
|
1249
1319
|
</button>
|
|
1250
1320
|
<button
|
|
1251
|
-
className="rounded-full
|
|
1321
|
+
className="rounded-full px-4 py-2 text-xs settings-btn-accent"
|
|
1252
1322
|
onClick={handleSave}
|
|
1253
1323
|
>
|
|
1254
1324
|
Save
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import {
|
|
5
|
-
Folder,
|
|
6
5
|
PanelLeftClose,
|
|
7
6
|
PenSquare,
|
|
8
7
|
Search,
|
|
@@ -83,8 +82,8 @@ export default function Sidebar({
|
|
|
83
82
|
</div>
|
|
84
83
|
|
|
85
84
|
<div className="flex-1 overflow-y-auto px-2">
|
|
86
|
-
<div className="sidebar-text px-2 py-2 text-
|
|
87
|
-
Your
|
|
85
|
+
<div className="sidebar-text px-2 py-2 text-sm text-[var(--text-muted)]">
|
|
86
|
+
Your Chats
|
|
88
87
|
</div>
|
|
89
88
|
<div className="space-y-2">
|
|
90
89
|
{groups.map(({ root }) => (
|
|
@@ -106,7 +105,6 @@ export default function Sidebar({
|
|
|
106
105
|
}`}
|
|
107
106
|
>
|
|
108
107
|
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
109
|
-
<Folder className="h-4 w-4 shrink-0 text-[var(--text-muted)]" />
|
|
110
108
|
<div className="sidebar-text min-w-0 truncate">
|
|
111
109
|
{root.title || "Main chat"}
|
|
112
110
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import { ChevronDown, MessageSquare,
|
|
4
|
+
import { ChevronDown, MessageSquare, Network, Settings } from "lucide-react";
|
|
5
5
|
import type { ModelConnection } from "../lib/types";
|
|
6
6
|
import { getModelDisplayLabel } from "../lib/model-presets";
|
|
7
7
|
|
|
@@ -67,7 +67,7 @@ export default function TopBar({
|
|
|
67
67
|
<button
|
|
68
68
|
type="button"
|
|
69
69
|
onClick={() => setConnectionMenuOpen((current) => !current)}
|
|
70
|
-
className="inline-flex max-w-[42vw] items-center gap-2 rounded-md px-1.5 py-1 text-left sm:max-w-[240px] sm:px-2"
|
|
70
|
+
className="inline-flex max-w-[42vw] items-center gap-2 rounded-md px-1.5 py-1 text-left text-[var(--text-primary)] sm:max-w-[240px] sm:px-2"
|
|
71
71
|
aria-haspopup="listbox"
|
|
72
72
|
aria-expanded={connectionMenuOpen}
|
|
73
73
|
aria-label="Model provider"
|
|
@@ -144,15 +144,9 @@ export default function TopBar({
|
|
|
144
144
|
onClick={onToggleView}
|
|
145
145
|
>
|
|
146
146
|
{viewMode === "chat" ? (
|
|
147
|
-
|
|
148
|
-
<Waypoints className="h-5 w-5" />
|
|
149
|
-
<span className="hidden sm:inline">Map</span>
|
|
150
|
-
</>
|
|
147
|
+
<Network className="h-5 w-5" />
|
|
151
148
|
) : (
|
|
152
|
-
|
|
153
|
-
<MessageSquare className="h-5 w-5" />
|
|
154
|
-
<span className="hidden sm:inline">Chat</span>
|
|
155
|
-
</>
|
|
149
|
+
<MessageSquare className="h-5 w-5" />
|
|
156
150
|
)}
|
|
157
151
|
</button>
|
|
158
152
|
) : null}
|
package/template/src/lib/data.ts
CHANGED
|
@@ -30,6 +30,11 @@ const DEFAULT_SETTINGS: Settings = {
|
|
|
30
30
|
memory: { ...DEFAULT_MEMORY_SETTINGS },
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
/** Default accent gray in dark mode (chat bubble, etc.). */
|
|
34
|
+
export const DEFAULT_ACCENT_DARK = "#66706e";
|
|
35
|
+
/** Default accent gray in light mode (light gray so bubble isn’t too dark on white). */
|
|
36
|
+
export const DEFAULT_ACCENT_LIGHT = "#e4e6e5";
|
|
37
|
+
|
|
33
38
|
const LEGACY_DEFAULT_LOCAL_TOOLS_SETTINGS: LocalToolsSettings = {
|
|
34
39
|
...DEFAULT_LOCAL_TOOLS_SETTINGS,
|
|
35
40
|
approvalMode: "always_confirm_writes",
|
|
@@ -136,6 +136,32 @@ export function filterModelIdsForConnection(params: {
|
|
|
136
136
|
return ids.slice(0, Math.min(ids.length, OPENAI_FRONTIER_MODEL_PRESETS.length));
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
const OPENAI_MODEL_LABELS: Record<string, string> = {
|
|
140
|
+
"gpt-5.2": "GPT 5.2",
|
|
141
|
+
"gpt-5.2-pro": "GPT 5.2 Pro",
|
|
142
|
+
"gpt-5.2-chat-latest": "GPT 5.2 Chat (Latest)",
|
|
143
|
+
"gpt-5.2-codex": "GPT 5.2 Codex",
|
|
144
|
+
"gpt-5.1": "GPT 5.1",
|
|
145
|
+
"gpt-5.1-chat-latest": "GPT 5.1 Chat (Latest)",
|
|
146
|
+
"gpt-5.1-codex": "GPT 5.1 Codex",
|
|
147
|
+
"gpt-5.1-codex-max": "GPT 5.1 Codex Max",
|
|
148
|
+
"gpt-5": "GPT 5",
|
|
149
|
+
"gpt-5-chat-latest": "GPT 5 Chat (Latest)",
|
|
150
|
+
"gpt-5-mini": "GPT 5 Mini",
|
|
151
|
+
"gpt-5-nano": "GPT 5 Nano",
|
|
152
|
+
"gpt-5-codex": "GPT 5 Codex",
|
|
153
|
+
"gpt-5-pro": "GPT 5 Pro",
|
|
154
|
+
"gpt-4.1": "GPT 4.1",
|
|
155
|
+
"gpt-4.1-mini": "GPT 4.1 Mini",
|
|
156
|
+
"gpt-4.1-nano": "GPT 4.1 Nano",
|
|
157
|
+
"gpt-4o": "GPT 4o",
|
|
158
|
+
"gpt-4o-mini": "GPT 4o Mini",
|
|
159
|
+
"o3-pro": "O3 Pro",
|
|
160
|
+
"o3": "O3",
|
|
161
|
+
"o4-mini": "O4 Mini",
|
|
162
|
+
"o3-mini": "O3 Mini",
|
|
163
|
+
};
|
|
164
|
+
|
|
139
165
|
const ANTHROPIC_MODEL_LABELS: Record<string, string> = {
|
|
140
166
|
"claude-opus-4-61": "Claude Opus 4.6",
|
|
141
167
|
"claude-sonnet-4-51": "Claude Sonnet 4.5",
|
|
@@ -209,6 +235,9 @@ export function getModelDisplayLabel(modelId: string, connection: ModelConnectio
|
|
|
209
235
|
if (connection.provider === "google") {
|
|
210
236
|
return GOOGLE_MODEL_LABELS[modelId] ?? humanizeModelId(modelId);
|
|
211
237
|
}
|
|
238
|
+
if (connection.provider === "openai") {
|
|
239
|
+
return OPENAI_MODEL_LABELS[modelId] ?? humanizeModelId(modelId);
|
|
240
|
+
}
|
|
212
241
|
return modelId;
|
|
213
242
|
}
|
|
214
243
|
|