桌面应用图标替换为 B-brackets 设计(深藏青 + 白/蓝花括号)
- icon.svg 取自 opsx-icon-options/api/B-brackets.svg,全套光栅重生成: icon.icns(iconutil)、icon.ico(PNG-in-ICO 6 尺寸)、16-1024 PNG、Windows Square*/StoreLogo - 替换 Tauri 脚手架默认五角星图标;GUI 重启实测 Dock 图标已生效 - 服务全量重启:server 8787(官网/文档 200)+ web 5173 + 桌面 GUI
This commit is contained in:
1 parent
71a6d986a4
commit
39f4d5addc
161 files changed
+3289
-515
No files matched your search
@@ -1,10 +1,10 @@
|
||||
import { wsSmart } from "./wsSmart";
|
||||
import type { HttpResult, HttpSpec, Transport, WsSession, WsSpec } from "@opsxapi/core";
|
||||
import { browserTransport } from "./browser";
|
||||
|
||||
/**
|
||||
* HTTP goes through Gin POST /v1/proxy/http.
|
||||
* WS (MVP): falls back to browser WebSocket — custom headers require
|
||||
* desktop/Gin tunnel later (Week 2).
|
||||
* WS: no custom headers → browser WebSocket; with headers → Gin WS tunnel (wsSmart).
|
||||
*/
|
||||
export function ginProxyTransport(baseUrl: string): Transport {
|
||||
const root = baseUrl.replace(/\/$/, "");
|
||||
@@ -24,12 +24,15 @@ export function ginProxyTransport(baseUrl: string): Transport {
|
||||
},
|
||||
|
||||
ws(spec: WsSpec): WsSession {
|
||||
// TODO(week2): custom headers need desktop/Gin WS tunnel.
|
||||
// For MVP, fall back to browser WebSocket (no custom headers).
|
||||
console.warn(
|
||||
"[opsxapi/transport] gin-proxy WS falls back to browser WebSocket; custom headers need desktop/Gin tunnel later",
|
||||
const headers = spec.headers ?? {};
|
||||
if (Object.keys(headers).length === 0) {
|
||||
return browserTransport.ws(spec);
|
||||
}
|
||||
// Custom handshake headers → Gin WS tunnel
|
||||
return wsSmart(
|
||||
{ url: spec.url, headers, protocols: spec.protocols },
|
||||
{ ginBase: root },
|
||||
);
|
||||
return browserTransport.ws(spec);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
export { browserTransport } from "./browser";
|
||||
export { ginProxyTransport } from "./ginProxy";
|
||||
export { tauriNativeTransport } from "./tauriNative";
|
||||
export { tauriNativeTransport, isTauriRuntime } from "./tauriNative";
|
||||
export { detectTransport } from "./detect";
|
||||
export type { TransportKind, DetectOptions } from "./detect";
|
||||
|
||||
|
||||
@@ -12,6 +12,17 @@ async function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T
|
||||
}
|
||||
}
|
||||
|
||||
export function isTauriRuntime(): boolean {
|
||||
if (typeof window === "undefined") return false;
|
||||
return Boolean((window as unknown as { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__);
|
||||
}
|
||||
|
||||
type WsEventPayload = {
|
||||
sessionId: string;
|
||||
kind: string;
|
||||
data: string;
|
||||
};
|
||||
|
||||
export const tauriNativeTransport: Transport = {
|
||||
async http(spec: HttpSpec): Promise<HttpResult> {
|
||||
return invoke<HttpResult>("http_send", { spec });
|
||||
@@ -21,20 +32,37 @@ export const tauriNativeTransport: Transport = {
|
||||
const messageCbs: Array<(data: string) => void> = [];
|
||||
const statusCbs: Array<(s: string) => void> = [];
|
||||
let sessionId: string | null = null;
|
||||
let unlisten: (() => void) | null = null;
|
||||
let closed = false;
|
||||
|
||||
const emitStatus = (s: string) => statusCbs.forEach((cb) => cb(s));
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
emitStatus("connecting");
|
||||
const { listen } = await import("@tauri-apps/api/event");
|
||||
unlisten = await listen<WsEventPayload>("ws-event", (ev) => {
|
||||
const p = ev.payload;
|
||||
if (!sessionId || p.sessionId !== sessionId) return;
|
||||
if (p.kind === "message") {
|
||||
messageCbs.forEach((cb) => cb(p.data));
|
||||
return;
|
||||
}
|
||||
if (p.kind === "status") {
|
||||
emitStatus(p.data);
|
||||
if (p.data === "closed" || p.data === "error") {
|
||||
// keep listening until explicit close()
|
||||
}
|
||||
}
|
||||
});
|
||||
sessionId = await invoke<string>("ws_connect", { spec });
|
||||
emitStatus("open");
|
||||
// Full event listen pipeline is Week 2; stub keeps UI usable.
|
||||
// open status also arrives via event; emit early for snappy UI
|
||||
} catch (e) {
|
||||
emitStatus("error");
|
||||
messageCbs.forEach((cb) =>
|
||||
cb(`[ws_connect error] ${e instanceof Error ? e.message : String(e)}`),
|
||||
);
|
||||
emitStatus("closed");
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -57,11 +85,17 @@ export const tauriNativeTransport: Transport = {
|
||||
});
|
||||
},
|
||||
close() {
|
||||
if (!sessionId) return;
|
||||
void invoke("ws_close", { sessionId }).finally(() => {
|
||||
sessionId = null;
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
const id = sessionId;
|
||||
sessionId = null;
|
||||
unlisten?.();
|
||||
unlisten = null;
|
||||
if (!id) {
|
||||
emitStatus("closed");
|
||||
});
|
||||
return;
|
||||
}
|
||||
void invoke("ws_close", { sessionId: id }).finally(() => emitStatus("closed"));
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user