mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-26 11:21:16 +01:00
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:
@@ -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}:
|
||||
@@ -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 && <> </>}
|
||||
<span className="text-primary">{arg.name}:</span>
|
||||
<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 };
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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", {});
|
||||
|
||||
Reference in New Issue
Block a user