@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.
Files changed (109) hide show
  1. package/bun.lock +776 -2
  2. package/client/index.html +1 -13
  3. package/client/package.json +7 -1
  4. package/client/src/App.tsx +2 -0
  5. package/client/src/commands.ts +36 -0
  6. package/client/src/components/analytics/AnalyticsView.tsx +61 -0
  7. package/client/src/components/analytics/ChartCard.tsx +22 -0
  8. package/client/src/components/analytics/PeriodSelector.tsx +42 -0
  9. package/client/src/components/analytics/QuickStats.tsx +99 -0
  10. package/client/src/components/analytics/charts/CostAreaChart.tsx +83 -0
  11. package/client/src/components/analytics/charts/CostDistributionChart.tsx +62 -0
  12. package/client/src/components/analytics/charts/CostDonutChart.tsx +93 -0
  13. package/client/src/components/analytics/charts/CumulativeCostChart.tsx +62 -0
  14. package/client/src/components/analytics/charts/SessionBubbleChart.tsx +122 -0
  15. package/client/src/components/chat/AttachmentChips.tsx +116 -0
  16. package/client/src/components/chat/ChatInput.tsx +250 -73
  17. package/client/src/components/chat/ChatView.tsx +242 -10
  18. package/client/src/components/chat/CommandPalette.tsx +162 -0
  19. package/client/src/components/chat/Message.tsx +23 -2
  20. package/client/src/components/chat/PromptQuestion.tsx +271 -0
  21. package/client/src/components/chat/TodoCard.tsx +57 -0
  22. package/client/src/components/chat/ToolResultRenderer.tsx +2 -1
  23. package/client/src/components/chat/VoiceRecorder.tsx +85 -0
  24. package/client/src/components/dashboard/DashboardView.tsx +5 -0
  25. package/client/src/components/project-settings/ProjectMemory.tsx +12 -2
  26. package/client/src/components/project-settings/ProjectNotifications.tsx +48 -0
  27. package/client/src/components/project-settings/ProjectRules.tsx +10 -1
  28. package/client/src/components/project-settings/ProjectSettingsView.tsx +6 -0
  29. package/client/src/components/settings/Appearance.tsx +1 -0
  30. package/client/src/components/settings/ClaudeSettings.tsx +10 -0
  31. package/client/src/components/settings/Editor.tsx +123 -0
  32. package/client/src/components/settings/GlobalMcp.tsx +10 -1
  33. package/client/src/components/settings/GlobalMemory.tsx +19 -0
  34. package/client/src/components/settings/GlobalRules.tsx +149 -0
  35. package/client/src/components/settings/GlobalSkills.tsx +10 -0
  36. package/client/src/components/settings/Notifications.tsx +88 -0
  37. package/client/src/components/settings/SettingsView.tsx +12 -0
  38. package/client/src/components/settings/skill-shared.tsx +2 -1
  39. package/client/src/components/setup/SetupWizard.tsx +1 -1
  40. package/client/src/components/sidebar/NodeSettingsModal.tsx +23 -1
  41. package/client/src/components/sidebar/ProjectDropdown.tsx +176 -27
  42. package/client/src/components/sidebar/SettingsSidebar.tsx +11 -1
  43. package/client/src/components/sidebar/Sidebar.tsx +43 -2
  44. package/client/src/components/sidebar/UserIsland.tsx +18 -7
  45. package/client/src/components/ui/UpdatePrompt.tsx +47 -0
  46. package/client/src/components/workspace/FileBrowser.tsx +174 -0
  47. package/client/src/components/workspace/FileTree.tsx +129 -0
  48. package/client/src/components/workspace/FileViewer.tsx +211 -0
  49. package/client/src/components/workspace/NoteCard.tsx +119 -0
  50. package/client/src/components/workspace/NotesView.tsx +102 -0
  51. package/client/src/components/workspace/ScheduledTasksView.tsx +117 -0
  52. package/client/src/components/workspace/SplitPane.tsx +81 -0
  53. package/client/src/components/workspace/TabBar.tsx +185 -0
  54. package/client/src/components/workspace/TaskCard.tsx +158 -0
  55. package/client/src/components/workspace/TaskEditModal.tsx +114 -0
  56. package/client/src/components/{panels/Terminal.tsx → workspace/TerminalInstance.tsx} +50 -7
  57. package/client/src/components/workspace/TerminalView.tsx +110 -0
  58. package/client/src/components/workspace/WorkspaceView.tsx +116 -0
  59. package/client/src/hooks/useAnalytics.ts +75 -0
  60. package/client/src/hooks/useAttachments.ts +280 -0
  61. package/client/src/hooks/useEditorConfig.ts +28 -0
  62. package/client/src/hooks/useIdleDetection.ts +44 -0
  63. package/client/src/hooks/useInstallPrompt.ts +53 -0
  64. package/client/src/hooks/useNotifications.ts +54 -0
  65. package/client/src/hooks/useOnline.ts +6 -0
  66. package/client/src/hooks/useSession.ts +110 -4
  67. package/client/src/hooks/useSpinnerVerb.ts +36 -0
  68. package/client/src/hooks/useSwipeDrawer.ts +275 -0
  69. package/client/src/hooks/useVoiceRecorder.ts +123 -0
  70. package/client/src/hooks/useWorkspace.ts +48 -0
  71. package/client/src/providers/WebSocketProvider.tsx +18 -0
  72. package/client/src/router.tsx +52 -20
  73. package/client/src/stores/analytics.ts +54 -0
  74. package/client/src/stores/session.ts +136 -0
  75. package/client/src/stores/sidebar.ts +11 -2
  76. package/client/src/stores/workspace.ts +254 -0
  77. package/client/src/styles/global.css +123 -0
  78. package/client/src/utils/editorUrl.ts +62 -0
  79. package/client/vite.config.ts +54 -1
  80. package/package.json +1 -1
  81. package/server/src/analytics/engine.ts +491 -0
  82. package/server/src/daemon.ts +12 -1
  83. package/server/src/features/scheduler.ts +23 -0
  84. package/server/src/features/sticky-notes.ts +5 -3
  85. package/server/src/handlers/analytics.ts +34 -0
  86. package/server/src/handlers/attachment.ts +172 -0
  87. package/server/src/handlers/chat.ts +43 -2
  88. package/server/src/handlers/editor.ts +40 -0
  89. package/server/src/handlers/fs.ts +10 -2
  90. package/server/src/handlers/memory.ts +3 -0
  91. package/server/src/handlers/notes.ts +4 -2
  92. package/server/src/handlers/scheduler.ts +18 -1
  93. package/server/src/handlers/session.ts +14 -8
  94. package/server/src/handlers/settings.ts +37 -2
  95. package/server/src/handlers/terminal.ts +13 -6
  96. package/server/src/project/pty-worker.cjs +83 -0
  97. package/server/src/project/sdk-bridge.ts +266 -11
  98. package/server/src/project/session.ts +4 -4
  99. package/server/src/project/terminal.ts +78 -34
  100. package/shared/src/analytics.ts +24 -0
  101. package/shared/src/index.ts +1 -0
  102. package/shared/src/messages.ts +173 -4
  103. package/shared/src/models.ts +27 -1
  104. package/shared/src/project-settings.ts +1 -1
  105. package/tp.js +19 -0
  106. package/client/public/manifest.json +0 -24
  107. package/client/public/sw.js +0 -61
  108. package/client/src/components/panels/FileBrowser.tsx +0 -241
  109. 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
+ }
@@ -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: [tailwindcss(), react()],
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.0",
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>",