@cryptiklemur/lattice 1.3.0 → 1.5.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/bun.lock +776 -2
- package/client/index.html +1 -13
- package/client/package.json +7 -1
- package/client/src/App.tsx +2 -0
- package/client/src/commands.ts +36 -0
- package/client/src/components/analytics/AnalyticsView.tsx +61 -0
- package/client/src/components/analytics/ChartCard.tsx +22 -0
- package/client/src/components/analytics/PeriodSelector.tsx +42 -0
- package/client/src/components/analytics/QuickStats.tsx +99 -0
- package/client/src/components/analytics/charts/CostAreaChart.tsx +83 -0
- package/client/src/components/analytics/charts/CostDistributionChart.tsx +62 -0
- package/client/src/components/analytics/charts/CostDonutChart.tsx +93 -0
- package/client/src/components/analytics/charts/CumulativeCostChart.tsx +62 -0
- package/client/src/components/analytics/charts/SessionBubbleChart.tsx +122 -0
- package/client/src/components/chat/AttachmentChips.tsx +116 -0
- package/client/src/components/chat/ChatInput.tsx +250 -73
- package/client/src/components/chat/ChatView.tsx +242 -10
- package/client/src/components/chat/CommandPalette.tsx +162 -0
- package/client/src/components/chat/Message.tsx +23 -2
- package/client/src/components/chat/PromptQuestion.tsx +271 -0
- package/client/src/components/chat/TodoCard.tsx +57 -0
- package/client/src/components/chat/ToolResultRenderer.tsx +2 -1
- package/client/src/components/chat/VoiceRecorder.tsx +85 -0
- package/client/src/components/dashboard/DashboardView.tsx +5 -0
- package/client/src/components/project-settings/ProjectMemory.tsx +12 -2
- package/client/src/components/project-settings/ProjectNotifications.tsx +48 -0
- package/client/src/components/project-settings/ProjectRules.tsx +10 -1
- package/client/src/components/project-settings/ProjectSettingsView.tsx +6 -0
- package/client/src/components/settings/Appearance.tsx +1 -0
- package/client/src/components/settings/ClaudeSettings.tsx +10 -0
- package/client/src/components/settings/Editor.tsx +123 -0
- package/client/src/components/settings/GlobalMcp.tsx +10 -1
- package/client/src/components/settings/GlobalMemory.tsx +19 -0
- package/client/src/components/settings/GlobalRules.tsx +149 -0
- package/client/src/components/settings/GlobalSkills.tsx +10 -0
- package/client/src/components/settings/Notifications.tsx +88 -0
- package/client/src/components/settings/SettingsView.tsx +12 -0
- package/client/src/components/settings/skill-shared.tsx +2 -1
- package/client/src/components/setup/SetupWizard.tsx +1 -1
- package/client/src/components/sidebar/NodeSettingsModal.tsx +23 -1
- package/client/src/components/sidebar/ProjectDropdown.tsx +176 -27
- package/client/src/components/sidebar/SettingsSidebar.tsx +11 -1
- package/client/src/components/sidebar/Sidebar.tsx +43 -2
- package/client/src/components/sidebar/UserIsland.tsx +18 -7
- package/client/src/components/ui/UpdatePrompt.tsx +47 -0
- package/client/src/components/workspace/FileBrowser.tsx +174 -0
- package/client/src/components/workspace/FileTree.tsx +129 -0
- package/client/src/components/workspace/FileViewer.tsx +211 -0
- package/client/src/components/workspace/NoteCard.tsx +119 -0
- package/client/src/components/workspace/NotesView.tsx +102 -0
- package/client/src/components/workspace/ScheduledTasksView.tsx +117 -0
- package/client/src/components/workspace/SplitPane.tsx +81 -0
- package/client/src/components/workspace/TabBar.tsx +185 -0
- package/client/src/components/workspace/TaskCard.tsx +158 -0
- package/client/src/components/workspace/TaskEditModal.tsx +114 -0
- package/client/src/components/{panels/Terminal.tsx → workspace/TerminalInstance.tsx} +50 -7
- package/client/src/components/workspace/TerminalView.tsx +110 -0
- package/client/src/components/workspace/WorkspaceView.tsx +116 -0
- package/client/src/hooks/useAnalytics.ts +75 -0
- package/client/src/hooks/useAttachments.ts +280 -0
- package/client/src/hooks/useEditorConfig.ts +28 -0
- package/client/src/hooks/useIdleDetection.ts +44 -0
- package/client/src/hooks/useInstallPrompt.ts +53 -0
- package/client/src/hooks/useNotifications.ts +54 -0
- package/client/src/hooks/useOnline.ts +6 -0
- package/client/src/hooks/useSession.ts +110 -4
- package/client/src/hooks/useSpinnerVerb.ts +36 -0
- package/client/src/hooks/useSwipeDrawer.ts +275 -0
- package/client/src/hooks/useVoiceRecorder.ts +123 -0
- package/client/src/hooks/useWorkspace.ts +48 -0
- package/client/src/providers/WebSocketProvider.tsx +18 -0
- package/client/src/router.tsx +52 -20
- package/client/src/stores/analytics.ts +54 -0
- package/client/src/stores/session.ts +136 -0
- package/client/src/stores/sidebar.ts +11 -2
- package/client/src/stores/workspace.ts +254 -0
- package/client/src/styles/global.css +123 -0
- package/client/src/utils/editorUrl.ts +62 -0
- package/client/vite.config.ts +54 -1
- package/package.json +1 -1
- package/server/src/analytics/engine.ts +491 -0
- package/server/src/daemon.ts +12 -1
- package/server/src/features/scheduler.ts +23 -0
- package/server/src/features/sticky-notes.ts +5 -3
- package/server/src/handlers/analytics.ts +34 -0
- package/server/src/handlers/attachment.ts +172 -0
- package/server/src/handlers/chat.ts +43 -2
- package/server/src/handlers/editor.ts +40 -0
- package/server/src/handlers/fs.ts +10 -2
- package/server/src/handlers/memory.ts +3 -0
- package/server/src/handlers/notes.ts +4 -2
- package/server/src/handlers/scheduler.ts +18 -1
- package/server/src/handlers/session.ts +14 -8
- package/server/src/handlers/settings.ts +37 -2
- package/server/src/handlers/terminal.ts +13 -6
- package/server/src/project/pty-worker.cjs +83 -0
- package/server/src/project/sdk-bridge.ts +266 -11
- package/server/src/project/session.ts +4 -4
- package/server/src/project/terminal.ts +78 -34
- package/shared/src/analytics.ts +24 -0
- package/shared/src/index.ts +1 -0
- package/shared/src/messages.ts +173 -4
- package/shared/src/models.ts +27 -1
- package/shared/src/project-settings.ts +1 -1
- package/tp.js +19 -0
- package/client/public/manifest.json +0 -24
- package/client/public/sw.js +0 -61
- package/client/src/components/panels/FileBrowser.tsx +0 -241
- package/client/src/components/panels/StickyNotes.tsx +0 -187
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Store } from "@tanstack/react-store";
|
|
2
|
+
|
|
3
|
+
export type TabType = "chat" | "files" | "terminal" | "notes" | "tasks";
|
|
4
|
+
|
|
5
|
+
export interface Tab {
|
|
6
|
+
id: string;
|
|
7
|
+
type: TabType;
|
|
8
|
+
label: string;
|
|
9
|
+
closeable: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Pane {
|
|
13
|
+
id: string;
|
|
14
|
+
tabIds: string[];
|
|
15
|
+
activeTabId: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface WorkspaceState {
|
|
19
|
+
tabs: Tab[];
|
|
20
|
+
panes: Pane[];
|
|
21
|
+
activePaneId: string;
|
|
22
|
+
splitDirection: "horizontal" | "vertical" | null;
|
|
23
|
+
splitRatio: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var CHAT_TAB: Tab = { id: "chat", type: "chat", label: "Chat", closeable: false };
|
|
27
|
+
|
|
28
|
+
var DEFAULT_PANE: Pane = { id: "pane-1", tabIds: ["chat"], activeTabId: "chat" };
|
|
29
|
+
|
|
30
|
+
var workspaceStore = new Store<WorkspaceState>({
|
|
31
|
+
tabs: [CHAT_TAB],
|
|
32
|
+
panes: [DEFAULT_PANE],
|
|
33
|
+
activePaneId: "pane-1",
|
|
34
|
+
splitDirection: null,
|
|
35
|
+
splitRatio: 0.5,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export function getWorkspaceStore(): Store<WorkspaceState> {
|
|
39
|
+
return workspaceStore;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function openTab(type: TabType): void {
|
|
43
|
+
workspaceStore.setState(function (state) {
|
|
44
|
+
var existing = state.tabs.find(function (t) { return t.type === type; });
|
|
45
|
+
if (existing) {
|
|
46
|
+
var paneWithTab = state.panes.find(function (p) {
|
|
47
|
+
return p.tabIds.indexOf(existing!.id) !== -1;
|
|
48
|
+
});
|
|
49
|
+
if (paneWithTab) {
|
|
50
|
+
return {
|
|
51
|
+
...state,
|
|
52
|
+
activePaneId: paneWithTab.id,
|
|
53
|
+
panes: state.panes.map(function (p) {
|
|
54
|
+
if (p.id === paneWithTab!.id) {
|
|
55
|
+
return { ...p, activeTabId: existing!.id };
|
|
56
|
+
}
|
|
57
|
+
return p;
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return state;
|
|
62
|
+
}
|
|
63
|
+
var labels: Record<TabType, string> = {
|
|
64
|
+
chat: "Chat",
|
|
65
|
+
files: "Files",
|
|
66
|
+
terminal: "Terminal",
|
|
67
|
+
notes: "Notes",
|
|
68
|
+
tasks: "Tasks",
|
|
69
|
+
};
|
|
70
|
+
var tab: Tab = {
|
|
71
|
+
id: type,
|
|
72
|
+
type: type,
|
|
73
|
+
label: labels[type],
|
|
74
|
+
closeable: type !== "chat",
|
|
75
|
+
};
|
|
76
|
+
var newPanes = state.panes.map(function (p) {
|
|
77
|
+
if (p.id === state.activePaneId) {
|
|
78
|
+
return {
|
|
79
|
+
...p,
|
|
80
|
+
tabIds: [...p.tabIds, tab.id],
|
|
81
|
+
activeTabId: tab.id,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return p;
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
...state,
|
|
88
|
+
tabs: [...state.tabs, tab],
|
|
89
|
+
panes: newPanes,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function closeTab(tabId: string): void {
|
|
95
|
+
workspaceStore.setState(function (state) {
|
|
96
|
+
var tab = state.tabs.find(function (t) { return t.id === tabId; });
|
|
97
|
+
if (!tab || !tab.closeable) return state;
|
|
98
|
+
|
|
99
|
+
var filteredTabs = state.tabs.filter(function (t) { return t.id !== tabId; });
|
|
100
|
+
var newPanes = state.panes.map(function (p) {
|
|
101
|
+
var idx = p.tabIds.indexOf(tabId);
|
|
102
|
+
if (idx === -1) return p;
|
|
103
|
+
var newTabIds = p.tabIds.filter(function (id) { return id !== tabId; });
|
|
104
|
+
var newActiveTabId = p.activeTabId === tabId
|
|
105
|
+
? (newTabIds.length > 0 ? newTabIds[newTabIds.length - 1] : "")
|
|
106
|
+
: p.activeTabId;
|
|
107
|
+
return { ...p, tabIds: newTabIds, activeTabId: newActiveTabId };
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
var emptyPane = newPanes.find(function (p) { return p.tabIds.length === 0; });
|
|
111
|
+
if (emptyPane && newPanes.length > 1) {
|
|
112
|
+
var remainingPanes = newPanes.filter(function (p) { return p.tabIds.length > 0; });
|
|
113
|
+
return {
|
|
114
|
+
tabs: filteredTabs,
|
|
115
|
+
panes: remainingPanes,
|
|
116
|
+
activePaneId: remainingPanes[0].id,
|
|
117
|
+
splitDirection: null,
|
|
118
|
+
splitRatio: 0.5,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
...state,
|
|
124
|
+
tabs: filteredTabs,
|
|
125
|
+
panes: newPanes,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function setActiveTab(tabId: string): void {
|
|
131
|
+
workspaceStore.setState(function (state) {
|
|
132
|
+
var paneWithTab = state.panes.find(function (p) {
|
|
133
|
+
return p.tabIds.indexOf(tabId) !== -1;
|
|
134
|
+
});
|
|
135
|
+
if (!paneWithTab) return state;
|
|
136
|
+
return {
|
|
137
|
+
...state,
|
|
138
|
+
activePaneId: paneWithTab.id,
|
|
139
|
+
panes: state.panes.map(function (p) {
|
|
140
|
+
if (p.id === paneWithTab!.id) {
|
|
141
|
+
return { ...p, activeTabId: tabId };
|
|
142
|
+
}
|
|
143
|
+
return p;
|
|
144
|
+
}),
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function resetWorkspace(): void {
|
|
150
|
+
workspaceStore.setState(function () {
|
|
151
|
+
return {
|
|
152
|
+
tabs: [CHAT_TAB],
|
|
153
|
+
panes: [{ id: "pane-1", tabIds: ["chat"], activeTabId: "chat" }],
|
|
154
|
+
activePaneId: "pane-1",
|
|
155
|
+
splitDirection: null,
|
|
156
|
+
splitRatio: 0.5,
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function splitPane(tabId: string, direction: "horizontal" | "vertical"): void {
|
|
162
|
+
workspaceStore.setState(function (state) {
|
|
163
|
+
if (state.panes.length >= 2) return state;
|
|
164
|
+
|
|
165
|
+
var sourcePane = state.panes.find(function (p) {
|
|
166
|
+
return p.tabIds.indexOf(tabId) !== -1;
|
|
167
|
+
});
|
|
168
|
+
if (!sourcePane) return state;
|
|
169
|
+
if (sourcePane.tabIds.length < 2) return state;
|
|
170
|
+
|
|
171
|
+
var newPaneId = "pane-" + Date.now();
|
|
172
|
+
var newSourceTabIds = sourcePane.tabIds.filter(function (id) { return id !== tabId; });
|
|
173
|
+
var newSourceActiveTabId = sourcePane.activeTabId === tabId
|
|
174
|
+
? newSourceTabIds[newSourceTabIds.length - 1]
|
|
175
|
+
: sourcePane.activeTabId;
|
|
176
|
+
|
|
177
|
+
var updatedSourcePane: Pane = {
|
|
178
|
+
...sourcePane,
|
|
179
|
+
tabIds: newSourceTabIds,
|
|
180
|
+
activeTabId: newSourceActiveTabId,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
var newPane: Pane = {
|
|
184
|
+
id: newPaneId,
|
|
185
|
+
tabIds: [tabId],
|
|
186
|
+
activeTabId: tabId,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
var newPanes = state.panes.map(function (p) {
|
|
190
|
+
if (p.id === sourcePane!.id) return updatedSourcePane;
|
|
191
|
+
return p;
|
|
192
|
+
});
|
|
193
|
+
newPanes.push(newPane);
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
...state,
|
|
197
|
+
panes: newPanes,
|
|
198
|
+
activePaneId: newPaneId,
|
|
199
|
+
splitDirection: direction,
|
|
200
|
+
splitRatio: 0.5,
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function closePane(paneId: string): void {
|
|
206
|
+
workspaceStore.setState(function (state) {
|
|
207
|
+
if (state.panes.length <= 1) return state;
|
|
208
|
+
|
|
209
|
+
var closingPane = state.panes.find(function (p) { return p.id === paneId; });
|
|
210
|
+
var remainingPane = state.panes.find(function (p) { return p.id !== paneId; });
|
|
211
|
+
if (!closingPane || !remainingPane) return state;
|
|
212
|
+
|
|
213
|
+
var mergedTabIds = [...remainingPane.tabIds, ...closingPane.tabIds];
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
...state,
|
|
217
|
+
panes: [{
|
|
218
|
+
...remainingPane,
|
|
219
|
+
tabIds: mergedTabIds,
|
|
220
|
+
}],
|
|
221
|
+
activePaneId: remainingPane.id,
|
|
222
|
+
splitDirection: null,
|
|
223
|
+
splitRatio: 0.5,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function setPaneActiveTab(paneId: string, tabId: string): void {
|
|
229
|
+
workspaceStore.setState(function (state) {
|
|
230
|
+
return {
|
|
231
|
+
...state,
|
|
232
|
+
activePaneId: paneId,
|
|
233
|
+
panes: state.panes.map(function (p) {
|
|
234
|
+
if (p.id === paneId) {
|
|
235
|
+
return { ...p, activeTabId: tabId };
|
|
236
|
+
}
|
|
237
|
+
return p;
|
|
238
|
+
}),
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function setSplitRatio(ratio: number): void {
|
|
244
|
+
var clamped = Math.min(0.8, Math.max(0.2, ratio));
|
|
245
|
+
workspaceStore.setState(function (state) {
|
|
246
|
+
return { ...state, splitRatio: clamped };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function setActivePaneId(paneId: string): void {
|
|
251
|
+
workspaceStore.setState(function (state) {
|
|
252
|
+
return { ...state, activePaneId: paneId };
|
|
253
|
+
});
|
|
254
|
+
}
|
|
@@ -101,6 +101,13 @@ html, body {
|
|
|
101
101
|
font-family: var(--font-mono);
|
|
102
102
|
font-size: 13px;
|
|
103
103
|
line-height: 1.6;
|
|
104
|
+
overflow-x: auto;
|
|
105
|
+
max-width: 100%;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.prose {
|
|
109
|
+
overflow-wrap: break-word;
|
|
110
|
+
word-break: break-word;
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
.prose code {
|
|
@@ -116,6 +123,79 @@ html, body {
|
|
|
116
123
|
background: transparent;
|
|
117
124
|
}
|
|
118
125
|
|
|
126
|
+
.prose .table-wrapper {
|
|
127
|
+
overflow-x: auto;
|
|
128
|
+
margin: 0.75em 0;
|
|
129
|
+
border-radius: 0.5rem;
|
|
130
|
+
border: 1px solid oklch(from var(--color-base-content) l c h / 0.08);
|
|
131
|
+
background: oklch(from var(--color-base-100) l c h / 0.6);
|
|
132
|
+
scrollbar-width: thin;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.prose table {
|
|
136
|
+
border-collapse: collapse;
|
|
137
|
+
width: 100%;
|
|
138
|
+
font-size: 12px;
|
|
139
|
+
font-family: var(--font-mono);
|
|
140
|
+
margin: 0;
|
|
141
|
+
border: none;
|
|
142
|
+
line-height: 1.5;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.prose thead {
|
|
146
|
+
background: oklch(from var(--color-base-content) l c h / 0.04);
|
|
147
|
+
position: sticky;
|
|
148
|
+
top: 0;
|
|
149
|
+
z-index: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.prose th {
|
|
153
|
+
text-align: left;
|
|
154
|
+
font-weight: 600;
|
|
155
|
+
font-size: 10px;
|
|
156
|
+
text-transform: uppercase;
|
|
157
|
+
letter-spacing: 0.06em;
|
|
158
|
+
padding: 8px 14px;
|
|
159
|
+
color: oklch(from var(--color-base-content) l c h / 0.35);
|
|
160
|
+
border-bottom: 1px solid oklch(from var(--color-base-content) l c h / 0.1);
|
|
161
|
+
white-space: nowrap;
|
|
162
|
+
user-select: none;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.prose td {
|
|
166
|
+
padding: 8px 14px;
|
|
167
|
+
color: oklch(from var(--color-base-content) l c h / 0.65);
|
|
168
|
+
border-bottom: 1px solid oklch(from var(--color-base-content) l c h / 0.04);
|
|
169
|
+
vertical-align: top;
|
|
170
|
+
white-space: normal;
|
|
171
|
+
min-width: 60px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.prose td code {
|
|
175
|
+
font-size: 11px;
|
|
176
|
+
white-space: nowrap;
|
|
177
|
+
padding: 1px 5px;
|
|
178
|
+
border-radius: 3px;
|
|
179
|
+
background: oklch(from var(--color-primary) l c h / 0.08);
|
|
180
|
+
color: oklch(from var(--color-primary) l c h / 0.8);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.prose tbody tr:last-child td {
|
|
184
|
+
border-bottom: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.prose tbody tr {
|
|
188
|
+
transition: background-color 150ms ease;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.prose tbody tr:nth-child(even) {
|
|
192
|
+
background: oklch(from var(--color-base-content) l c h / 0.015);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.prose tbody tr:hover {
|
|
196
|
+
background: oklch(from var(--color-primary) l c h / 0.04);
|
|
197
|
+
}
|
|
198
|
+
|
|
119
199
|
.bg-lattice-grid {
|
|
120
200
|
background-color: transparent;
|
|
121
201
|
}
|
|
@@ -156,6 +236,40 @@ html, body {
|
|
|
156
236
|
}
|
|
157
237
|
}
|
|
158
238
|
|
|
239
|
+
/* Override DaisyUI drawer for smooth slide transitions on mobile.
|
|
240
|
+
DaisyUI defaults use opacity on the whole drawer-side which causes
|
|
241
|
+
an instant vanish. We keep drawer-side always opacity:1 and instead
|
|
242
|
+
slide the content panel + fade the overlay independently. */
|
|
243
|
+
@media (max-width: 1023px) {
|
|
244
|
+
/* Keep the container itself always fully opaque so the slide is visible.
|
|
245
|
+
Use visibility + pointer-events to control interactivity. */
|
|
246
|
+
.drawer-side {
|
|
247
|
+
opacity: 1 !important;
|
|
248
|
+
transition: visibility 0s linear 0.25s !important;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.drawer-toggle:checked ~ .drawer-side {
|
|
252
|
+
transition: visibility 0s linear 0s !important;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Sidebar content: slide in/out using translate (matches DaisyUI's property) */
|
|
256
|
+
.drawer-side > *:not(.drawer-overlay) {
|
|
257
|
+
will-change: translate;
|
|
258
|
+
transition: translate 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* Overlay: fade in/out */
|
|
262
|
+
.drawer-side > .drawer-overlay {
|
|
263
|
+
will-change: opacity;
|
|
264
|
+
opacity: 0;
|
|
265
|
+
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.drawer-toggle:checked ~ .drawer-side > .drawer-overlay {
|
|
269
|
+
opacity: 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
159
273
|
.scrollbar-hidden {
|
|
160
274
|
scrollbar-width: none;
|
|
161
275
|
-ms-overflow-style: none;
|
|
@@ -165,6 +279,15 @@ html, body {
|
|
|
165
279
|
display: none;
|
|
166
280
|
}
|
|
167
281
|
|
|
282
|
+
@keyframes waveform {
|
|
283
|
+
0%, 100% { height: 4px; opacity: 0.3; }
|
|
284
|
+
50% { height: 16px; opacity: 0.8; }
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.animate-waveform {
|
|
288
|
+
animation: waveform 0.6s ease-in-out infinite;
|
|
289
|
+
}
|
|
290
|
+
|
|
168
291
|
@media (prefers-reduced-motion: reduce) {
|
|
169
292
|
*, *::before, *::after {
|
|
170
293
|
animation-duration: 0.01ms !important;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
var JETBRAINS_IDS: Record<string, string> = {
|
|
2
|
+
webstorm: "webstorm",
|
|
3
|
+
intellij: "idea",
|
|
4
|
+
pycharm: "pycharm",
|
|
5
|
+
goland: "goland",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function toWindowsPath(linuxPath: string, wslDistro: string): string {
|
|
9
|
+
return "\\\\" + "wsl.localhost\\" + wslDistro + linuxPath.replace(/\//g, "\\");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function buildJetBrainsUrl(ideId: string, filePath: string, line?: number, projectName?: string): string {
|
|
13
|
+
var url = "jetbrains://" + ideId + "/navigate/reference?";
|
|
14
|
+
if (projectName) {
|
|
15
|
+
url += "project=" + encodeURIComponent(projectName);
|
|
16
|
+
}
|
|
17
|
+
if (filePath) {
|
|
18
|
+
url += (projectName ? "&" : "") + "path=" + encodeURIComponent(filePath);
|
|
19
|
+
if (line) {
|
|
20
|
+
url += "&line=" + line;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return url;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface EditorUrlOptions {
|
|
27
|
+
editorType: string;
|
|
28
|
+
projectPath: string;
|
|
29
|
+
filePath: string;
|
|
30
|
+
line?: number;
|
|
31
|
+
wslDistro?: string;
|
|
32
|
+
ideProjectName?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getEditorUrl(editorType: string, projectPath: string, filePath: string, line?: number, wslDistro?: string, ideProjectName?: string): string | null {
|
|
36
|
+
var fullPath = filePath === "." ? projectPath : projectPath + "/" + filePath;
|
|
37
|
+
var resolvedPath = wslDistro ? toWindowsPath(fullPath, wslDistro) : fullPath;
|
|
38
|
+
|
|
39
|
+
var jetbrainsId = JETBRAINS_IDS[editorType];
|
|
40
|
+
if (jetbrainsId) {
|
|
41
|
+
if (ideProjectName) {
|
|
42
|
+
var jbPath = filePath === "." ? "" : filePath;
|
|
43
|
+
return buildJetBrainsUrl(jetbrainsId, jbPath, line, ideProjectName);
|
|
44
|
+
}
|
|
45
|
+
return buildJetBrainsUrl(jetbrainsId, resolvedPath, line);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (editorType === "vscode" || editorType === "vscode-insiders" || editorType === "cursor") {
|
|
49
|
+
var scheme = editorType;
|
|
50
|
+
var isFile = filePath !== ".";
|
|
51
|
+
var lineSuffix = isFile ? ":" + (line || 1) : "";
|
|
52
|
+
if (wslDistro) {
|
|
53
|
+
return scheme + "://vscode-remote/wsl+" + wslDistro + fullPath + lineSuffix;
|
|
54
|
+
}
|
|
55
|
+
return scheme + "://file/" + resolvedPath + lineSuffix;
|
|
56
|
+
}
|
|
57
|
+
if (editorType === "sublime") {
|
|
58
|
+
return "subl://open?url=file://" + encodeURIComponent(resolvedPath) + (line ? "&line=" + line : "");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
package/client/vite.config.ts
CHANGED
|
@@ -1,9 +1,53 @@
|
|
|
1
1
|
import { defineConfig } from "vite";
|
|
2
2
|
import react from "@vitejs/plugin-react";
|
|
3
3
|
import tailwindcss from "@tailwindcss/vite";
|
|
4
|
+
import { VitePWA } from "vite-plugin-pwa";
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
|
-
plugins: [
|
|
7
|
+
plugins: [
|
|
8
|
+
tailwindcss(),
|
|
9
|
+
react(),
|
|
10
|
+
VitePWA({
|
|
11
|
+
registerType: "prompt",
|
|
12
|
+
workbox: {
|
|
13
|
+
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024,
|
|
14
|
+
globPatterns: ["**/*.{js,css,html,svg,png,woff2}"],
|
|
15
|
+
navigateFallback: "/index.html",
|
|
16
|
+
navigateFallbackDenylist: [/^\/ws/, /^\/api/],
|
|
17
|
+
runtimeCaching: [
|
|
18
|
+
{
|
|
19
|
+
urlPattern: /^https?:\/\/.*\.(?:js|css|woff2)$/,
|
|
20
|
+
handler: "CacheFirst",
|
|
21
|
+
options: {
|
|
22
|
+
cacheName: "static-assets",
|
|
23
|
+
expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
urlPattern: /^https?:\/\/.*\.(?:svg|png|jpg|jpeg|gif|webp)$/,
|
|
28
|
+
handler: "CacheFirst",
|
|
29
|
+
options: {
|
|
30
|
+
cacheName: "images",
|
|
31
|
+
expiration: { maxEntries: 50, maxAgeSeconds: 30 * 24 * 60 * 60 },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
manifest: {
|
|
37
|
+
name: "Lattice",
|
|
38
|
+
short_name: "Lattice",
|
|
39
|
+
description: "Multi-machine agentic dashboard for Claude Code",
|
|
40
|
+
display: "standalone",
|
|
41
|
+
start_url: "/",
|
|
42
|
+
theme_color: "#0d0d0d",
|
|
43
|
+
background_color: "#0d0d0d",
|
|
44
|
+
icons: [
|
|
45
|
+
{ src: "/icons/icon-192.svg", sizes: "192x192", type: "image/svg+xml", purpose: "maskable" },
|
|
46
|
+
{ src: "/icons/icon-512.svg", sizes: "512x512", type: "image/svg+xml", purpose: "maskable" },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
7
51
|
server: {
|
|
8
52
|
host: "0.0.0.0",
|
|
9
53
|
open: true,
|
|
@@ -11,9 +55,18 @@ export default defineConfig({
|
|
|
11
55
|
"/ws": {
|
|
12
56
|
target: "ws://localhost:7654",
|
|
13
57
|
ws: true,
|
|
58
|
+
configure: function (proxy) {
|
|
59
|
+
proxy.on("error", function () {});
|
|
60
|
+
proxy.on("proxyReqWs", function (_proxyReq, _req, socket) {
|
|
61
|
+
socket.on("error", function () {});
|
|
62
|
+
});
|
|
63
|
+
},
|
|
14
64
|
},
|
|
15
65
|
"/api": {
|
|
16
66
|
target: "http://localhost:7654",
|
|
67
|
+
configure: function (proxy) {
|
|
68
|
+
proxy.on("error", function () {});
|
|
69
|
+
},
|
|
17
70
|
},
|
|
18
71
|
},
|
|
19
72
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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>",
|