iris-chatbot 1.0.0 → 4.0.0
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/next-env.d.ts +1 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/src/app/api/local-sync/route.ts +4 -20
- package/template/src/app/globals.css +3 -3
- package/template/src/app/page.tsx +23 -5
- package/template/src/components/ChatView.tsx +4 -4
- package/template/src/components/SettingsModal.tsx +11 -8
- package/template/src/components/Sidebar.tsx +17 -11
- package/template/src/components/TopBar.tsx +20 -16
- package/template/src/lib/db.ts +1 -1
package/package.json
CHANGED
package/template/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/types/routes.d.ts";
|
|
3
|
+
import "./.next/dev/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iris",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "iris",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "4.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sdk": "^0.72.1",
|
|
12
12
|
"clsx": "^2.1.1",
|
package/template/package.json
CHANGED
|
@@ -6,15 +6,14 @@ export const runtime = "nodejs";
|
|
|
6
6
|
export const dynamic = "force-dynamic";
|
|
7
7
|
|
|
8
8
|
const STATE_FILE = "state.json";
|
|
9
|
-
const
|
|
10
|
-
const DATA_DIR_LEGACY = ".zenith-data";
|
|
9
|
+
const DATA_DIR = ".iris-data";
|
|
11
10
|
|
|
12
11
|
function getStatePath(dir: string): string {
|
|
13
12
|
return path.join(process.cwd(), dir, STATE_FILE);
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
function getDataDir(): string {
|
|
17
|
-
return path.join(process.cwd(),
|
|
16
|
+
return path.join(process.cwd(), DATA_DIR);
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
const EMPTY_PAYLOAD = {
|
|
@@ -41,22 +40,7 @@ export async function GET() {
|
|
|
41
40
|
return JSON.parse(raw) as Record<string, unknown>;
|
|
42
41
|
};
|
|
43
42
|
try {
|
|
44
|
-
const data = await tryRead(
|
|
45
|
-
Object.assign(payload, {
|
|
46
|
-
threads: Array.isArray(data.threads) ? data.threads : EMPTY_PAYLOAD.threads,
|
|
47
|
-
messages: Array.isArray(data.messages) ? data.messages : EMPTY_PAYLOAD.messages,
|
|
48
|
-
settings: Array.isArray(data.settings) ? data.settings : EMPTY_PAYLOAD.settings,
|
|
49
|
-
toolEvents: Array.isArray(data.toolEvents) ? data.toolEvents : EMPTY_PAYLOAD.toolEvents,
|
|
50
|
-
toolApprovals: Array.isArray(data.toolApprovals) ? data.toolApprovals : EMPTY_PAYLOAD.toolApprovals,
|
|
51
|
-
memories: Array.isArray(data.memories) ? data.memories : EMPTY_PAYLOAD.memories,
|
|
52
|
-
});
|
|
53
|
-
return Response.json(payload);
|
|
54
|
-
} catch (err) {
|
|
55
|
-
const code = err && typeof err === "object" && "code" in err ? (err as NodeJS.ErrnoException).code : null;
|
|
56
|
-
if (code !== "ENOENT") throw err;
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
const data = await tryRead(DATA_DIR_LEGACY);
|
|
43
|
+
const data = await tryRead(DATA_DIR);
|
|
60
44
|
Object.assign(payload, {
|
|
61
45
|
threads: Array.isArray(data.threads) ? data.threads : EMPTY_PAYLOAD.threads,
|
|
62
46
|
messages: Array.isArray(data.messages) ? data.messages : EMPTY_PAYLOAD.messages,
|
|
@@ -76,7 +60,7 @@ export async function GET() {
|
|
|
76
60
|
}
|
|
77
61
|
|
|
78
62
|
export async function POST(request: NextRequest) {
|
|
79
|
-
const statePath = getStatePath(
|
|
63
|
+
const statePath = getStatePath(DATA_DIR);
|
|
80
64
|
const dir = getDataDir();
|
|
81
65
|
let body: unknown;
|
|
82
66
|
try {
|
|
@@ -784,8 +784,8 @@ a {
|
|
|
784
784
|
height: 38px;
|
|
785
785
|
width: 38px;
|
|
786
786
|
border-radius: 12px;
|
|
787
|
-
border: 1px solid
|
|
788
|
-
background:
|
|
787
|
+
border: 1px solid transparent;
|
|
788
|
+
background: transparent;
|
|
789
789
|
color: var(--text-secondary);
|
|
790
790
|
display: flex;
|
|
791
791
|
align-items: center;
|
|
@@ -794,7 +794,7 @@ a {
|
|
|
794
794
|
}
|
|
795
795
|
|
|
796
796
|
.sidebar-icon-button:hover {
|
|
797
|
-
border-color: var(--border
|
|
797
|
+
border-color: var(--border);
|
|
798
798
|
color: var(--text-primary);
|
|
799
799
|
transform: translateY(-1px);
|
|
800
800
|
}
|
|
@@ -132,6 +132,11 @@ export default function Home() {
|
|
|
132
132
|
[threads, activeThreadId]
|
|
133
133
|
);
|
|
134
134
|
|
|
135
|
+
const sidebarThreads = useMemo(
|
|
136
|
+
() => threads?.filter((t) => t.headMessageId != null) ?? [],
|
|
137
|
+
[threads]
|
|
138
|
+
);
|
|
139
|
+
|
|
135
140
|
const conversationMessages = useConversationMessages(
|
|
136
141
|
activeThread?.conversationId || null
|
|
137
142
|
);
|
|
@@ -333,11 +338,23 @@ export default function Home() {
|
|
|
333
338
|
}
|
|
334
339
|
>
|
|
335
340
|
<Sidebar
|
|
336
|
-
threads={
|
|
341
|
+
threads={sidebarThreads}
|
|
337
342
|
activeThreadId={activeThreadId}
|
|
338
343
|
collapsed={sidebarCollapsed}
|
|
339
|
-
onSelect={(id) =>
|
|
344
|
+
onSelect={async (id) => {
|
|
345
|
+
if (id !== activeThreadId) {
|
|
346
|
+
const prev = threads?.find((t) => t.id === activeThreadId);
|
|
347
|
+
if (prev?.headMessageId == null) {
|
|
348
|
+
await deleteThread(prev.id);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
setActiveThreadId(id);
|
|
352
|
+
}}
|
|
340
353
|
onNewChat={async () => {
|
|
354
|
+
const prev = threads?.find((t) => t.id === activeThreadId);
|
|
355
|
+
if (prev?.headMessageId == null) {
|
|
356
|
+
await deleteThread(prev.id);
|
|
357
|
+
}
|
|
341
358
|
const thread = await createNewThread();
|
|
342
359
|
setViewMode("chat");
|
|
343
360
|
setActiveThreadId(thread.id);
|
|
@@ -380,8 +397,6 @@ export default function Home() {
|
|
|
380
397
|
localToolsEnabled={Boolean(settings?.localTools?.enabled)}
|
|
381
398
|
modelPresets={modelPresets}
|
|
382
399
|
viewMode={viewMode}
|
|
383
|
-
onConnectionChange={handleConnectionChange}
|
|
384
|
-
onModelChange={handleModelChange}
|
|
385
400
|
onToggleView={() => {
|
|
386
401
|
const nextViewMode = viewMode === "chat" ? "map" : "chat";
|
|
387
402
|
if (nextViewMode === "chat") {
|
|
@@ -389,6 +404,9 @@ export default function Home() {
|
|
|
389
404
|
}
|
|
390
405
|
setViewMode(nextViewMode);
|
|
391
406
|
}}
|
|
407
|
+
showMapButton={Boolean(conversationMessages?.length)}
|
|
408
|
+
onConnectionChange={handleConnectionChange}
|
|
409
|
+
onModelChange={handleModelChange}
|
|
392
410
|
onEnableLocalTools={handleEnableLocalTools}
|
|
393
411
|
onOpenSettings={() => setSettingsOpen(true)}
|
|
394
412
|
/>
|
|
@@ -431,7 +449,7 @@ export default function Home() {
|
|
|
431
449
|
) : null}
|
|
432
450
|
{searchOpen ? (
|
|
433
451
|
<SearchModal
|
|
434
|
-
threads={
|
|
452
|
+
threads={sidebarThreads}
|
|
435
453
|
onClose={() => setSearchOpen(false)}
|
|
436
454
|
onSelect={(id) => {
|
|
437
455
|
setActiveThreadId(id);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { ArrowDown } from "lucide-react";
|
|
4
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
4
|
import type {
|
|
6
5
|
ChatCitationSource,
|
|
@@ -27,6 +26,7 @@ import {
|
|
|
27
26
|
} from "../lib/memory";
|
|
28
27
|
import { useUIStore } from "../lib/store";
|
|
29
28
|
import { toChatConnectionPayload } from "../lib/connections";
|
|
29
|
+
import { ArrowDown } from "lucide-react";
|
|
30
30
|
import MessageCard from "./MessageCard";
|
|
31
31
|
import Composer from "./Composer";
|
|
32
32
|
|
|
@@ -1506,9 +1506,9 @@ export default function ChatView({
|
|
|
1506
1506
|
{threadMessages.length > 0 ? (
|
|
1507
1507
|
<>
|
|
1508
1508
|
{showJumpToBottom ? (
|
|
1509
|
-
<div className="pointer-events-none absolute bottom-
|
|
1509
|
+
<div className="pointer-events-none absolute bottom-28 left-1/2 z-30 -translate-x-1/2 opacity-80 transition-opacity duration-200">
|
|
1510
1510
|
<button
|
|
1511
|
-
className="pointer-events-auto inline-flex h-
|
|
1511
|
+
className="pointer-events-auto inline-flex h-9 w-9 items-center justify-center rounded-full border border-[var(--border-strong)] bg-[var(--panel)] text-[var(--text-primary)] shadow-[var(--shadow)] transition hover:translate-y-[1px] hover:border-[var(--accent)] hover:opacity-100"
|
|
1512
1512
|
onClick={() => {
|
|
1513
1513
|
shouldStickToBottomRef.current = true;
|
|
1514
1514
|
setShowJumpToBottom(false);
|
|
@@ -1518,7 +1518,7 @@ export default function ChatView({
|
|
|
1518
1518
|
aria-label="Jump to latest message"
|
|
1519
1519
|
title="Jump to latest"
|
|
1520
1520
|
>
|
|
1521
|
-
<ArrowDown className="h-
|
|
1521
|
+
<ArrowDown className="h-4 w-4" />
|
|
1522
1522
|
</button>
|
|
1523
1523
|
</div>
|
|
1524
1524
|
) : null}
|
|
@@ -559,14 +559,6 @@ export default function SettingsModal({
|
|
|
559
559
|
<div className="mt-5 max-h-[68vh] overflow-y-auto pr-1 text-sm">
|
|
560
560
|
{activeTab === "models" ? (
|
|
561
561
|
<div className="space-y-4">
|
|
562
|
-
<label className="flex items-center gap-2 rounded-xl border border-[var(--border)] bg-[var(--panel-2)] p-3 text-sm text-[var(--text-secondary)]">
|
|
563
|
-
<input
|
|
564
|
-
type="checkbox"
|
|
565
|
-
checked={showExtendedOpenAIModels}
|
|
566
|
-
onChange={(event) => setShowExtendedOpenAIModels(event.target.checked)}
|
|
567
|
-
/>
|
|
568
|
-
Show extended OpenAI model list (non-frontier models)
|
|
569
|
-
</label>
|
|
570
562
|
<div>
|
|
571
563
|
<label className="mb-2 block text-xs uppercase tracking-[0.2em] text-[var(--text-muted)]">
|
|
572
564
|
Default Connection
|
|
@@ -624,6 +616,17 @@ export default function SettingsModal({
|
|
|
624
616
|
</div>
|
|
625
617
|
) : null}
|
|
626
618
|
</div>
|
|
619
|
+
{selectedDefaultConnection?.kind === "builtin" &&
|
|
620
|
+
selectedDefaultConnection?.provider === "openai" ? (
|
|
621
|
+
<label className="mt-3 flex items-center gap-2 text-sm text-[var(--text-secondary)]">
|
|
622
|
+
<input
|
|
623
|
+
type="checkbox"
|
|
624
|
+
checked={showExtendedOpenAIModels}
|
|
625
|
+
onChange={(event) => setShowExtendedOpenAIModels(event.target.checked)}
|
|
626
|
+
/>
|
|
627
|
+
Show extended OpenAI model list (non-frontier models)
|
|
628
|
+
</label>
|
|
629
|
+
) : null}
|
|
627
630
|
</div>
|
|
628
631
|
) : null}
|
|
629
632
|
</div>
|
|
@@ -90,23 +90,33 @@ export default function Sidebar({
|
|
|
90
90
|
{groups.map(({ root }) => (
|
|
91
91
|
<div key={root.id} className="space-y-1">
|
|
92
92
|
<div
|
|
93
|
-
|
|
93
|
+
role="button"
|
|
94
|
+
tabIndex={0}
|
|
95
|
+
onClick={() => onSelect(root.id)}
|
|
96
|
+
onKeyDown={(e) => {
|
|
97
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
onSelect(root.id);
|
|
100
|
+
}
|
|
101
|
+
}}
|
|
102
|
+
className={`group flex h-11 cursor-pointer items-center justify-between rounded-lg px-3 text-sm transition ${
|
|
94
103
|
activeThreadId === root.id
|
|
95
104
|
? "bg-[var(--panel-2)] text-[var(--text-primary)]"
|
|
96
105
|
: "text-[var(--text-secondary)] hover:bg-[var(--panel)]"
|
|
97
106
|
}`}
|
|
98
107
|
>
|
|
99
|
-
<
|
|
100
|
-
onClick={() => onSelect(root.id)}
|
|
101
|
-
className="flex min-w-0 flex-1 items-center gap-2 text-left"
|
|
102
|
-
>
|
|
108
|
+
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
103
109
|
<Folder className="h-4 w-4 shrink-0 text-[var(--text-muted)]" />
|
|
104
110
|
<div className="sidebar-text min-w-0 truncate">
|
|
105
111
|
{root.title || "Main chat"}
|
|
106
112
|
</div>
|
|
107
|
-
</
|
|
113
|
+
</div>
|
|
108
114
|
<button
|
|
109
|
-
|
|
115
|
+
type="button"
|
|
116
|
+
onClick={(e) => {
|
|
117
|
+
e.stopPropagation();
|
|
118
|
+
onDeleteThread(root);
|
|
119
|
+
}}
|
|
110
120
|
className="ml-2 rounded-full border border-transparent p-1 text-[var(--text-muted)] opacity-0 transition hover:border-[var(--border)] hover:text-[var(--danger)] group-hover:opacity-100"
|
|
111
121
|
>
|
|
112
122
|
<Trash2 className="h-3 w-3" />
|
|
@@ -116,10 +126,6 @@ export default function Sidebar({
|
|
|
116
126
|
))}
|
|
117
127
|
</div>
|
|
118
128
|
</div>
|
|
119
|
-
|
|
120
|
-
<div className="border-t border-[var(--border)] px-4 py-4 text-xs text-[var(--text-muted)]">
|
|
121
|
-
<span className="sidebar-text">Local mode</span>
|
|
122
|
-
</div>
|
|
123
129
|
</>
|
|
124
130
|
) : (
|
|
125
131
|
<div className="flex-1 px-2 pb-4 pt-4">
|
|
@@ -16,6 +16,7 @@ export default function TopBar({
|
|
|
16
16
|
onModelChange,
|
|
17
17
|
viewMode,
|
|
18
18
|
onToggleView,
|
|
19
|
+
showMapButton,
|
|
19
20
|
onEnableLocalTools,
|
|
20
21
|
onOpenSettings,
|
|
21
22
|
modelPresets,
|
|
@@ -30,6 +31,7 @@ export default function TopBar({
|
|
|
30
31
|
onModelChange: (model: string) => void;
|
|
31
32
|
viewMode: "chat" | "map";
|
|
32
33
|
onToggleView: () => void;
|
|
34
|
+
showMapButton: boolean;
|
|
33
35
|
onEnableLocalTools: () => void | Promise<void>;
|
|
34
36
|
onOpenSettings: () => void;
|
|
35
37
|
modelPresets: string[];
|
|
@@ -136,22 +138,24 @@ export default function TopBar({
|
|
|
136
138
|
Enable Local Tools
|
|
137
139
|
</button>
|
|
138
140
|
) : null}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
141
|
+
{showMapButton ? (
|
|
142
|
+
<button
|
|
143
|
+
className="flex items-center gap-1.5 rounded-full border border-[var(--border)] bg-[var(--bg)] px-3 py-2 text-xs text-[var(--text-secondary)] hover:border-[var(--border-strong)] sm:gap-2 sm:px-4 sm:text-sm"
|
|
144
|
+
onClick={onToggleView}
|
|
145
|
+
>
|
|
146
|
+
{viewMode === "chat" ? (
|
|
147
|
+
<>
|
|
148
|
+
<Waypoints className="h-5 w-5" />
|
|
149
|
+
<span className="hidden sm:inline">Map</span>
|
|
150
|
+
</>
|
|
151
|
+
) : (
|
|
152
|
+
<>
|
|
153
|
+
<MessageSquare className="h-5 w-5" />
|
|
154
|
+
<span className="hidden sm:inline">Chat</span>
|
|
155
|
+
</>
|
|
156
|
+
)}
|
|
157
|
+
</button>
|
|
158
|
+
) : null}
|
|
155
159
|
<button
|
|
156
160
|
className="rounded-full border border-[var(--border)] bg-[var(--bg)] p-2.5 text-[var(--text-secondary)] hover:border-[var(--border-strong)]"
|
|
157
161
|
onClick={onOpenSettings}
|
package/template/src/lib/db.ts
CHANGED
|
@@ -10,7 +10,7 @@ class IrisDB extends Dexie {
|
|
|
10
10
|
memories!: Table<MemoryEntry, string>;
|
|
11
11
|
|
|
12
12
|
constructor() {
|
|
13
|
-
super("
|
|
13
|
+
super("iris-chat");
|
|
14
14
|
this.version(1).stores({
|
|
15
15
|
messages: "id, conversationId, parentId, createdAt",
|
|
16
16
|
threads: "id, conversationId, headMessageId, updatedAt",
|