@cryptiklemur/lattice 1.18.6 → 1.20.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.
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
import { useProjects } from "../../hooks/useProjects";
|
|
8
8
|
import { useSidebar } from "../../hooks/useSidebar";
|
|
9
9
|
import { useEditorConfig } from "../../hooks/useEditorConfig";
|
|
10
|
-
import { getEditorUrl } from "../../utils/editorUrl";
|
|
10
|
+
import { getEditorUrl, isJetBrainsEditor } from "../../utils/editorUrl";
|
|
11
|
+
import { useWebSocket } from "../../hooks/useWebSocket";
|
|
11
12
|
import { openTab } from "../../stores/workspace";
|
|
12
13
|
import { getSidebarStore } from "../../stores/sidebar";
|
|
13
14
|
import { useState } from "react";
|
|
@@ -69,8 +70,27 @@ export function ProjectDropdown(props: ProjectDropdownProps) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
var ws = useWebSocket();
|
|
72
74
|
var ideUrl = activeProject ? getEditorUrl(editorType, activeProject.path, ".", undefined, wslDistro, activeProject.ideProjectName) : null;
|
|
73
75
|
|
|
76
|
+
function handleOpenInIde() {
|
|
77
|
+
if (!activeProject || !ideUrl) return;
|
|
78
|
+
if (isJetBrainsEditor(editorType) && !activeProject.ideProjectName) {
|
|
79
|
+
var handler = function (msg: { type: string; projectSlug?: string; ideProjectName?: string }) {
|
|
80
|
+
if (msg.type !== "editor:ensure-project_result") return;
|
|
81
|
+
if (msg.projectSlug !== activeProject!.slug) return;
|
|
82
|
+
ws.unsubscribe("editor:ensure-project_result", handler as any);
|
|
83
|
+
var updatedUrl = getEditorUrl(editorType, activeProject!.path, ".", undefined, wslDistro, msg.ideProjectName);
|
|
84
|
+
if (updatedUrl) window.location.href = updatedUrl;
|
|
85
|
+
};
|
|
86
|
+
ws.subscribe("editor:ensure-project_result", handler as any);
|
|
87
|
+
ws.send({ type: "editor:ensure-project", projectSlug: activeProject.slug } as any);
|
|
88
|
+
} else {
|
|
89
|
+
window.location.href = ideUrl;
|
|
90
|
+
}
|
|
91
|
+
props.onClose();
|
|
92
|
+
}
|
|
93
|
+
|
|
74
94
|
function handleOpenTerminal() {
|
|
75
95
|
openTab("terminal");
|
|
76
96
|
var state = getSidebarStore().state;
|
|
@@ -113,15 +133,14 @@ export function ProjectDropdown(props: ProjectDropdownProps) {
|
|
|
113
133
|
Actions
|
|
114
134
|
</div>
|
|
115
135
|
{ideUrl && (
|
|
116
|
-
<
|
|
136
|
+
<button
|
|
117
137
|
role="menuitem"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
className="w-full flex items-center gap-2.5 px-2.5 py-1.5 rounded-lg text-[12px] text-base-content/60 hover:text-base-content hover:bg-base-content/5 transition-colors"
|
|
138
|
+
onClick={handleOpenInIde}
|
|
139
|
+
className="w-full flex items-center gap-2.5 px-2.5 py-1.5 rounded-lg text-[12px] text-base-content/60 hover:text-base-content hover:bg-base-content/5 transition-colors cursor-pointer"
|
|
121
140
|
>
|
|
122
141
|
<ExternalLink size={13} className="flex-shrink-0 text-base-content/30" />
|
|
123
142
|
Open in IDE
|
|
124
|
-
</
|
|
143
|
+
</button>
|
|
125
144
|
)}
|
|
126
145
|
<button
|
|
127
146
|
role="menuitem"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var JETBRAINS_IDS: Record<string, string> = {
|
|
1
|
+
export var JETBRAINS_IDS: Record<string, string> = {
|
|
2
2
|
webstorm: "web-storm",
|
|
3
3
|
intellij: "idea",
|
|
4
4
|
pycharm: "py-charm",
|
|
@@ -9,13 +9,11 @@ function toWindowsPath(linuxPath: string, wslDistro: string): string {
|
|
|
9
9
|
return "\\\\" + "wsl.localhost\\" + wslDistro + linuxPath.replace(/\//g, "\\");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
function buildJetBrainsUrl(ideId: string, relativePath: string, line?: number, projectName?: string
|
|
12
|
+
function buildJetBrainsUrl(ideId: string, relativePath: string, line?: number, projectName?: string): string {
|
|
13
13
|
var url = "jetbrains://" + ideId + "/navigate/reference?";
|
|
14
14
|
var params: string[] = [];
|
|
15
15
|
if (projectName) {
|
|
16
16
|
params.push("project=" + encodeURIComponent(projectName));
|
|
17
|
-
} else if (projectRoot) {
|
|
18
|
-
params.push("origin=" + encodeURIComponent(projectRoot));
|
|
19
17
|
}
|
|
20
18
|
if (relativePath) {
|
|
21
19
|
params.push("path=" + encodeURIComponent(relativePath));
|
|
@@ -26,13 +24,8 @@ function buildJetBrainsUrl(ideId: string, relativePath: string, line?: number, p
|
|
|
26
24
|
return url + params.join("&");
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
export
|
|
30
|
-
editorType
|
|
31
|
-
projectPath: string;
|
|
32
|
-
filePath: string;
|
|
33
|
-
line?: number;
|
|
34
|
-
wslDistro?: string;
|
|
35
|
-
ideProjectName?: string;
|
|
27
|
+
export function isJetBrainsEditor(editorType: string): boolean {
|
|
28
|
+
return editorType in JETBRAINS_IDS;
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
export function getEditorUrl(editorType: string, projectPath: string, filePath: string, line?: number, wslDistro?: string, ideProjectName?: string): string | null {
|
|
@@ -42,8 +35,7 @@ export function getEditorUrl(editorType: string, projectPath: string, filePath:
|
|
|
42
35
|
var jetbrainsId = JETBRAINS_IDS[editorType];
|
|
43
36
|
if (jetbrainsId) {
|
|
44
37
|
var jbRelativePath = filePath === "." ? "" : filePath;
|
|
45
|
-
|
|
46
|
-
return buildJetBrainsUrl(jetbrainsId, jbRelativePath, line, ideProjectName || undefined, ideProjectName ? undefined : jbProjectRoot);
|
|
38
|
+
return buildJetBrainsUrl(jetbrainsId, jbRelativePath, line, ideProjectName || undefined);
|
|
47
39
|
}
|
|
48
40
|
|
|
49
41
|
if (editorType === "vscode" || editorType === "vscode-insiders" || editorType === "cursor") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cryptiklemur/lattice",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.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>",
|
package/server/src/daemon.ts
CHANGED
|
@@ -373,4 +373,16 @@ export async function startDaemon(portOverride?: number | null): Promise<void> {
|
|
|
373
373
|
return;
|
|
374
374
|
}
|
|
375
375
|
});
|
|
376
|
+
|
|
377
|
+
setInterval(function () {
|
|
378
|
+
var currentConfig = loadConfig();
|
|
379
|
+
var currentIdentity = loadOrCreateIdentity();
|
|
380
|
+
broadcast({ type: "mesh:nodes", nodes: buildNodesMessage() });
|
|
381
|
+
broadcast({
|
|
382
|
+
type: "projects:list",
|
|
383
|
+
projects: currentConfig.projects.map(function (p: typeof currentConfig.projects[number]) {
|
|
384
|
+
return { slug: p.slug, path: p.path, title: p.title, nodeId: currentIdentity.id, nodeName: currentConfig.name, isRemote: false };
|
|
385
|
+
}),
|
|
386
|
+
});
|
|
387
|
+
}, 10000);
|
|
376
388
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
2
4
|
import type { ClientMessage, EditorDetectMessage } from "@lattice/shared";
|
|
3
5
|
import { registerHandler } from "../ws/router";
|
|
4
6
|
import { sendTo } from "../ws/broadcast";
|
|
7
|
+
import { loadConfig } from "../config";
|
|
5
8
|
|
|
6
9
|
var binaryNames: Record<string, string[]> = {
|
|
7
10
|
"vscode": ["code"],
|
|
@@ -30,6 +33,18 @@ function detectEditorPath(editorType: string): string | null {
|
|
|
30
33
|
return null;
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
function ensureIdeaProject(projectPath: string, projectTitle: string): string {
|
|
37
|
+
var ideaDir = join(projectPath, ".idea");
|
|
38
|
+
if (!existsSync(ideaDir)) {
|
|
39
|
+
mkdirSync(ideaDir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
var nameFile = join(ideaDir, ".name");
|
|
42
|
+
if (!existsSync(nameFile)) {
|
|
43
|
+
writeFileSync(nameFile, projectTitle, "utf-8");
|
|
44
|
+
}
|
|
45
|
+
return projectTitle;
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
registerHandler("editor", function (clientId: string, message: ClientMessage) {
|
|
34
49
|
if (message.type === "editor:detect") {
|
|
35
50
|
var detectMsg = message as EditorDetectMessage;
|
|
@@ -37,4 +52,15 @@ registerHandler("editor", function (clientId: string, message: ClientMessage) {
|
|
|
37
52
|
sendTo(clientId, { type: "editor:detect_result", editorType: detectMsg.editorType, path: detectedPath });
|
|
38
53
|
return;
|
|
39
54
|
}
|
|
55
|
+
|
|
56
|
+
if (message.type === "editor:ensure-project") {
|
|
57
|
+
var ensureMsg = message as { type: string; projectSlug: string };
|
|
58
|
+
var config = loadConfig();
|
|
59
|
+
var project = config.projects.find(function (p: typeof config.projects[number]) { return p.slug === ensureMsg.projectSlug; });
|
|
60
|
+
if (project) {
|
|
61
|
+
var name = ensureIdeaProject(project.path, project.title);
|
|
62
|
+
sendTo(clientId, { type: "editor:ensure-project_result", projectSlug: ensureMsg.projectSlug, ideProjectName: name });
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
40
66
|
});
|