Run oxfmt across repo, add format script and docs

Add .oxfmtignore to skip generated bindings and wasm-pack output.
Add npm format script, update DEVELOPMENT.md for Vite+ toolchain,
and format all non-generated files with oxfmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-03-13 10:15:49 -07:00
parent 45262edfbd
commit b4a1c418bb
664 changed files with 13638 additions and 13492 deletions

View File

@@ -1,13 +1,13 @@
import type { Color } from '@yaakapp-internal/plugins';
import classNames from 'classnames';
import { fuzzyMatch } from 'fuzzbunny';
import type { Color } from "@yaakapp-internal/plugins";
import classNames from "classnames";
import { fuzzyMatch } from "fuzzbunny";
import type {
GraphQLField,
GraphQLInputField,
GraphQLNamedType,
GraphQLSchema,
GraphQLType,
} from 'graphql';
} from "graphql";
import {
getNamedType,
isEnumType,
@@ -19,22 +19,22 @@ import {
isObjectType,
isScalarType,
isUnionType,
} from 'graphql';
import { useAtomValue } from 'jotai';
import type { CSSProperties, HTMLAttributes, KeyboardEvent, ReactNode } from 'react';
import { Fragment, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useClickOutside } from '../../hooks/useClickOutside';
import { useContainerSize } from '../../hooks/useContainerQuery';
import { useDebouncedValue } from '../../hooks/useDebouncedValue';
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
import { jotaiStore } from '../../lib/jotai';
import { Banner } from '../core/Banner';
import { CountBadge } from '../core/CountBadge';
import { Icon } from '../core/Icon';
import { IconButton } from '../core/IconButton';
import { PlainInput } from '../core/PlainInput';
import { Markdown } from '../Markdown';
import { showGraphQLDocExplorerAtom } from './graphqlAtoms';
} from "graphql";
import { useAtomValue } from "jotai";
import type { CSSProperties, HTMLAttributes, KeyboardEvent, ReactNode } from "react";
import { Fragment, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useClickOutside } from "../../hooks/useClickOutside";
import { useContainerSize } from "../../hooks/useContainerQuery";
import { useDebouncedValue } from "../../hooks/useDebouncedValue";
import { useStateWithDeps } from "../../hooks/useStateWithDeps";
import { jotaiStore } from "../../lib/jotai";
import { Banner } from "../core/Banner";
import { CountBadge } from "../core/CountBadge";
import { Icon } from "../core/Icon";
import { IconButton } from "../core/IconButton";
import { PlainInput } from "../core/PlainInput";
import { Markdown } from "../Markdown";
import { showGraphQLDocExplorerAtom } from "./graphqlAtoms";
interface Props {
style?: CSSProperties;
@@ -44,10 +44,10 @@ interface Props {
}
type ExplorerItem =
| { kind: 'type'; type: GraphQLType; from: ExplorerItem }
| { kind: "type"; type: GraphQLType; from: ExplorerItem }
// oxlint-disable-next-line no-explicit-any
| { kind: 'field'; type: GraphQLField<any, any>; from: ExplorerItem }
| { kind: 'input_field'; type: GraphQLInputField; from: ExplorerItem }
| { kind: "field"; type: GraphQLField<any, any>; from: ExplorerItem }
| { kind: "input_field"; type: GraphQLInputField; from: ExplorerItem }
| null;
export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
@@ -68,9 +68,9 @@ export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
setActiveItem(null);
} else {
const isRootParentType =
showField.parentType === 'Query' ||
showField.parentType === 'Mutation' ||
showField.parentType === 'Subscription';
showField.parentType === "Query" ||
showField.parentType === "Mutation" ||
showField.parentType === "Subscription";
walkTypeGraph(schema, null, (t, from) => {
if (
showField.field === t.name &&
@@ -89,15 +89,15 @@ export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
}
}, [schema, showField]);
const qryItem: ExplorerItem = qryType ? { kind: 'type', type: qryType, from: null } : null;
const mutItem: ExplorerItem = mutType ? { kind: 'type', type: mutType, from: null } : null;
const subItem: ExplorerItem = subType ? { kind: 'type', type: subType, from: null } : null;
const qryItem: ExplorerItem = qryType ? { kind: "type", type: qryType, from: null } : null;
const mutItem: ExplorerItem = mutType ? { kind: "type", type: mutType, from: null } : null;
const subItem: ExplorerItem = subType ? { kind: "type", type: subType, from: null } : null;
const allTypes = schema.getTypeMap();
const containerRef = useRef<HTMLDivElement>(null);
const containerSize = useContainerSize(containerRef);
return (
<div ref={containerRef} className={classNames(className, 'py-3 mx-3')} style={style}>
<div ref={containerRef} className={classNames(className, "py-3 mx-3")} style={style}>
<div className="grid grid-rows-[auto_minmax(0,1fr)] h-full border border-dashed border-border rounded-lg overflow-hidden">
<GraphQLExplorerHeader
containerHeight={containerSize.height}
@@ -112,19 +112,19 @@ export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
<div className="flex flex-col gap-3 overflow-y-auto h-full w-full px-3 pb-6">
<Heading>Root Types</Heading>
<GqlTypeRow
name={{ value: 'query', color: 'primary' }}
name={{ value: "query", color: "primary" }}
item={qryItem}
setItem={setActiveItem}
className="!my-0"
/>
<GqlTypeRow
name={{ value: 'mutation', color: 'primary' }}
name={{ value: "mutation", color: "primary" }}
item={mutItem}
setItem={setActiveItem}
className="!my-0"
/>
<GqlTypeRow
name={{ value: 'subscription', color: 'primary' }}
name={{ value: "subscription", color: "primary" }}
item={subItem}
setItem={setActiveItem}
className="!my-0"
@@ -137,7 +137,7 @@ export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
<GqlTypeLink
key={t.name}
color="notice"
item={{ kind: 'type', type: t, from: null }}
item={{ kind: "type", type: t, from: null }}
setItem={setActiveItem}
/>
);
@@ -146,7 +146,7 @@ export const GraphQLDocsExplorer = memo(function GraphQLDocsExplorer({
</div>
) : (
<div
key={'name' in activeItem.type ? activeItem.type.name : String(activeItem.type)} // Reset scroll position to top
key={"name" in activeItem.type ? activeItem.type.name : String(activeItem.type)} // Reset scroll position to top
className="overflow-y-auto h-full w-full p-3 grid grid-cols-[minmax(0,1fr)]"
>
<GqlTypeInfo item={activeItem} setItem={setActiveItem} schema={schema} />
@@ -202,7 +202,7 @@ function GraphQLExplorerHeader({
})}
</div>
<GqlSchemaSearch
key={item != null && 'name' in item.type ? item.type.name : 'search'} // Force reset when changing items
key={item != null && "name" in item.type ? item.type.name : "search"} // Force reset when changing items
maxHeight={containerHeight}
currentItem={item}
schema={schema}
@@ -229,15 +229,15 @@ function GqlTypeInfo({
if (item == null) return null;
const description =
item.kind === 'type' ? getNamedType(item.type).description : item.type.description;
item.kind === "type" ? getNamedType(item.type).description : item.type.description;
const heading = (
<div className="mb-3">
<Heading>
<GqlTypeLabel item={item} />
</Heading>
<DocMarkdown>{description || 'No description'}</DocMarkdown>
{'deprecationReason' in item.type && item.type.deprecationReason && (
<DocMarkdown>{description || "No description"}</DocMarkdown>
{"deprecationReason" in item.type && item.type.deprecationReason && (
<Banner color="notice">
<DocMarkdown>{item.type.deprecationReason}</DocMarkdown>
</Banner>
@@ -274,7 +274,7 @@ function GqlTypeInfo({
<GqlTypeRow
item={fieldItem}
setItem={setItem}
name={{ value: fieldName, color: 'primary' }}
name={{ value: fieldName, color: "primary" }}
/>
</div>
);
@@ -300,7 +300,7 @@ function GqlTypeInfo({
<Subheading>Possible Types</Subheading>
{types.map((t) => (
<GqlTypeRow key={t.name} item={{ kind: 'type', type: t, from: item }} setItem={setItem} />
<GqlTypeRow key={t.name} item={{ kind: "type", type: t, from: item }} setItem={setItem} />
))}
</div>
);
@@ -321,7 +321,7 @@ function GqlTypeInfo({
</div>
);
}
if (item.kind === 'input_field') {
if (item.kind === "input_field") {
return (
<div className="flex flex-col gap-3">
{heading}
@@ -337,14 +337,14 @@ function GqlTypeInfo({
<Subheading>Type</Subheading>
<GqlTypeRow
className="mt-4"
item={{ kind: 'type', type: item.type.type, from: item }}
item={{ kind: "type", type: item.type.type, from: item }}
setItem={setItem}
/>
</div>
</div>
);
}
if (item.kind === 'field') {
if (item.kind === "field") {
return (
<div className="flex flex-col gap-3">
{heading}
@@ -353,7 +353,7 @@ function GqlTypeInfo({
<Subheading>Type</Subheading>
<GqlTypeRow
className="mt-4"
item={{ kind: 'type', type: item.type.type, from: item }}
item={{ kind: "type", type: item.type.type, from: item }}
setItem={setItem}
/>
</div>
@@ -365,8 +365,8 @@ function GqlTypeInfo({
return (
<div key={`${String(a.type)}::${a.name}`} className="my-4">
<GqlTypeRow
name={{ value: a.name, color: 'info' }}
item={{ kind: 'type', type: a.type, from: item }}
name={{ value: a.name, color: "info" }}
item={{ kind: "type", type: a.type, from: item }}
setItem={setItem}
/>
</div>
@@ -388,7 +388,7 @@ function GqlTypeInfo({
const field = fields[fieldName];
if (field == null) return null;
const fieldItem: ExplorerItem = {
kind: 'input_field',
kind: "input_field",
type: field,
from: item,
};
@@ -397,7 +397,7 @@ function GqlTypeInfo({
<GqlTypeRow
item={fieldItem}
setItem={setItem}
name={{ value: fieldName, color: 'primary' }}
name={{ value: fieldName, color: "primary" }}
/>
</div>
);
@@ -418,7 +418,7 @@ function GqlTypeInfo({
{interfaces.map((i) => (
<GqlTypeRow
key={i.name}
item={{ kind: 'type', type: i, from: item }}
item={{ kind: "type", type: i, from: item }}
setItem={setItem}
/>
))}
@@ -429,13 +429,13 @@ function GqlTypeInfo({
{Object.keys(fields).map((fieldName) => {
const field = fields[fieldName];
if (field == null) return null;
const fieldItem: ExplorerItem = { kind: 'field', type: field, from: item };
const fieldItem: ExplorerItem = { kind: "field", type: field, from: item };
return (
<div key={`${String(field.type)}::${field.name}`} className="my-4">
<GqlTypeRow
item={fieldItem}
setItem={setItem}
name={{ value: fieldName, color: 'primary' }}
name={{ value: fieldName, color: "primary" }}
/>
</div>
);
@@ -444,7 +444,7 @@ function GqlTypeInfo({
);
}
console.log('Unknown GraphQL Type', item);
console.log("Unknown GraphQL Type", item);
return <div>Unknown GraphQL type</div>;
}
@@ -467,19 +467,19 @@ function GqlTypeRow({
let child: ReactNode = <>Unknown Type</>;
if (item.kind === 'type') {
if (item.kind === "type") {
child = (
<>
<div className="font-mono text-editor">
{name && (
<span
className={classNames(
name?.color === 'danger' && 'text-danger',
name?.color === 'primary' && 'text-primary',
name?.color === 'success' && 'text-success',
name?.color === 'warning' && 'text-warning',
name?.color === 'notice' && 'text-notice',
name?.color === 'info' && 'text-info',
name?.color === "danger" && "text-danger",
name?.color === "primary" && "text-primary",
name?.color === "success" && "text-success",
name?.color === "warning" && "text-warning",
name?.color === "notice" && "text-notice",
name?.color === "info" && "text-info",
)}
>
{name.value}:&nbsp;
@@ -495,9 +495,9 @@ function GqlTypeRow({
)}
</>
);
} else if (item.kind === 'field') {
} else if (item.kind === "field") {
const returnItem: ExplorerItem = {
kind: 'type',
kind: "type",
type: item.type.type,
from: item.from,
};
@@ -513,13 +513,13 @@ function GqlTypeRow({
{item.type.args.map((arg) => (
<div
key={`${String(arg.type)}::${arg.name}`}
className={classNames(item.type.args.length === 1 && 'inline-flex')}
className={classNames(item.type.args.length === 1 && "inline-flex")}
>
{item.type.args.length > 1 && <>&nbsp;&nbsp;</>}
<span className="text-primary">{arg.name}:</span>&nbsp;
<GqlTypeLink
color="notice"
item={{ kind: 'type', type: arg.type, from: item.from }}
item={{ kind: "type", type: arg.type, from: item.from }}
setItem={setItem}
/>
</div>
@@ -527,7 +527,7 @@ function GqlTypeRow({
<span className="text-text-subtle">)</span>
</>
)}
<span className="text-text-subtle">:</span>{' '}
<span className="text-text-subtle">:</span>{" "}
<GqlTypeLink color="notice" item={returnItem} setItem={setItem} />
</div>
<DocMarkdown className="!text-text-subtle mt-0.5">
@@ -535,11 +535,11 @@ function GqlTypeRow({
</DocMarkdown>
</div>
);
} else if (item.kind === 'input_field') {
} else if (item.kind === "input_field") {
child = (
<>
<div className="font-mono text-editor">
{name && <span className="text-primary">{name.value}:</span>}{' '}
{name && <span className="text-primary">{name.value}:</span>}{" "}
<GqlTypeLink color="notice" item={item} setItem={setItem} />
</div>
<DocMarkdown>{item.type.description ?? null}</DocMarkdown>
@@ -547,7 +547,7 @@ function GqlTypeRow({
);
}
return <div className={classNames(className, 'w-full min-w-0')}>{child}</div>;
return <div className={classNames(className, "w-full min-w-0")}>{child}</div>;
}
function GqlTypeLink({
@@ -571,7 +571,7 @@ function GqlTypeLink({
className?: string;
noTruncate?: boolean;
}) {
if (item?.kind === 'type' && isListType(item.type)) {
if (item?.kind === "type" && isListType(item.type)) {
return (
<span className="font-mono text-editor">
<span className="text-text-subtle">[</span>
@@ -588,7 +588,7 @@ function GqlTypeLink({
</span>
);
}
if (item?.kind === 'type' && isNonNullType(item.type)) {
if (item?.kind === "type" && isNonNullType(item.type)) {
return (
<span className="font-mono text-editor">
<GqlTypeLink
@@ -610,16 +610,16 @@ function GqlTypeLink({
type="button"
className={classNames(
className,
'hover:underline text-left mr-auto gap-2 max-w-full',
'inline-flex items-center',
'font-mono text-editor',
!noTruncate && 'truncate',
color === 'danger' && 'text-danger',
color === 'primary' && 'text-primary',
color === 'success' && 'text-success',
color === 'warning' && 'text-warning',
color === 'notice' && 'text-notice',
color === 'info' && 'text-info',
"hover:underline text-left mr-auto gap-2 max-w-full",
"inline-flex items-center",
"font-mono text-editor",
!noTruncate && "truncate",
color === "danger" && "text-danger",
color === "primary" && "text-primary",
color === "success" && "text-success",
color === "warning" && "text-warning",
color === "notice" && "text-notice",
color === "info" && "text-info",
)}
onClick={() => {
setItem(item);
@@ -650,17 +650,17 @@ function GqlTypeLabel({
if (children) {
inner = children;
} else if (item == null) {
inner = 'Root';
} else if (item.kind === 'field') {
inner = item.type.name + (item.type.args.length > 0 ? '(…)' : '');
} else if ('name' in item.type) {
inner = "Root";
} else if (item.kind === "field") {
inner = item.type.name + (item.type.args.length > 0 ? "(…)" : "");
} else if ("name" in item.type) {
inner = item.type.name;
} else {
console.error('Unknown item type', item);
inner = 'UNKNOWN';
console.error("Unknown item type", item);
inner = "UNKNOWN";
}
return <span className={classNames(className, !noTruncate && 'truncate')}>{inner}</span>;
return <span className={classNames(className, !noTruncate && "truncate")}>{inner}</span>;
}
function Subheading({ children, count }: { children: ReactNode; count?: number }) {
@@ -698,7 +698,7 @@ function GqlSchemaSearch({
currentItem,
]);
const [focused, setOpen] = useState<boolean>(false);
const [value, setValue] = useState<string>('');
const [value, setValue] = useState<string>("");
const debouncedValue = useDebouncedValue(value, 300);
const menuRef = useRef<HTMLDivElement>(null);
const canSearch =
@@ -723,8 +723,8 @@ function GqlSchemaSearch({
return true;
});
results.sort((a, b) => {
if (value === '') {
if (a.name.startsWith('_') && !b.name.startsWith('_')) {
if (value === "") {
if (a.name.startsWith("_") && !b.name.startsWith("_")) {
// Always sort __<NAME> types to the end when there is no query
return 1;
}
@@ -760,17 +760,17 @@ function GqlSchemaSearch({
const handleKeyDown = useCallback(
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'ArrowDown' || (e.ctrlKey && e.key === 'n')) {
if (e.key === "ArrowDown" || (e.ctrlKey && e.key === "n")) {
e.preventDefault();
const next = results[activeIndex + 1] ?? results[results.length - 1] ?? null;
setActiveResult(next);
} else if (e.key === 'ArrowUp' || (e.ctrlKey && e.key === 'k')) {
} else if (e.key === "ArrowUp" || (e.ctrlKey && e.key === "k")) {
e.preventDefault();
const prev = results[activeIndex - 1] ?? results[0] ?? null;
setActiveResult(prev);
} else if (e.key === 'Escape') {
} else if (e.key === "Escape") {
inputRef.current?.blur();
} else if (e.key === 'Enter') {
} else if (e.key === "Enter") {
const result = activeResult ?? results[0] ?? null;
if (result) {
setItem(toExplorerItem(result?.type, currentItem));
@@ -787,9 +787,9 @@ function GqlSchemaSearch({
<div
className={classNames(
className,
'relative flex items-center bg-surface z-20 min-w-0',
!focused && 'max-w-[6rem] ml-auto',
focused && '!absolute top-0 left-1.5 right-1.5 bottom-0 pt-1.5',
"relative flex items-center bg-surface z-20 min-w-0",
!focused && "max-w-[6rem] ml-auto",
focused && "!absolute top-0 left-1.5 right-1.5 bottom-0 pt-1.5",
)}
>
<PlainInput
@@ -798,7 +798,11 @@ function GqlSchemaSearch({
label="search"
hideLabel
defaultValue={value}
placeholder={focused ? `Search ${currentItem != null && 'name' in currentItem.type ? currentItem.type.name : 'Schema'}` : 'Search'}
placeholder={
focused
? `Search ${currentItem != null && "name" in currentItem.type ? currentItem.type.name : "Schema"}`
: "Search"
}
leftSlot={
<div className="w-10 flex justify-center items-center">
<Icon size="sm" icon="search" color="secondary" />
@@ -814,9 +818,9 @@ function GqlSchemaSearch({
ref={menuRef}
style={{ maxHeight: maxHeight - 60 }}
className={classNames(
'x-theme-menu absolute z-10 mt-0.5 p-1.5 top-full right-0 bg-surface',
'border border-border rounded-lg overflow-y-auto w-full shadow-lg',
!focused && 'hidden',
"x-theme-menu absolute z-10 mt-0.5 p-1.5 top-full right-0 bg-surface",
"border border-border rounded-lg overflow-y-auto w-full shadow-lg",
!focused && "hidden",
)}
>
{results.length === 0 && (
@@ -867,7 +871,7 @@ function SearchResult({
(el: HTMLButtonElement | null) => {
if (el === null) return;
if (isActive) {
el.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });
el.scrollIntoView({ block: "nearest", inline: "nearest", behavior: "smooth" });
}
},
[isActive],
@@ -877,8 +881,8 @@ function SearchResult({
ref={initRef}
className={classNames(
className,
'px-3 truncate w-full text-left h-sm rounded text-editor font-mono',
isActive && 'bg-surface-highlight',
"px-3 truncate w-full text-left h-sm rounded text-editor font-mono",
isActive && "bg-surface-highlight",
)}
{...extraProps}
/>
@@ -891,7 +895,7 @@ function Heading({ children }: { children: ReactNode }) {
function DocMarkdown({ children, className }: { children: string | null; className?: string }) {
return (
<Markdown className={classNames(className, '!text-text-subtle italic')}>{children}</Markdown>
<Markdown className={classNames(className, "!text-text-subtle italic")}>{children}</Markdown>
);
}
@@ -986,21 +990,21 @@ function toExplorerItem(t: any, from: ExplorerItem | null): ExplorerItem | null
if (t == null) return null;
// GraphQLField-like: has `args` (array) and `type`
if (typeof t === 'object' && Array.isArray(t.args) && t.type) {
return { kind: 'field', type: t, from };
if (typeof t === "object" && Array.isArray(t.args) && t.type) {
return { kind: "field", type: t, from };
}
// GraphQLInputField-like: has `type`, no `args`, maybe `defaultValue`, and no `resolve`
if (
typeof t === 'object' &&
typeof t === "object" &&
t.type &&
!('args' in t) &&
!('resolve' in t) &&
('defaultValue' in t || 'description' in t)
!("args" in t) &&
!("resolve" in t) &&
("defaultValue" in t || "description" in t)
) {
return { kind: 'input_field', type: t, from };
return { kind: "input_field", type: t, from };
}
// Fallback: treat as GraphQLNamedType (object, scalar, enum, etc.)
return { kind: 'type', type: t, from };
return { kind: "type", type: t, from };
}

View File

@@ -1,26 +1,26 @@
import type { HttpRequest } from '@yaakapp-internal/models';
import type { HttpRequest } from "@yaakapp-internal/models";
import { useAtom } from 'jotai';
import { useCallback, useMemo } from 'react';
import { useLocalStorage } from 'react-use';
import { useIntrospectGraphQL } from '../../hooks/useIntrospectGraphQL';
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
import { showDialog } from '../../lib/dialog';
import { Banner } from '../core/Banner';
import { Button } from '../core/Button';
import type { DropdownItem } from '../core/Dropdown';
import { Dropdown } from '../core/Dropdown';
import type { EditorProps } from '../core/Editor/Editor';
import { Editor } from '../core/Editor/LazyEditor';
import { FormattedError } from '../core/FormattedError';
import { Icon } from '../core/Icon';
import { Separator } from '../core/Separator';
import { tryFormatGraphql } from '../../lib/formatters';
import { showGraphQLDocExplorerAtom } from './graphqlAtoms';
import { useAtom } from "jotai";
import { useCallback, useMemo } from "react";
import { useLocalStorage } from "react-use";
import { useIntrospectGraphQL } from "../../hooks/useIntrospectGraphQL";
import { useStateWithDeps } from "../../hooks/useStateWithDeps";
import { showDialog } from "../../lib/dialog";
import { Banner } from "../core/Banner";
import { Button } from "../core/Button";
import type { DropdownItem } from "../core/Dropdown";
import { Dropdown } from "../core/Dropdown";
import type { EditorProps } from "../core/Editor/Editor";
import { Editor } from "../core/Editor/LazyEditor";
import { FormattedError } from "../core/FormattedError";
import { Icon } from "../core/Icon";
import { Separator } from "../core/Separator";
import { tryFormatGraphql } from "../../lib/formatters";
import { showGraphQLDocExplorerAtom } from "./graphqlAtoms";
type Props = Pick<EditorProps, 'heightMode' | 'className' | 'forceUpdateKey'> & {
type Props = Pick<EditorProps, "heightMode" | "className" | "forceUpdateKey"> & {
baseRequest: HttpRequest;
onChange: (body: HttpRequest['body']) => void;
onChange: (body: HttpRequest["body"]) => void;
request: HttpRequest;
};
@@ -33,7 +33,7 @@ export function GraphQLEditor(props: Props) {
function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProps }: Props) {
const [autoIntrospectDisabled, setAutoIntrospectDisabled] = useLocalStorage<
Record<string, boolean>
>('graphQLAutoIntrospectDisabled', {});
>("graphQLAutoIntrospectDisabled", {});
const { schema, isLoading, error, refetch, clear } = useIntrospectGraphQL(baseRequest, {
disabled: autoIntrospectDisabled?.[baseRequest.id],
});
@@ -43,13 +43,13 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
}>(() => {
// Migrate text bodies to GraphQL format
// NOTE: This is how GraphQL used to be stored
if ('text' in request.body) {
if ("text" in request.body) {
const b = tryParseJson(request.body.text, {});
const variables = JSON.stringify(b.variables || undefined, null, 2);
return { query: b.query ?? '', variables };
return { query: b.query ?? "", variables };
}
return { query: request.body.query ?? '', variables: request.body.variables ?? '' };
return { query: request.body.query ?? "", variables: request.body.variables ?? "" };
}, [extraEditorProps.forceUpdateKey]);
const [isDocOpenRecord, setGraphqlDocStateAtomValue] = useAtom(showGraphQLDocExplorerAtom);
@@ -77,7 +77,7 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
[onChange, setCurrentBody],
);
const actions = useMemo<EditorProps['actions']>(
const actions = useMemo<EditorProps["actions"]>(
() => [
<div key="actions" className="flex flex-row !opacity-100 !shadow">
<div key="introspection" className="!opacity-100">
@@ -87,12 +87,12 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
...((schema != null
? [
{
label: 'Clear',
label: "Clear",
onSelect: clear,
color: 'danger',
color: "danger",
leftSlot: <Icon icon="trash" />,
},
{ type: 'separator' },
{ type: "separator" },
]
: []) satisfies DropdownItem[]),
{
@@ -106,12 +106,12 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
variant="border"
onClick={() => {
showDialog({
title: 'Introspection Failed',
size: 'sm',
id: 'introspection-failed',
title: "Introspection Failed",
size: "sm",
id: "introspection-failed",
render: ({ hide }) => (
<>
<FormattedError>{error ?? 'unknown'}</FormattedError>
<FormattedError>{error ?? "unknown"}</FormattedError>
<div className="w-full my-4">
<Button
onClick={async () => {
@@ -134,11 +134,11 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
</Button>
</Banner>
),
type: 'content',
type: "content",
},
{
hidden: schema == null,
label: `${isDocOpen ? 'Hide' : 'Show'} Documentation`,
label: `${isDocOpen ? "Hide" : "Show"} Documentation`,
leftSlot: <Icon icon="book_open_text" />,
onSelect: () => {
setGraphqlDocStateAtomValue((v) => ({
@@ -148,14 +148,14 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
},
},
{
label: 'Introspect Schema',
label: "Introspect Schema",
leftSlot: <Icon icon="refresh" spin={isLoading} />,
keepOpenOnSelect: true,
onSelect: refetch,
},
{ type: 'separator', label: 'Setting' },
{ type: "separator", label: "Setting" },
{
label: 'Automatic Introspection',
label: "Automatic Introspection",
keepOpenOnSelect: true,
onSelect: () => {
setAutoIntrospectDisabled({
@@ -167,8 +167,8 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
<Icon
icon={
autoIntrospectDisabled?.[baseRequest.id]
? 'check_square_unchecked'
: 'check_square_checked'
? "check_square_unchecked"
: "check_square_checked"
}
/>
),
@@ -180,10 +180,10 @@ function GraphQLEditorInner({ request, onChange, baseRequest, ...extraEditorProp
variant="border"
title="Refetch Schema"
isLoading={isLoading}
color={error ? 'danger' : 'default'}
color={error ? "danger" : "default"}
forDropdown
>
{error ? 'Introspection Failed' : schema ? 'Schema' : 'No Schema'}
{error ? "Introspection Failed" : schema ? "Schema" : "No Schema"}
</Button>
</Dropdown>
)}

View File

@@ -1,4 +1,4 @@
import { atomWithKVStorage } from '../../lib/atoms/atomWithKVStorage';
import { atomWithKVStorage } from "../../lib/atoms/atomWithKVStorage";
export const showGraphQLDocExplorerAtom = atomWithKVStorage<
Record<
@@ -11,4 +11,4 @@ export const showGraphQLDocExplorerAtom = atomWithKVStorage<
| null
| undefined
>
>('show_graphql_docs', {});
>("show_graphql_docs", {});