桌面 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:
1 parent
6452026fd6
commit
2bb4ef7a95
299 files changed
+6059
-3322
No files matched your search
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* GraphQL 编辑器:Query / Variables / Headers / Auth + 响应。
|
||||
*/
|
||||
import type { GraphqlApiNode, GraphqlRequestData, KVRow } from "../../core/index";
|
||||
import { useState } from "react";
|
||||
import { sendGraphqlRequest } from "../../send";
|
||||
import { useWorkspace } from "../../workspaceStore";
|
||||
import { KVEditor, cx } from "../common";
|
||||
import { Icon } from "../icons";
|
||||
import { UrlPathInput } from "../UrlPathInput";
|
||||
import { AuthEditor } from "./HttpEditor";
|
||||
import { ResponsePanel } from "./ResponsePanel";
|
||||
|
||||
const TABS = ["Query", "Variables", "Headers", "Auth"] as const;
|
||||
type Tab = (typeof TABS)[number];
|
||||
|
||||
export function GraphqlEditor({ node }: { node: GraphqlApiNode }) {
|
||||
const data = node.data;
|
||||
const update = useWorkspace((s) => s.updateRequestData);
|
||||
const runtime = useWorkspace((s) => s.runtime[node.id]);
|
||||
const [tab, setTab] = useState<Tab>("Query");
|
||||
|
||||
const patch = (p: Partial<GraphqlRequestData>) => update(node.id, p);
|
||||
|
||||
const variablesValid = (() => {
|
||||
try {
|
||||
JSON.parse(data.variables || "{}");
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col">
|
||||
<div className="flex shrink-0 flex-wrap items-center gap-2 px-3 py-2.5" style={{ borderBottom: "1px solid var(--border)" }}>
|
||||
<span className="ops-chip inline-flex items-center gap-1" style={{ padding: "2px 10px" }}>POST <Icon name="hexagon" size={12} /> GraphQL</span>
|
||||
<UrlPathInput
|
||||
className="ops-input flex-1 font-mono text-[13px]"
|
||||
placeholder="https://api.example.com/graphql 或 /graphql"
|
||||
value={data.url}
|
||||
onChange={(v) => patch({ url: v })}
|
||||
onKeyDown={(e) => e.key === "Enter" && sendGraphqlRequest(node)}
|
||||
/>
|
||||
<button
|
||||
className="ops-btn ops-btn-primary min-w-[84px]"
|
||||
disabled={runtime?.loading}
|
||||
onClick={() => sendGraphqlRequest(node)}
|
||||
>
|
||||
<Icon name="play" size={13} /> {runtime?.loading ? "发送中…" : "执行"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 px-2" style={{ borderBottom: "1px solid var(--border)" }}>
|
||||
{TABS.map((t) => (
|
||||
<button key={t} className={cx("ops-tab", tab === t && "ops-tab-active")} onClick={() => setTab(t)}>
|
||||
{t}
|
||||
{t === "Headers" && data.headers.some((h: { enabled: boolean; key: string }) => h.enabled && h.key) ? (
|
||||
<span className="ops-count">{data.headers.filter((h: { enabled: boolean; key: string }) => h.enabled && h.key).length}</span>
|
||||
) : null}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="ops-scroll shrink-0 p-3.5" style={{ maxHeight: "42%", minHeight: 120 }}>
|
||||
{tab === "Query" && (
|
||||
<textarea
|
||||
className="ops-code min-h-48 w-full resize-y font-mono"
|
||||
spellCheck={false}
|
||||
placeholder="query Hero($id: ID!) {\n hero(id: $id) {\n name\n }\n}"
|
||||
value={data.query}
|
||||
onChange={(e) => patch({ query: e.target.value })}
|
||||
/>
|
||||
)}
|
||||
{tab === "Variables" && (
|
||||
<div className="space-y-2">
|
||||
<textarea
|
||||
className="ops-code min-h-40 w-full resize-y font-mono"
|
||||
spellCheck={false}
|
||||
placeholder='{\n "id": "1"\n}'
|
||||
value={data.variables}
|
||||
onChange={(e) => patch({ variables: e.target.value })}
|
||||
style={{ borderColor: variablesValid ? undefined : "var(--danger)" }}
|
||||
/>
|
||||
{!variablesValid && <div className="ops-danger text-[11px]">Variables 不是合法 JSON</div>}
|
||||
</div>
|
||||
)}
|
||||
{tab === "Headers" && (
|
||||
<KVEditor rows={data.headers} onChange={(rows: KVRow[]) => patch({ headers: rows })} />
|
||||
)}
|
||||
{tab === "Auth" && (
|
||||
<AuthEditor
|
||||
data={{ ...data, method: "POST", query: [], cookies: [], bodyMode: "json", bodyRaw: "", bodyForm: [] } as never}
|
||||
patch={(p: { auth?: GraphqlRequestData["auth"] }) => {
|
||||
if (p.auth) patch({ auth: p.auth });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ResponsePanel nodeId={node.id} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user