mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-11 22:53:00 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
dede93dceb
4 changed files with 164 additions and 93 deletions
84
Cargo.lock
generated
84
Cargo.lock
generated
|
|
@ -1535,7 +1535,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-agent"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -1574,7 +1574,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-api"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-config",
|
||||
|
|
@ -1594,7 +1594,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-auth"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -1617,7 +1617,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-checkpoint"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-config",
|
||||
|
|
@ -1633,7 +1633,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-cli"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -1728,7 +1728,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-client"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
|
@ -1757,7 +1757,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-config"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -1784,7 +1784,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-core"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-types",
|
||||
|
|
@ -1799,7 +1799,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-dev"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -1819,7 +1819,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-devcontainer"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-http",
|
||||
"fabro-static",
|
||||
|
|
@ -1836,7 +1836,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-github"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
|
|
@ -1857,7 +1857,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-graphviz"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-types",
|
||||
|
|
@ -1871,7 +1871,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-hooks"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-agent",
|
||||
|
|
@ -1895,7 +1895,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-http"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-static",
|
||||
"http",
|
||||
|
|
@ -1905,7 +1905,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-install"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
|
@ -1920,7 +1920,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-interview"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"dialoguer",
|
||||
|
|
@ -1934,7 +1934,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-llm"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -1966,7 +1966,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-macros"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fabro-options-metadata",
|
||||
|
|
@ -1977,7 +1977,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-mcp"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-config",
|
||||
|
|
@ -1993,7 +1993,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-model"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-static",
|
||||
"insta",
|
||||
|
|
@ -2004,7 +2004,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-oauth"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
|
|
@ -2025,7 +2025,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-options-metadata"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -2033,7 +2033,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-proc"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
|
@ -2042,7 +2042,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-redact"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"ref-cast",
|
||||
|
|
@ -2058,7 +2058,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-retro"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2076,7 +2076,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-sandbox"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2112,7 +2112,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-server"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2193,7 +2193,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-slack"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-http",
|
||||
"fabro-interview",
|
||||
|
|
@ -2213,18 +2213,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-spa"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"rust-embed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fabro-static"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
|
||||
[[package]]
|
||||
name = "fabro-store"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
|
|
@ -2251,7 +2251,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-telemetry"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
|
@ -2277,7 +2277,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-template"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-util",
|
||||
|
|
@ -2289,7 +2289,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-test"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"axum",
|
||||
|
|
@ -2312,7 +2312,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-tracker"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-github",
|
||||
|
|
@ -2325,7 +2325,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-types"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
|
|
@ -2346,7 +2346,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-util"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"console 0.15.11",
|
||||
|
|
@ -2366,7 +2366,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-validate"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-graphviz",
|
||||
"fabro-model",
|
||||
|
|
@ -2376,7 +2376,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-vault"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-types",
|
||||
|
|
@ -2388,7 +2388,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-workflow"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -7128,7 +7128,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twin-github"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
|
|
@ -7147,7 +7147,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twin-openai"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ resolver = "2"
|
|||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
version = "0.217.0-nightly.0"
|
||||
version = "0.218.0-nightly.0"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useParams } from "react-router";
|
||||
import type { BundledLanguage } from "@pierre/diffs";
|
||||
import { graphTheme } from "../lib/graph-theme";
|
||||
import { useRunGraph, useRunGraphSource, useRunStages } from "../lib/queries";
|
||||
import { CopyButton } from "../components/ui";
|
||||
import { LoadingState } from "../components/state";
|
||||
import { StageSidebar } from "../components/stage-sidebar";
|
||||
import {
|
||||
|
|
@ -10,6 +10,8 @@ import {
|
|||
GRAPH_ZOOM_STEPS,
|
||||
GraphToolbar,
|
||||
} from "../components/graph-toolbar";
|
||||
import { CollapsibleFile } from "../components/collapsible-file";
|
||||
import { registerDotLanguage } from "../data/register-dot-language";
|
||||
import { mapRunStagesToSidebarStages } from "../lib/stage-sidebar";
|
||||
|
||||
export const handle = { wide: true };
|
||||
|
|
@ -91,6 +93,17 @@ export default function RunGraph() {
|
|||
const [error, setError] = useState<string | null>(null);
|
||||
const [zoomIndex, setZoomIndex] = useState(GRAPH_DEFAULT_ZOOM_INDEX);
|
||||
const [pan, setPan] = useState({ x: 0, y: 0 });
|
||||
const [dotReady, setDotReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
registerDotLanguage().then(() => {
|
||||
if (!cancelled) setDotReady(true);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
const dragState = useRef<{ startX: number; startY: number; startPanX: number; startPanY: number } | null>(null);
|
||||
const zoom = GRAPH_ZOOM_STEPS[zoomIndex];
|
||||
|
||||
|
|
@ -191,38 +204,47 @@ export default function RunGraph() {
|
|||
<StageSidebar stages={stages} runId={id!} activeLink="graph" />
|
||||
|
||||
<div className="min-w-0 flex-1 space-y-3">
|
||||
<ViewToggle view={view} setView={setView} />
|
||||
<div className="flex justify-end">
|
||||
<ViewToggle view={view} setView={setView} />
|
||||
</div>
|
||||
|
||||
{view === "graph" ? (
|
||||
<div className="graph-svg relative rounded-md border border-line bg-panel-alt">
|
||||
<GraphToolbar
|
||||
direction={direction}
|
||||
setDirection={setDirection}
|
||||
fitToWindow={fitToWindow}
|
||||
zoomIndex={zoomIndex}
|
||||
setZoomIndex={setZoomIndex}
|
||||
/>
|
||||
<div
|
||||
className="graph-svg relative rounded-md border border-line bg-panel-alt"
|
||||
hidden={view !== "graph"}
|
||||
>
|
||||
<GraphToolbar
|
||||
direction={direction}
|
||||
setDirection={setDirection}
|
||||
fitToWindow={fitToWindow}
|
||||
zoomIndex={zoomIndex}
|
||||
setZoomIndex={setZoomIndex}
|
||||
/>
|
||||
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="overflow-hidden p-6"
|
||||
style={{ cursor: dragState.current ? "grabbing" : "grab" }}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerCancel={onPointerUp}
|
||||
>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="overflow-hidden p-6"
|
||||
style={{ cursor: dragState.current ? "grabbing" : "grab" }}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerCancel={onPointerUp}
|
||||
ref={innerRef}
|
||||
className="flex items-center justify-center"
|
||||
style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom / 100})`, transformOrigin: "center center" }}
|
||||
>
|
||||
<div
|
||||
ref={innerRef}
|
||||
className="flex items-center justify-center"
|
||||
style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom / 100})`, transformOrigin: "center center" }}
|
||||
>
|
||||
<p className="text-sm text-fg-muted">Loading diagram...</p>
|
||||
</div>
|
||||
<p className="text-sm text-fg-muted">Loading diagram...</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<SourcePanel source={sourceQuery.data} loading={sourceQuery.data === undefined && !sourceQuery.error} />
|
||||
</div>
|
||||
|
||||
{view === "source" && (
|
||||
<SourcePanel
|
||||
source={sourceQuery.data}
|
||||
loading={sourceQuery.data === undefined && !sourceQuery.error}
|
||||
dotReady={dotReady}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -254,8 +276,16 @@ function ViewToggle({ view, setView }: { view: View; setView: (v: View) => void
|
|||
);
|
||||
}
|
||||
|
||||
function SourcePanel({ source, loading }: { source: string | null | undefined; loading: boolean }) {
|
||||
if (loading) {
|
||||
function SourcePanel({
|
||||
source,
|
||||
loading,
|
||||
dotReady,
|
||||
}: {
|
||||
source: string | null | undefined;
|
||||
loading: boolean;
|
||||
dotReady: boolean;
|
||||
}) {
|
||||
if (loading || !dotReady) {
|
||||
return (
|
||||
<div className="rounded-md border border-line bg-panel-alt p-4">
|
||||
<LoadingState label="Loading graph source…" />
|
||||
|
|
@ -270,14 +300,8 @@ function SourcePanel({ source, loading }: { source: string | null | undefined; l
|
|||
);
|
||||
}
|
||||
return (
|
||||
<div className="rounded-md border border-line bg-panel-alt">
|
||||
<div className="flex items-center justify-between gap-3 border-b border-line px-3 py-2">
|
||||
<span className="font-mono text-xs text-fg-muted">workflow.fabro</span>
|
||||
<CopyButton value={source} label="Copy graph source" />
|
||||
</div>
|
||||
<pre className="max-h-[70vh] overflow-auto whitespace-pre p-4 font-mono text-xs leading-5 text-fg-2">
|
||||
{source}
|
||||
</pre>
|
||||
</div>
|
||||
<CollapsibleFile
|
||||
file={{ name: "workflow.fabro", contents: source, lang: "dot" as BundledLanguage }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,27 @@
|
|||
import type { ReactNode } from "react";
|
||||
import { useState, type ReactNode } from "react";
|
||||
import type {
|
||||
ObjectStoreSettings,
|
||||
ServerListenSettings,
|
||||
ServerSettings,
|
||||
} from "@qltysh/fabro-api-client";
|
||||
import { useServerSettings } from "../lib/queries";
|
||||
import { CollapsibleFile } from "../components/collapsible-file";
|
||||
|
||||
export function meta({}: any) {
|
||||
return [{ title: "Settings — Fabro" }];
|
||||
}
|
||||
|
||||
type View = "settings" | "json";
|
||||
|
||||
export default function Settings() {
|
||||
const settingsQuery = useServerSettings();
|
||||
const settings = settingsQuery.data;
|
||||
const [view, setView] = useState<View>("settings");
|
||||
|
||||
if (!settings) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageIntro />
|
||||
<PageIntro view={view} setView={setView} />
|
||||
<PanelSkeleton />
|
||||
<PanelSkeleton />
|
||||
<PanelSkeleton />
|
||||
|
|
@ -27,21 +31,64 @@ export default function Settings() {
|
|||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageIntro />
|
||||
<ServerPanel settings={settings} />
|
||||
<AccessPanel settings={settings} />
|
||||
<IntegrationsPanel settings={settings} />
|
||||
<PageIntro view={view} setView={setView} />
|
||||
{view === "settings" ? (
|
||||
<>
|
||||
<ServerPanel settings={settings} />
|
||||
<AccessPanel settings={settings} />
|
||||
<IntegrationsPanel settings={settings} />
|
||||
</>
|
||||
) : (
|
||||
<CollapsibleFile
|
||||
file={{
|
||||
name: "server-settings.json",
|
||||
contents: JSON.stringify(settings, null, 2),
|
||||
lang: "json",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PageIntro() {
|
||||
function PageIntro({ view, setView }: { view: View; setView: (v: View) => void }) {
|
||||
return (
|
||||
<p className="max-w-[64ch] text-sm/6 text-fg-3 text-pretty">
|
||||
Snapshot of the server configuration. Edit via{" "}
|
||||
<code className="font-mono text-fg-2">settings.toml</code>; changes take
|
||||
effect on the next server restart.
|
||||
</p>
|
||||
<div className="flex items-start justify-between gap-6">
|
||||
<p className="max-w-[64ch] text-sm/6 text-fg-3 text-pretty">
|
||||
Snapshot of the server configuration. Edit via{" "}
|
||||
<code className="font-mono text-fg-2">settings.toml</code>; changes take
|
||||
effect on the next server restart.
|
||||
</p>
|
||||
<ViewToggle view={view} setView={setView} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ViewToggle({ view, setView }: { view: View; setView: (v: View) => void }) {
|
||||
const btn = "rounded px-3 py-1.5 text-xs font-medium transition-colors";
|
||||
return (
|
||||
<div
|
||||
role="group"
|
||||
aria-label="Settings view"
|
||||
className="inline-flex shrink-0 rounded-md border border-line bg-panel/80 p-0.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setView("settings")}
|
||||
aria-pressed={view === "settings"}
|
||||
className={`${btn} ${view === "settings" ? "bg-overlay text-teal-500" : "text-fg-muted hover:text-fg-3"}`}
|
||||
>
|
||||
Settings
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setView("json")}
|
||||
aria-pressed={view === "json"}
|
||||
className={`${btn} ${view === "json" ? "bg-overlay text-teal-500" : "text-fg-muted hover:text-fg-3"}`}
|
||||
>
|
||||
JSON
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue