diff --git a/package-lock.json b/package-lock.json index 876231de..3b2ec34c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codenomad-workspace", - "version": "0.7.2", + "version": "0.7.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codenomad-workspace", - "version": "0.7.2", + "version": "0.7.3", "dependencies": { "7zip-bin": "^5.2.0", "google-auth-library": "^10.5.0" @@ -7389,7 +7389,7 @@ }, "packages/electron-app": { "name": "@neuralnomads/codenomad-electron-app", - "version": "0.7.2", + "version": "0.7.3", "dependencies": { "@codenomad/ui": "file:../ui", "@neuralnomads/codenomad": "file:../server" @@ -7423,7 +7423,7 @@ }, "packages/server": { "name": "@neuralnomads/codenomad", - "version": "0.7.2", + "version": "0.7.3", "dependencies": { "@fastify/cors": "^8.5.0", "@fastify/reply-from": "^9.8.0", @@ -7458,14 +7458,14 @@ }, "packages/tauri-app": { "name": "@codenomad/tauri-app", - "version": "0.7.2", + "version": "0.7.3", "devDependencies": { "@tauri-apps/cli": "^2.9.4" } }, "packages/ui": { "name": "@codenomad/ui", - "version": "0.7.2", + "version": "0.7.3", "dependencies": { "@git-diff-view/solid": "^0.0.8", "@kobalte/core": "0.13.11", diff --git a/package.json b/package.json index 97063365..399b9a68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codenomad-workspace", - "version": "0.7.2", + "version": "0.7.3", "private": true, "description": "CodeNomad monorepo workspace", "workspaces": { diff --git a/packages/electron-app/package.json b/packages/electron-app/package.json index 52c08829..019b67e4 100644 --- a/packages/electron-app/package.json +++ b/packages/electron-app/package.json @@ -1,6 +1,6 @@ { "name": "@neuralnomads/codenomad-electron-app", - "version": "0.7.2", + "version": "0.7.3", "description": "CodeNomad - AI coding assistant", "author": { "name": "Neural Nomads", diff --git a/packages/server/package-lock.json b/packages/server/package-lock.json index 74a8f33e..a6b44b34 100644 --- a/packages/server/package-lock.json +++ b/packages/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "@neuralnomads/codenomad", - "version": "0.7.2", + "version": "0.7.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@neuralnomads/codenomad", - "version": "0.7.2", + "version": "0.7.3", "dependencies": { "@fastify/cors": "^8.5.0", "commander": "^12.1.0", diff --git a/packages/server/package.json b/packages/server/package.json index e6e7ca77..de54772c 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@neuralnomads/codenomad", - "version": "0.7.2", + "version": "0.7.3", "description": "CodeNomad Server", "author": { "name": "Neural Nomads", diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 831e9381..c2085a0a 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -127,10 +127,18 @@ function parsePort(input: string): number { } function resolveHost(input: string | undefined): string { - if (input && input.trim() === "0.0.0.0") { + const trimmed = input?.trim() + if (!trimmed) return DEFAULT_HOST + + if (trimmed === "0.0.0.0") { return "0.0.0.0" } - return DEFAULT_HOST + + if (trimmed === "localhost") { + return DEFAULT_HOST + } + + return trimmed } async function main() { @@ -149,11 +157,13 @@ async function main() { const eventBus = new EventBus(eventLogger) + const isLoopbackHost = (host: string) => host === "127.0.0.1" || host === "::1" || host.startsWith("127.") + const serverMeta: ServerMeta = { httpBaseUrl: `http://${options.host}:${options.port}`, eventsUrl: `/api/events`, host: options.host, - listeningMode: options.host === "0.0.0.0" ? "all" : "local", + listeningMode: isLoopbackHost(options.host) ? "local" : "all", port: options.port, hostLabel: options.host, workspaceRoot: options.rootDir, diff --git a/packages/server/src/server/http-server.ts b/packages/server/src/server/http-server.ts index a92f01e3..65ef3472 100644 --- a/packages/server/src/server/http-server.ts +++ b/packages/server/src/server/http-server.ts @@ -93,6 +93,7 @@ export function createHttpServer(deps: HttpServerDeps) { }) const allowedDevOrigins = new Set(["http://localhost:3000", "http://127.0.0.1:3000"]) + const isLoopbackHost = (host: string) => host === "127.0.0.1" || host === "::1" || host.startsWith("127.") app.register(cors, { origin: (origin, cb) => { @@ -113,10 +114,17 @@ export function createHttpServer(deps: HttpServerDeps) { return } - if (allowedDevOrigins.has(origin)) { - cb(null, true) - return - } + if (allowedDevOrigins.has(origin)) { + cb(null, true) + return + } + + // When we bind to a non-loopback host (e.g., 0.0.0.0 or LAN IP), allow cross-origin UI access. + if (deps.host === "0.0.0.0" || !isLoopbackHost(deps.host)) { + cb(null, true) + return + } + cb(null, false) }, @@ -275,13 +283,13 @@ export function createHttpServer(deps: HttpServerDeps) { } } - const displayHost = deps.host === "0.0.0.0" ? "127.0.0.1" : deps.host === "127.0.0.1" ? "localhost" : deps.host + const displayHost = deps.host === "127.0.0.1" ? "localhost" : deps.host const serverUrl = `http://${displayHost}:${actualPort}` deps.serverMeta.httpBaseUrl = serverUrl deps.serverMeta.host = deps.host deps.serverMeta.port = actualPort - deps.serverMeta.listeningMode = deps.host === "0.0.0.0" ? "all" : "local" + deps.serverMeta.listeningMode = deps.host === "0.0.0.0" || !isLoopbackHost(deps.host) ? "all" : "local" deps.logger.info({ port: actualPort, host: deps.host }, "HTTP server listening") console.log(`CodeNomad Server is ready at ${serverUrl}`) diff --git a/packages/server/src/server/routes/meta.ts b/packages/server/src/server/routes/meta.ts index d7161985..40782181 100644 --- a/packages/server/src/server/routes/meta.ts +++ b/packages/server/src/server/routes/meta.ts @@ -17,7 +17,7 @@ function buildMetaResponse(meta: ServerMeta): ServerMeta { return { ...meta, port, - listeningMode: meta.host === "0.0.0.0" ? "all" : "local", + listeningMode: meta.host === "0.0.0.0" || !isLoopbackHost(meta.host) ? "all" : "local", addresses, } } @@ -35,6 +35,10 @@ function resolvePort(meta: ServerMeta): number { } } +function isLoopbackHost(host: string): boolean { + return host === "127.0.0.1" || host === "::1" || host.startsWith("127.") +} + function resolveAddresses(port: number, host: string): NetworkAddress[] { const interfaces = os.networkInterfaces() const seen = new Set() diff --git a/packages/tauri-app/package.json b/packages/tauri-app/package.json index 2bd824fb..25201d3b 100644 --- a/packages/tauri-app/package.json +++ b/packages/tauri-app/package.json @@ -1,6 +1,6 @@ { "name": "@codenomad/tauri-app", - "version": "0.7.2", + "version": "0.7.3", "private": true, "scripts": { "dev": "tauri dev", diff --git a/packages/ui/package.json b/packages/ui/package.json index 0f526134..d132ed80 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@codenomad/ui", - "version": "0.7.2", + "version": "0.7.3", "private": true, "type": "module", "scripts": { diff --git a/packages/ui/src/components/prompt-input.tsx b/packages/ui/src/components/prompt-input.tsx index f6295c37..ab4bb636 100644 --- a/packages/ui/src/components/prompt-input.tsx +++ b/packages/ui/src/components/prompt-input.tsx @@ -604,6 +604,7 @@ export default function PromptInput(props: PromptInputProps) { } } + setExpandState("normal") clearPrompt() // Ignore attachments for slash commands, but keep them for next prompt. @@ -843,7 +844,10 @@ export default function PromptInput(props: PromptInputProps) { const currentPrompt = prompt() const pos = atPosition() const cursorPos = textareaRef?.selectionStart || 0 - const folderMention = relativePath === "." || relativePath === "" ? "/" : displayPath + const folderMention = + relativePath === "." || relativePath === "" + ? "/" + : relativePath.replace(/\/+$/, "") + "/" if (pos !== null) { const before = currentPrompt.substring(0, pos + 1) @@ -887,7 +891,7 @@ export default function PromptInput(props: PromptInputProps) { if (pos !== null) { const before = currentPrompt.substring(0, pos) const after = currentPrompt.substring(cursorPos) - const attachmentText = `@${filename}` + const attachmentText = `@${normalizedPath}` const newPrompt = before + attachmentText + " " + after setPrompt(newPrompt) diff --git a/packages/ui/src/components/unified-picker.tsx b/packages/ui/src/components/unified-picker.tsx index fab200c9..541d059c 100644 --- a/packages/ui/src/components/unified-picker.tsx +++ b/packages/ui/src/components/unified-picker.tsx @@ -339,7 +339,7 @@ const UnifiedPicker: Component = (props) => { e.preventDefault() setSelectedIndex((prev) => Math.max(prev - 1, 0)) scrollToSelected() - } else if (e.key === "Enter") { + } else if (e.key === "Enter" || e.key === "Tab") { e.preventDefault() const selected = items[selectedIndex()] if (selected) { @@ -534,7 +534,7 @@ const UnifiedPicker: Component = (props) => {