Merge pull request #3 from xmanrui/pr-3

This commit is contained in:
xmanrui
2026-02-28 20:29:22 +08:00
5 changed files with 36 additions and 9 deletions
+10 -1
View File
@@ -3,6 +3,15 @@
import { useEffect, useState, useCallback } from "react";
import { useI18n } from "@/lib/i18n";
function resolveGatewayUrl(url?: string): string | undefined {
if (!url || typeof window === "undefined") return url;
try {
const parsed = new URL(url);
if (parsed.hostname === "localhost") parsed.hostname = window.location.hostname;
return parsed.toString();
} catch { return url; }
}
interface HealthResult {
ok: boolean;
error?: string;
@@ -31,7 +40,7 @@ export function GatewayStatus() {
return (
<div className="relative inline-flex items-center gap-2">
<a
href={health?.ok && health.webUrl ? health.webUrl : undefined}
href={health?.ok && health.webUrl ? resolveGatewayUrl(health.webUrl) : undefined}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium border bg-cyan-500/20 text-cyan-300 border-cyan-500/30 hover:bg-cyan-500/30 transition-colors cursor-pointer"
+5 -4
View File
@@ -2,6 +2,7 @@
import { useEffect, useState, useCallback, useRef } from "react";
import { useI18n } from "@/lib/i18n";
import { buildGatewayUrl } from "@/lib/gateway-url";
import { GatewayStatus } from "./gateway-status";
interface Platform {
@@ -186,8 +187,8 @@ function PlatformBadge({ platform, agentId, gatewayPort, gatewayToken, t, testRe
} else {
sessionKey = `agent:${agentId}:main`;
}
let sessionUrl = `http://localhost:${gatewayPort}/chat?session=${encodeURIComponent(sessionKey)}`;
if (gatewayToken) sessionUrl += `&token=${encodeURIComponent(gatewayToken)}`;
let sessionUrl = buildGatewayUrl(gatewayPort, "/chat", { session: sessionKey });
if (gatewayToken) sessionUrl = buildGatewayUrl(gatewayPort, "/chat", { session: sessionKey, token: gatewayToken });
const badgeStyle = pName === "feishu"
? "bg-blue-500/20 text-blue-300 border border-blue-500/30 hover:bg-blue-500/40 hover:border-blue-400"
@@ -321,8 +322,8 @@ function AgentStatusBadge({ state, t }: { state?: string; t: TFunc }) {
// Agent 卡片
function AgentCard({ agent, gatewayPort, gatewayToken, t, testResult, platformTestResults, sessionTestResult, agentState, dmSessionResults, providerAccessModeMap }: { agent: Agent; gatewayPort: number; gatewayToken?: string; t: TFunc; testResult?: { ok: boolean; text?: string; error?: string; elapsed: number } | null; platformTestResults?: Record<string, PlatformTestResult | null>; sessionTestResult?: { ok: boolean; reply?: string; error?: string; elapsed: number } | null; agentState?: string; dmSessionResults?: Record<string, PlatformTestResult | null>; providerAccessModeMap?: Record<string, "auth" | "api_key"> }) {
const sessionKey = `agent:${agent.id}:main`;
let sessionUrl = `http://localhost:${gatewayPort}/chat?session=${encodeURIComponent(sessionKey)}`;
if (gatewayToken) sessionUrl += `&token=${encodeURIComponent(gatewayToken)}`;
let sessionUrl = buildGatewayUrl(gatewayPort, "/chat", { session: sessionKey });
if (gatewayToken) sessionUrl = buildGatewayUrl(gatewayPort, "/chat", { session: sessionKey, token: gatewayToken });
const modelProvider = agent.model.includes("/") ? agent.model.split("/", 1)[0] : "default";
const modelAccessMode = providerAccessModeMap?.[modelProvider];
+3 -2
View File
@@ -3,6 +3,7 @@
import { useEffect, useRef, useState, useCallback } from 'react'
import { OfficeState } from '@/lib/pixel-office/engine/officeState'
import { renderFrame } from '@/lib/pixel-office/engine/renderer'
import { buildGatewayUrl } from "@/lib/gateway-url"
import type { EditorRenderState } from '@/lib/pixel-office/engine/renderer'
import type { ContributionData } from '@/lib/pixel-office/engine/renderer'
import { syncAgentsToOffice, AgentActivity } from '@/lib/pixel-office/agentBridge'
@@ -647,8 +648,8 @@ export default function PixelOfficePage() {
// Click on PC — open gateway chat for main agent
const gw = gatewayRef.current
const sessionKey = 'agent:main:main'
let chatUrl = `http://localhost:${gw.port}/chat?session=${encodeURIComponent(sessionKey)}`
if (gw.token) chatUrl += `&token=${encodeURIComponent(gw.token)}`
let chatUrl = buildGatewayUrl(gw.port, '/chat', { session: sessionKey })
if (gw.token) chatUrl = buildGatewayUrl(gw.port, '/chat', { session: sessionKey, token: gw.token })
window.open(chatUrl, '_blank')
} else if (office.layout.furniture.some(f => {
if (f.uid !== 'library-r') return false
+3 -2
View File
@@ -3,6 +3,7 @@
import { useEffect, useState, Suspense } from "react";
import { useSearchParams } from "next/navigation";
import Link from "next/link";
import { buildGatewayUrl } from "@/lib/gateway-url";
import { useI18n } from "@/lib/i18n";
interface AgentInfo {
@@ -288,8 +289,8 @@ function SessionList({ agentId }: { agentId: string }) {
<div className="space-y-3">
{sessions.map((s) => {
const typeInfo = getTypeLabel(s.type);
let chatUrl = `http://localhost:${gateway.port}/chat?session=${encodeURIComponent(s.key)}`;
if (gateway.token) chatUrl += `&token=${encodeURIComponent(gateway.token)}`;
let chatUrl = buildGatewayUrl(gateway.port, "/chat", { session: s.key });
if (gateway.token) chatUrl = buildGatewayUrl(gateway.port, "/chat", { session: s.key, token: gateway.token });
return (
<div
key={s.key}
+15
View File
@@ -0,0 +1,15 @@
/**
* Build a gateway URL using the current browser hostname instead of
* hard-coded "localhost", so the dashboard works over LAN.
* Falls back to "localhost" during SSR.
*/
export function buildGatewayUrl(port: number, path: string, params?: Record<string, string>): string {
const host = typeof window !== "undefined" ? window.location.hostname : "localhost";
const url = new URL(`http://${host}:${port}${path}`);
if (params) {
for (const [k, v] of Object.entries(params)) {
if (v) url.searchParams.set(k, v);
}
}
return url.toString();
}