@cryptiklemur/lattice 1.43.7 → 1.44.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.
|
@@ -3,6 +3,7 @@ import { X, Columns2, Rows2, MessageSquare, FolderOpen, TerminalSquare, StickyNo
|
|
|
3
3
|
import { useWorkspace } from "../../hooks/useWorkspace";
|
|
4
4
|
import { useSession } from "../../hooks/useSession";
|
|
5
5
|
import type { Tab, TabType } from "../../stores/workspace";
|
|
6
|
+
import { pinTab } from "../../stores/workspace";
|
|
6
7
|
import { formatSessionTitle } from "../../utils/formatSessionTitle";
|
|
7
8
|
|
|
8
9
|
interface TabBarProps {
|
|
@@ -155,6 +156,7 @@ export function TabBar({ paneId, isActivePane }: TabBarProps) {
|
|
|
155
156
|
tabIndex={0}
|
|
156
157
|
aria-selected={isActive}
|
|
157
158
|
onClick={function () { handleTabClick(tab.id); }}
|
|
159
|
+
onDoubleClick={function () { if (!tab.pinned) pinTab(tab.id); }}
|
|
158
160
|
onMouseDown={function (e) { handleMiddleClick(e, tab); }}
|
|
159
161
|
onKeyDown={function (e) {
|
|
160
162
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -171,7 +173,7 @@ export function TabBar({ paneId, isActivePane }: TabBarProps) {
|
|
|
171
173
|
}
|
|
172
174
|
>
|
|
173
175
|
<Icon size={14} className={isActive ? "text-primary" : ""} />
|
|
174
|
-
<span className="truncate text-[12px]">{label}</span>
|
|
176
|
+
<span className={"truncate text-[12px]" + (tab.pinned ? "" : " italic")}>{label}</span>
|
|
175
177
|
{tab.closeable && (
|
|
176
178
|
<button
|
|
177
179
|
aria-label={"Close " + label + " tab"}
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
} from "@lattice/shared";
|
|
19
19
|
import { useWebSocket } from "./useWebSocket";
|
|
20
20
|
import { setActiveSessionId as setSidebarSessionId } from "../stores/sidebar";
|
|
21
|
-
import { updateSessionTabTitle } from "../stores/workspace";
|
|
21
|
+
import { updateSessionTabTitle, pinTab } from "../stores/workspace";
|
|
22
22
|
import {
|
|
23
23
|
getSessionStore,
|
|
24
24
|
setSessionMessages,
|
|
@@ -120,6 +120,7 @@ export function useSession(): UseSessionReturn {
|
|
|
120
120
|
setPromptSuggestion(null);
|
|
121
121
|
setIsProcessing(true);
|
|
122
122
|
setSessionBusy(false);
|
|
123
|
+
pinTab("chat-" + currentSessionId);
|
|
123
124
|
sendRef.current(msg as ChatSendMessage);
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -156,17 +156,19 @@ export function decodeWorkspaceUrl(
|
|
|
156
156
|
type: "chat",
|
|
157
157
|
label: "Session",
|
|
158
158
|
closeable: true,
|
|
159
|
+
pinned: true,
|
|
159
160
|
sessionId: resolvedId,
|
|
160
161
|
projectSlug: projectSlug,
|
|
161
162
|
};
|
|
162
163
|
} else if (tabType === "chat") {
|
|
163
|
-
tab = { id: "chat", type: "chat", label: "Chat", closeable: false };
|
|
164
|
+
tab = { id: "chat", type: "chat", label: "Chat", closeable: false, pinned: true };
|
|
164
165
|
} else {
|
|
165
166
|
tab = {
|
|
166
167
|
id: tabType,
|
|
167
168
|
type: tabType,
|
|
168
169
|
label: labels[tabType],
|
|
169
170
|
closeable: true,
|
|
171
|
+
pinned: true,
|
|
170
172
|
};
|
|
171
173
|
}
|
|
172
174
|
|
|
@@ -190,7 +192,7 @@ export function decodeWorkspaceUrl(
|
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
if (panes.length === 0) {
|
|
193
|
-
const defaultTab: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false };
|
|
195
|
+
const defaultTab: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false, pinned: true };
|
|
194
196
|
tabs.push(defaultTab);
|
|
195
197
|
panes.push({ id: "pane-1", tabIds: ["chat"], activeTabId: "chat" });
|
|
196
198
|
}
|
|
@@ -8,6 +8,7 @@ export interface Tab {
|
|
|
8
8
|
type: TabType;
|
|
9
9
|
label: string;
|
|
10
10
|
closeable: boolean;
|
|
11
|
+
pinned: boolean;
|
|
11
12
|
sessionId?: string;
|
|
12
13
|
projectSlug?: string;
|
|
13
14
|
}
|
|
@@ -26,7 +27,7 @@ export interface WorkspaceState {
|
|
|
26
27
|
splitRatio: number;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
var CHAT_TAB: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false };
|
|
30
|
+
var CHAT_TAB: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false, pinned: true };
|
|
30
31
|
|
|
31
32
|
var DEFAULT_PANE: Pane = { id: "pane-1", tabIds: ["chat"], activeTabId: "chat" };
|
|
32
33
|
|
|
@@ -77,6 +78,7 @@ export function openTab(type: TabType): void {
|
|
|
77
78
|
type: type,
|
|
78
79
|
label: labels[type],
|
|
79
80
|
closeable: type !== "chat",
|
|
81
|
+
pinned: true,
|
|
80
82
|
};
|
|
81
83
|
|
|
82
84
|
var defaultChat = state.tabs.find(function (t) { return t.id === "chat" && !t.sessionId; });
|
|
@@ -135,36 +137,55 @@ export function openSessionTab(sessionId: string, projectSlug: string, title: st
|
|
|
135
137
|
type: "chat",
|
|
136
138
|
label: title || "Session",
|
|
137
139
|
closeable: true,
|
|
140
|
+
pinned: false,
|
|
138
141
|
sessionId: sessionId,
|
|
139
142
|
projectSlug: projectSlug,
|
|
140
143
|
};
|
|
141
144
|
|
|
145
|
+
var previewTab = state.tabs.find(function (t) { return t.type === "chat" && t.sessionId && !t.pinned; });
|
|
142
146
|
var hadDefaultChat = state.tabs.some(function (t) { return t.id === "chat"; });
|
|
143
147
|
|
|
144
|
-
var newTabs
|
|
145
|
-
|
|
146
|
-
: [...state.tabs, tab];
|
|
147
|
-
|
|
148
|
-
var newPanes = state.panes.map(function (p) {
|
|
149
|
-
var updatedTabIds = hadDefaultChat
|
|
150
|
-
? p.tabIds.map(function (id) { return id === "chat" ? tabId : id; })
|
|
151
|
-
: (p.id === state.activePaneId ? [...p.tabIds, tabId] : p.tabIds);
|
|
148
|
+
var newTabs: Tab[];
|
|
149
|
+
var newPanes: Pane[];
|
|
152
150
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
if (previewTab) {
|
|
152
|
+
var oldId = previewTab.id;
|
|
153
|
+
newTabs = state.tabs.map(function (t) { return t.id === oldId ? tab : t; });
|
|
154
|
+
newPanes = state.panes.map(function (p) {
|
|
155
|
+
var updatedTabIds = p.tabIds.map(function (id) { return id === oldId ? tabId : id; });
|
|
156
|
+
return {
|
|
157
|
+
...p,
|
|
158
|
+
tabIds: updatedTabIds,
|
|
159
|
+
activeTabId: p.activeTabId === oldId ? tabId : p.activeTabId,
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
} else if (hadDefaultChat) {
|
|
163
|
+
newTabs = state.tabs.filter(function (t) { return t.id !== "chat"; }).concat([tab]);
|
|
164
|
+
newPanes = state.panes.map(function (p) {
|
|
165
|
+
var updatedTabIds = p.tabIds.map(function (id) { return id === "chat" ? tabId : id; });
|
|
166
|
+
var needsActiveUpdate = p.activeTabId === "chat" || p.id === state.activePaneId;
|
|
167
|
+
return { ...p, tabIds: updatedTabIds, activeTabId: needsActiveUpdate ? tabId : p.activeTabId };
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
newTabs = [...state.tabs, tab];
|
|
171
|
+
newPanes = state.panes.map(function (p) {
|
|
172
|
+
return p.id === state.activePaneId
|
|
173
|
+
? { ...p, tabIds: [...p.tabIds, tabId], activeTabId: tabId }
|
|
174
|
+
: p;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
156
177
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
activeTabId: needsActiveUpdate ? tabId : p.activeTabId,
|
|
161
|
-
};
|
|
162
|
-
});
|
|
178
|
+
return { ...state, tabs: newTabs, panes: newPanes };
|
|
179
|
+
});
|
|
180
|
+
}
|
|
163
181
|
|
|
182
|
+
export function pinTab(tabId: string): void {
|
|
183
|
+
workspaceStore.setState(function (state) {
|
|
164
184
|
return {
|
|
165
185
|
...state,
|
|
166
|
-
tabs:
|
|
167
|
-
|
|
186
|
+
tabs: state.tabs.map(function (t) {
|
|
187
|
+
return t.id === tabId ? { ...t, pinned: true } : t;
|
|
188
|
+
}),
|
|
168
189
|
};
|
|
169
190
|
});
|
|
170
191
|
}
|
|
@@ -198,7 +219,7 @@ export function closeTab(tabId: string): void {
|
|
|
198
219
|
var isLastChatTab = tab.type === "chat" && chatTabCount <= 1;
|
|
199
220
|
|
|
200
221
|
if (isLastChatTab) {
|
|
201
|
-
var replacementTab: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false };
|
|
222
|
+
var replacementTab: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false, pinned: true };
|
|
202
223
|
var replacedTabs = state.tabs.map(function (t) {
|
|
203
224
|
if (t.id === tabId) return replacementTab;
|
|
204
225
|
return t;
|
|
@@ -236,7 +257,7 @@ export function closeTab(tabId: string): void {
|
|
|
236
257
|
|
|
237
258
|
var hasEmptySinglePane = newPanes.length === 1 && newPanes[0].tabIds.length === 0;
|
|
238
259
|
if (hasEmptySinglePane) {
|
|
239
|
-
var defaultTab: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false };
|
|
260
|
+
var defaultTab: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false, pinned: true };
|
|
240
261
|
return {
|
|
241
262
|
tabs: [defaultTab],
|
|
242
263
|
panes: [{ ...newPanes[0], tabIds: ["chat"], activeTabId: "chat" }],
|
|
@@ -468,7 +489,7 @@ export function loadWorkspaceForProject(projectSlug: string | null): void {
|
|
|
468
489
|
urlSyncSuppressed = true;
|
|
469
490
|
workspaceStore.setState(function () {
|
|
470
491
|
return {
|
|
471
|
-
tabs: [{ id: "chat", type: "chat" as TabType, label: "Chat", closeable: false }],
|
|
492
|
+
tabs: [{ id: "chat", type: "chat" as TabType, label: "Chat", closeable: false, pinned: true }],
|
|
472
493
|
panes: [{ id: "pane-1", tabIds: ["chat"], activeTabId: "chat" }],
|
|
473
494
|
activePaneId: "pane-1",
|
|
474
495
|
splitDirection: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "Multi-machine agentic dashboard for Claude Code. Monitor sessions, manage MCP servers and skills, orchestrate across mesh-networked nodes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Aaron Scherer <me@aaronscherer.me>",
|
|
@@ -107,8 +107,13 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
|
|
|
107
107
|
setActiveProject(clientId, activateMsg.projectSlug);
|
|
108
108
|
watchSessionLock(activateMsg.sessionId);
|
|
109
109
|
var activateT0 = Date.now();
|
|
110
|
-
void
|
|
111
|
-
|
|
110
|
+
void Promise.all([
|
|
111
|
+
loadSessionHistory(activateMsg.projectSlug, activateMsg.sessionId),
|
|
112
|
+
getSessionTitle(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
|
|
113
|
+
]).then(function (results) {
|
|
114
|
+
log.session("session:activate history+title: %dms", Date.now() - activateT0);
|
|
115
|
+
var historyResult = results[0];
|
|
116
|
+
var title = results[1];
|
|
112
117
|
var interrupted = wasSessionInterrupted(activateMsg.sessionId);
|
|
113
118
|
if (interrupted) {
|
|
114
119
|
clearInterruptedFlag(activateMsg.sessionId);
|
|
@@ -120,7 +125,7 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
|
|
|
120
125
|
projectSlug: activateMsg.projectSlug,
|
|
121
126
|
sessionId: activateMsg.sessionId,
|
|
122
127
|
messages: historyResult.messages,
|
|
123
|
-
title:
|
|
128
|
+
title: title,
|
|
124
129
|
interrupted: interrupted || undefined,
|
|
125
130
|
busy: busy || undefined,
|
|
126
131
|
busyOwner: busyOwner,
|
|
@@ -133,15 +138,11 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
|
|
|
133
138
|
});
|
|
134
139
|
|
|
135
140
|
void Promise.all([
|
|
136
|
-
getSessionTitle(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
|
|
137
141
|
getSessionUsage(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
|
|
138
142
|
getContextBreakdown(activateMsg.projectSlug, activateMsg.sessionId).catch(function () { return null; }),
|
|
139
143
|
]).then(function (results) {
|
|
140
144
|
try {
|
|
141
|
-
|
|
142
|
-
sendTo(clientId, { type: "session:history", projectSlug: activateMsg.projectSlug, sessionId: activateMsg.sessionId, messages: [], title: results[0] as string });
|
|
143
|
-
}
|
|
144
|
-
var usage = results[1];
|
|
145
|
+
var usage = results[0];
|
|
145
146
|
if (usage) {
|
|
146
147
|
sendTo(clientId, {
|
|
147
148
|
type: "chat:context_usage",
|
|
@@ -156,7 +157,7 @@ registerHandler("session", function (clientId: string, message: ClientMessage) {
|
|
|
156
157
|
log.session("Error sending context usage: %O", err);
|
|
157
158
|
}
|
|
158
159
|
try {
|
|
159
|
-
var breakdown = results[
|
|
160
|
+
var breakdown = results[1];
|
|
160
161
|
if (breakdown) {
|
|
161
162
|
sendTo(clientId, {
|
|
162
163
|
type: "chat:context_breakdown",
|