桌面 GUI 外链修复 + 主题顺序调整(含结构扁平化进行中的工作区基线)

- 集成 tauri-plugin-opener:Cargo/lib.rs/capabilities/npm 四处接线,新增 src/openExternal.ts(桌面走 opener,浏览器回退 window.open,相对路径绝对化)
- 关于页 4 个链接、活动栏官网菜单、代理错误横幅 2 处健康检查链接全部改走 openExternal,桌面端可正常跳转
- 设置→外观 主题顺序调整为 深色/浅色/跟随系统
- release 二进制重编并验证(浏览器实测关于页链接可开文档页)
- 附带工作区中未提交的扁平化重构现状(另一会话进行中)
- homepage 落地页与文档终检通过(8787 / 与 /docs 双 200)
This commit is contained in:
logan committed 2026-10-16 03:03:36 +08:00
1 parent 6452026fd6
commit 2bb4ef7a95
299 files changed
+6059 -3322

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
import { environmentPath, resolveRequestUrl } from "../core/requestUrl";
import { activeEnvMap } from "../workspaceStore";
/**
* URL 输入(前置 URL 模式):
* 当前环境配置了前置 URL 且值为相对路径(/ 开头或空)时,
* 展示「前缀块 + 分隔线 + 路径输入」的一体胶囊 —— 借鉴 Apifox 前置 URL。
* 路径输入只编辑相对部分,光标 / 全选不会碰到服务地址。
*/
import type { KeyboardEvent } from "react";
import { useWorkspace } from "../workspaceStore";
import { cx } from "./common";
import { EnvBadge } from "./EnvBadge";
import { VarHintInput } from "./VarHintInput";
export function useEnvBaseUrl(): { baseUrl: string; envId: string | null; envName: string } {
const envId = useWorkspace((s) => s.activeEnvironmentId);
const env = useWorkspace((s) => (envId ? s.environments.find((e) => e.id === envId) : undefined));
return {
baseUrl: env?.baseUrl?.trim() ?? "",
envId: envId ?? null,
envName: env?.name ?? "",
};
}
/** 前置 URL 与相对路径拼接(供编辑器展示「实际地址」与代码生成复用) */
export function joinUrl(baseUrl: string, path: string): string {
return resolveRequestUrl(path, activeEnvMap(), baseUrl);
}
export function UrlPathInput({
value,
onChange,
placeholder,
className,
onKeyDown,
ariaLabel,
}: {
value: string;
onChange: (v: string) => void;
placeholder?: string;
className?: string;
onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
ariaLabel?: string;
}) {
const { baseUrl, envId, envName } = useEnvBaseUrl();
const setDialog = useWorkspace((s) => s.setDialog);
const environments = useWorkspace((s) => s.environments);
const relative = environmentPath(value, environments.map((env) => resolveRequestUrl("", activeEnvMap(), env.baseUrl || "")));
const usePrefix = !!baseUrl && (value === "" || (value.trimStart().startsWith("/") && !value.trimStart().startsWith("//")));
if (!usePrefix) {
return (
<div className="flex min-w-0 flex-1 items-center gap-1">
<VarHintInput
className={className}
value={value}
onChange={onChange}
placeholder={placeholder}
onKeyDown={onKeyDown}
ariaLabel={ariaLabel}
/>
{relative !== null && baseUrl && <button type="button" className="ops-btn ops-btn-secondary" title={`改为 ${relative},随后跟随环境前置 URL`} onClick={() => onChange(relative)}>关联环境</button>}
</div>
);
}
// 展示 host(去协议),完整地址放 title
const display = resolveRequestUrl("", activeEnvMap(), baseUrl).replace(/\/+$/, "");
return (
<div className={cx("url-pill", className)}>
<button
type="button"
className="url-pill-prefix"
title={`前置 URL(${envName || "当前环境"}):${baseUrl}\n点击管理环境`}
onClick={() => setDialog({ type: "environments" })}
>
{envId && <EnvBadge envId={envId} name={envName} size={14} />}
<span className="truncate">{display}</span>
</button>
<span className="url-pill-divider" />
<VarHintInput
className="url-pill-input"
value={value}
onChange={onChange}
placeholder={placeholder ?? "/path"}
onKeyDown={onKeyDown}
ariaLabel={ariaLabel}
/>
</div>
);
}