diff --git a/.changeset/strong-hornets-jog.md b/.changeset/strong-hornets-jog.md new file mode 100644 index 000000000..fc5489e11 --- /dev/null +++ b/.changeset/strong-hornets-jog.md @@ -0,0 +1,5 @@ +--- +'@graphql-hive/laboratory': major +--- + +First release diff --git a/.changeset/young-papayas-hear.md b/.changeset/young-papayas-hear.md new file mode 100644 index 000000000..210e91aa6 --- /dev/null +++ b/.changeset/young-papayas-hear.md @@ -0,0 +1,5 @@ +--- +'@graphql-hive/render-laboratory': major +--- + +Render laboratory util for yoga diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e22c56cf1..977692f69 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -109,6 +109,8 @@ module.exports = { 'packages/migrations/**', // We bundle it all anyway, so there are no node_modules 'packages/web/app/**', + // We bundle it all anyway, so there are no node_modules + 'packages/libraries/laboratory/**', '**/*.spec.ts', '**/*.test.ts', '**/*.e2e.ts', diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 6f181fd59..2c94ee0c0 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -54,6 +54,12 @@ runs: working-directory: ${{ inputs.workingDirectory }} run: pnpm install --frozen-lockfile + - name: build laboratory + shell: bash + if: ${{ inputs.buildLaboratory == 'true' }} + working-directory: ${{ inputs.workingDirectory }} + run: pnpm turbo build --filter=./packages/libraries/laboratory --color + - name: generate graphql types if: ${{ inputs.codegen == 'true' }} shell: bash diff --git a/.github/workflows/typescript-typecheck.yaml b/.github/workflows/typescript-typecheck.yaml index 6aaca40c2..235c1d17a 100644 --- a/.github/workflows/typescript-typecheck.yaml +++ b/.github/workflows/typescript-typecheck.yaml @@ -15,6 +15,7 @@ jobs: uses: ./.github/actions/setup with: actor: typescript-typecheck + buildLaboratory: true - name: get cpu count id: cpu-cores diff --git a/cypress/e2e/laboratory-preflight.cy.ts b/cypress/e2e/laboratory-preflight.cy.ts index 29cdeeab1..ea7d634cf 100644 --- a/cypress/e2e/laboratory-preflight.cy.ts +++ b/cypress/e2e/laboratory-preflight.cy.ts @@ -287,12 +287,7 @@ describe('Execution', () => { parseSpecialCharSequences: false, }, ); - cy.dataCy('env-editor-mini').within(() => { - cy.get('textarea').type('{"foo":"injected"}', { - force: true, - parseSpecialCharSequences: false, - }); - }); + setMonacoEditorContents('env-editor-mini', '{"foo":"injected"}'); cy.intercept({ method: 'POST', diff --git a/package.json b/package.json index f1e1c2008..13c9a0a86 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "ip": "npm:neoip@2.1.0", "miniflare@3>undici": "^7.18.2", "tailwindcss": "3.4.17", + "@graphql-hive/laboratory>tailwindcss": "4.1.18", "@hive/app>tailwindcss": "4.1.18", "@tailwindcss/node>tailwindcss": "4.1.18", "@tailwindcss/vite>tailwindcss": "4.1.18", @@ -155,6 +156,7 @@ "seroval@<1.4.1": "^1.4.1", "fast-xml-parser@<5.3.8": "^5.3.8", "minimatch@10.x.x": "^10.2.2", + "amqplib": "^0.8.0", "minimatch@9.x.x": "^9.0.6", "minimatch@3.x.x": "^3.1.3", "minimatch@4.x.x": "^4.2.4", @@ -179,7 +181,8 @@ "bentocache": "patches/bentocache.patch", "nextra": "patches/nextra.patch", "nextra-theme-docs": "patches/nextra-theme-docs.patch", - "@graphql-codegen/schema-ast": "patches/@graphql-codegen__schema-ast.patch" + "@graphql-codegen/schema-ast": "patches/@graphql-codegen__schema-ast.patch", + "@fastify/vite": "patches/@fastify__vite.patch" }, "onlyBuiltDependencies": [ "msw" diff --git a/packages/libraries/laboratory/components.json b/packages/libraries/laboratory/components.json new file mode 100644 index 000000000..b266af2d1 --- /dev/null +++ b/packages/libraries/laboratory/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/laboratory/components", + "utils": "@/laboratory/lib/utils", + "ui": "@/laboratory/components/ui", + "lib": "@/laboratory/lib", + "hooks": "@/laboratory/hooks" + }, + "registries": {} +} diff --git a/packages/libraries/laboratory/index.html b/packages/libraries/laboratory/index.html new file mode 100644 index 000000000..4ae65e503 --- /dev/null +++ b/packages/libraries/laboratory/index.html @@ -0,0 +1,16 @@ + + + + + + + Hive Laboratory + + + + + +
+ + + diff --git a/packages/libraries/laboratory/package.json b/packages/libraries/laboratory/package.json new file mode 100644 index 000000000..b69766ddf --- /dev/null +++ b/packages/libraries/laboratory/package.json @@ -0,0 +1,145 @@ +{ + "name": "@graphql-hive/laboratory", + "version": "0.0.1", + "type": "module", + "main": "./dist/hive-laboratory.cjs.js", + "module": "./dist/hive-laboratory.es.js", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/hive-laboratory.es.js", + "require": "./dist/hive-laboratory.cjs.js" + }, + "./umd": "./dist/hive-laboratory.umd.js" + }, + "jsdelivr": "./dist/hive-laboratory.umd.js", + "unpkg": "./dist/hive-laboratory.umd.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "vite build --config vite.lib.config.ts && vite build --config vite.umd.config.ts", + "dev": "vite", + "dev:electron": "VITE_TARGET=electron concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\"", + "lint": "eslint .", + "preview": "vite preview" + }, + "peerDependencies": { + "@tanstack/react-form": "^1.23.8", + "date-fns": "^4.1.0", + "graphql-ws": "^6.0.6", + "lucide-react": "^0.548.0", + "lz-string": "^1.5.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "tslib": "^2.8.1", + "zod": "^4.1.12" + }, + "devDependencies": { + "@dagrejs/dagre": "^1.1.8", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@eslint/js": "^9.39.2", + "@mlc-ai/web-llm": "^0.2.80", + "@monaco-editor/react": "4.8.0-rc.2", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-context-menu": "^2.2.16", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toggle": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.8", + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@tailwindcss/cli": "^4.1.18", + "@tailwindcss/postcss": "^4.1.18", + "@tailwindcss/vite": "^4.1.18", + "@tanstack/react-form": "^1.27.7", + "@tanstack/react-router": "^1.154.13", + "@tanstack/react-router-devtools": "^1.154.13", + "@tanstack/router-plugin": "^1.154.13", + "@types/crypto-js": "^4.2.2", + "@types/lodash": "^4.17.23", + "@types/node": "^24.10.9", + "@types/react": "18.3.18", + "@types/react-dom": "18.3.5", + "@vitejs/plugin-react": "^5.1.2", + "@xyflow/react": "^12.10.0", + "autoprefixer": "^10.4.23", + "babel-plugin-react-compiler": "19.1.0-rc.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "color": "^5.0.3", + "concurrently": "^9.2.1", + "crypto-js": "^4.2.0", + "date-fns": "^4.1.0", + "esbuild": "^0.25.12", + "eslint": "^9.39.2", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^16.5.0", + "graphql": "^16.12.0", + "graphql-ws": "^6.0.6", + "lodash": "^4.17.23", + "lucide-react": "^0.548.0", + "lz-string": "^1.5.0", + "monaco-editor": "^0.52.2", + "monaco-graphql": "^1.7.3", + "monacopilot": "^1.2.12", + "next-themes": "^0.4.6", + "postcss": "^8.5.6", + "postcss-prefixwrap": "^1.57.2", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-resizable-panels": "^3.0.6", + "react-shadow": "^20.6.0", + "rollup-plugin-typescript2": "^0.36.0", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + "tailwindcss": "^4.1.18", + "tailwindcss-scoped-preflight": "^3.5.7", + "tslib": "^2.8.1", + "tsup": "^8.5.1", + "tw-animate-css": "^1.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.53.1", + "unplugin-dts": "1.0.0-beta.6", + "vite": "npm:rolldown-vite@7.1.14", + "vite-plugin-commonjs": "^0.10.4", + "vite-plugin-dts": "^4.5.4", + "vite-plugin-monaco-editor": "^1.1.0", + "wait-on": "^9.0.3", + "zod": "^4.3.6" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org", + "access": "public" + }, + "sideEffects": [ + "**/*.css" + ], + "build": { + "appId": "com.guild.hive.laboratory", + "productName": "Hive Laboratory", + "artifactName": "HiveLab-${version}-Do_Not_Open_(Seriously_It's_Pre-Release)-${os}-${arch}.${ext}", + "files": [ + "dist/**/*", + "electron/**/*" + ], + "directories": { + "buildResources": "assets" + } + } +} diff --git a/packages/web/app/src/laboratory/components/graphql-type.tsx b/packages/libraries/laboratory/src/components/graphql-type.tsx similarity index 81% rename from packages/web/app/src/laboratory/components/graphql-type.tsx rename to packages/libraries/laboratory/src/components/graphql-type.tsx index e05b5eb78..cfb04958d 100644 --- a/packages/web/app/src/laboratory/components/graphql-type.tsx +++ b/packages/libraries/laboratory/src/components/graphql-type.tsx @@ -15,7 +15,7 @@ export const GraphQLType = (props: { return ( - ! + ! ); } @@ -23,9 +23,9 @@ export const GraphQLType = (props: { if (props.type instanceof GraphQLList) { return ( - [ + [ - ] + ] ); } diff --git a/packages/web/app/src/laboratory/components/icons.tsx b/packages/libraries/laboratory/src/components/icons.tsx similarity index 96% rename from packages/web/app/src/laboratory/components/icons.tsx rename to packages/libraries/laboratory/src/components/icons.tsx index 8bccb9b36..c7b5766a4 100644 --- a/packages/web/app/src/laboratory/components/icons.tsx +++ b/packages/libraries/laboratory/src/components/icons.tsx @@ -1,3 +1,4 @@ +import type { SVGProps } from 'react'; import type { LucideProps } from 'lucide-react'; export const GraphQLIcon = (props: LucideProps) => { @@ -8,7 +9,7 @@ export const GraphQLIcon = (props: LucideProps) => { viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" - {...props} + {...(props as SVGProps)} > @@ -157,9 +142,9 @@ export const BuilderScalarField = (props: { - + {isOpen && (
{args.length > 0 && ( @@ -200,9 +185,9 @@ export const BuilderScalarField = (props: { - + {args.map(arg => ( @@ -335,9 +320,9 @@ export const BuilderObjectField = (props: { - + {isOpen && (
{args.length > 0 && ( @@ -378,9 +363,9 @@ export const BuilderObjectField = (props: { - + {args.map(arg => ( +
Builder
@@ -542,7 +527,7 @@ export const Builder = (props: { className="p-1! size-6 rounded-sm" disabled={openPaths.length === 0} > - + Collapse all @@ -581,7 +566,7 @@ export const Builder = (props: { onValueChange={setTabValue} className="flex size-full flex-col gap-0" > -
+
Query @@ -654,7 +639,7 @@ export const Builder = (props: { - + No endpoint selected diff --git a/packages/web/app/src/laboratory/components/laboratory/collections.tsx b/packages/libraries/laboratory/src/components/laboratory/collections.tsx similarity index 84% rename from packages/web/app/src/laboratory/components/laboratory/collections.tsx rename to packages/libraries/laboratory/src/components/laboratory/collections.tsx index ba66c0f97..9b831d1bf 100644 --- a/packages/web/app/src/laboratory/components/laboratory/collections.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/collections.tsx @@ -7,8 +7,10 @@ import { TrashIcon, XIcon, } from 'lucide-react'; -import { GraphQLIcon } from '@/laboratory/components/icons'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; +import { TooltipTrigger } from '@radix-ui/react-tooltip'; +import type { LaboratoryCollection, LaboratoryCollectionOperation } from '../../lib/collections'; +import { cn } from '../../lib/utils'; +import { GraphQLIcon } from '../icons'; import { AlertDialog, AlertDialogAction, @@ -19,13 +21,9 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from '@/laboratory/components/ui/alert-dialog'; -import { Button } from '@/laboratory/components/ui/button'; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/laboratory/components/ui/collapsible'; +} from '../ui/alert-dialog'; +import { Button } from '../ui/button'; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'; import { Empty, EmptyContent, @@ -33,16 +31,11 @@ import { EmptyHeader, EmptyMedia, EmptyTitle, -} from '@/laboratory/components/ui/empty'; -import { Input } from '@/laboratory/components/ui/input'; -import { ScrollArea, ScrollBar } from '@/laboratory/components/ui/scroll-area'; -import { Tooltip, TooltipContent } from '@/laboratory/components/ui/tooltip'; -import type { - LaboratoryCollection, - LaboratoryCollectionOperation, -} from '@/laboratory/lib/collections'; -import { cn } from '@/laboratory/lib/utils'; -import { TooltipTrigger } from '@radix-ui/react-tooltip'; +} from '../ui/empty'; +import { Input } from '../ui/input'; +import { ScrollArea, ScrollBar } from '../ui/scroll-area'; +import { Tooltip, TooltipContent } from '../ui/tooltip'; +import { useLaboratory } from './context'; export const CollectionItem = (props: { collection: LaboratoryCollection }) => { const { @@ -64,13 +57,13 @@ export const CollectionItem = (props: { collection: LaboratoryCollection }) => { - + {isOpen && props.collection.operations.map(operation => { const isActive = activeOperation?.id === operation.id; @@ -130,7 +123,7 @@ export const CollectionItem = (props: { collection: LaboratoryCollection }) => { key={operation.name} variant="ghost" className={cn('group w-full justify-start gap-2 px-2', { - 'bg-accent_80': isActive, + 'bg-accent/50': isActive, })} size="sm" onClick={() => { @@ -156,7 +149,7 @@ export const CollectionItem = (props: { collection: LaboratoryCollection }) => { ); @@ -282,7 +275,7 @@ export const Collections = () => { className="p-1! size-6 rounded-sm" onClick={openAddCollectionDialog} > - + Add collection @@ -290,8 +283,8 @@ export const Collections = () => { )}
-
- +
+ { className="p-1! absolute right-5 top-1/2 size-6 -translate-y-1/2 rounded-sm" onClick={() => setSearch('')} > - + )}
@@ -321,7 +314,7 @@ export const Collections = () => { - + No results found @@ -336,7 +329,7 @@ export const Collections = () => { - + No collections yet diff --git a/packages/web/app/src/laboratory/components/laboratory/command.tsx b/packages/libraries/laboratory/src/components/laboratory/command.tsx similarity index 97% rename from packages/web/app/src/laboratory/components/laboratory/command.tsx rename to packages/libraries/laboratory/src/components/laboratory/command.tsx index a01039010..e67f95139 100644 --- a/packages/web/app/src/laboratory/components/laboratory/command.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/command.tsx @@ -1,6 +1,5 @@ import { Fragment, useEffect, useState } from 'react'; import { FilePlus2Icon, FolderPlusIcon, PlayIcon, RefreshCcwIcon, ServerIcon } from 'lucide-react'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; import { CommandDialog, CommandEmpty, @@ -10,7 +9,8 @@ import { CommandList, CommandSeparator, CommandShortcut, -} from '@/laboratory/components/ui/command'; +} from '../ui/command'; +import { useLaboratory } from './context'; export function Command(props: { open?: boolean; onOpenChange?: (open: boolean) => void }) { const { diff --git a/packages/web/app/src/laboratory/components/laboratory/context.tsx b/packages/libraries/laboratory/src/components/laboratory/context.tsx similarity index 85% rename from packages/web/app/src/laboratory/components/laboratory/context.tsx rename to packages/libraries/laboratory/src/components/laboratory/context.tsx index e6caf7a7d..633197613 100644 --- a/packages/web/app/src/laboratory/components/laboratory/context.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/context.tsx @@ -5,47 +5,36 @@ import { type LaboratoryCollectionOperation, type LaboratoryCollectionsActions, type LaboratoryCollectionsState, -} from '@/laboratory/lib/collections'; -import { - type LaboratoryEndpointActions, - type LaboratoryEndpointState, -} from '@/laboratory/lib/endpoint'; -import type { LaboratoryEnv, LaboratoryEnvActions, LaboratoryEnvState } from '@/laboratory/lib/env'; +} from '../../lib/collections'; +import { type LaboratoryEndpointActions, type LaboratoryEndpointState } from '../../lib/endpoint'; +import type { LaboratoryEnv, LaboratoryEnvActions, LaboratoryEnvState } from '../../lib/env'; import type { LaboratoryHistory, LaboratoryHistoryActions, LaboratoryHistoryState, -} from '@/laboratory/lib/history'; +} from '../../lib/history'; import { type LaboratoryOperation, type LaboratoryOperationsActions, type LaboratoryOperationsState, -} from '@/laboratory/lib/operations'; +} from '../../lib/operations'; import { LaboratoryPlugin, LaboratoryPluginsActions, LaboratoryPluginsState, -} from '@/laboratory/lib/plugins'; +} from '../../lib/plugins'; import type { LaboratoryPreflight, LaboratoryPreflightActions, LaboratoryPreflightState, -} from '@/laboratory/lib/preflight'; +} from '../../lib/preflight'; import type { LaboratorySettings, LaboratorySettingsActions, LaboratorySettingsState, -} from '@/laboratory/lib/settings'; -import type { - LaboratoryTab, - LaboratoryTabsActions, - LaboratoryTabsState, -} from '@/laboratory/lib/tabs'; -import type { - LaboratoryTest, - LaboratoryTestActions, - LaboratoryTestState, -} from '@/laboratory/lib/tests'; +} from '../../lib/settings'; +import type { LaboratoryTab, LaboratoryTabsActions, LaboratoryTabsState } from '../../lib/tabs'; +import type { LaboratoryTest, LaboratoryTestActions, LaboratoryTestState } from '../../lib/tests'; type LaboratoryContextState = LaboratoryCollectionsState & LaboratoryEndpointState & @@ -58,6 +47,7 @@ type LaboratoryContextState = LaboratoryCollectionsState & LaboratoryPluginsState & LaboratoryTestState & { isFullScreen?: boolean; + theme?: 'light' | 'dark'; }; type LaboratoryContextActions = LaboratoryCollectionsActions & LaboratoryEndpointActions & @@ -73,9 +63,7 @@ type LaboratoryContextActions = LaboratoryCollectionsActions & openUpdateEndpointDialog?: () => void; openAddTestDialog?: () => void; openPreflightPromptModal?: (props: { - title?: string; - description?: string; - placeholder?: string; + placeholder: string; defaultValue?: string; onSubmit?: (value: string | null) => void; }) => void; @@ -86,9 +74,12 @@ type LaboratoryContextActions = LaboratoryCollectionsActions & ) => boolean; }; -const LaboratoryContext = createContext( - {} as LaboratoryContextState & LaboratoryContextActions, -); +type LaboratoryContext = LaboratoryContextState & + LaboratoryContextActions & { + container: HTMLDivElement | null; + }; + +const LaboratoryContext = createContext({} as LaboratoryContext); export const useLaboratory = () => { return useContext(LaboratoryContext); @@ -108,6 +99,7 @@ export interface LaboratoryPermissions { } export interface LaboratoryApi { + theme?: 'light' | 'dark'; defaultEndpoint?: string | null; onEndpointChange?: (endpoint: string | null) => void; defaultSchemaIntrospection?: IntrospectionQuery | null; @@ -144,9 +136,7 @@ export interface LaboratoryApi { openUpdateEndpointDialog?: () => void; openAddTestDialog?: () => void; openPreflightPromptModal?: (props: { - title?: string; - description?: string; - placeholder?: string; + placeholder: string; defaultValue?: string; onSubmit?: (value: string | null) => void; }) => void; @@ -176,7 +166,9 @@ export interface LaboratoryApi { export type LaboratoryContextProps = LaboratoryContextState & LaboratoryContextActions & - LaboratoryApi; + LaboratoryApi & { + container: HTMLDivElement | null; + }; export const LaboratoryProvider = (props: React.PropsWithChildren) => { return ( diff --git a/packages/web/app/src/laboratory/components/laboratory/editor.tsx b/packages/libraries/laboratory/src/components/laboratory/editor.tsx similarity index 71% rename from packages/web/app/src/laboratory/components/laboratory/editor.tsx rename to packages/libraries/laboratory/src/components/laboratory/editor.tsx index 209637867..ead2b11e4 100644 --- a/packages/web/app/src/laboratory/components/laboratory/editor.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/editor.tsx @@ -1,11 +1,11 @@ -import { forwardRef, useEffect, useId, useImperativeHandle, useRef } from 'react'; +import { forwardRef, useEffect, useId, useImperativeHandle, useRef, useState } from 'react'; import * as monaco from 'monaco-editor'; import { initializeMode } from 'monaco-graphql/initializeMode'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; import MonacoEditor, { loader } from '@monaco-editor/react'; +import { useLaboratory } from './context'; if (typeof window !== 'undefined') { - (window as any).monaco = monaco; + (window as Window & typeof globalThis & { monaco: typeof monaco }).monaco = monaco; } loader.config({ monaco }); @@ -73,7 +73,7 @@ const darkTheme: monaco.editor.IStandaloneThemeData = { ], colors: { 'editor.foreground': '#f6f8fa', - 'editor.background': '#18181b', + 'editor.background': '#0f1214', 'editor.selectionBackground': '#2A2F34', 'editor.inactiveSelectionBackground': '#2A2F34', 'editor.lineHighlightBackground': '#2A2F34', @@ -87,6 +87,15 @@ const darkTheme: monaco.editor.IStandaloneThemeData = { monaco.editor.defineTheme('hive-laboratory-dark', darkTheme); +const lightTheme: monaco.editor.IStandaloneThemeData = { + base: 'vs', + inherit: true, + rules: [], + colors: {}, +}; + +monaco.editor.defineTheme('hive-laboratory-light', lightTheme); + monaco.languages.setMonarchTokensProvider('dotenv', { tokenizer: { root: [ @@ -104,19 +113,21 @@ monaco.languages.setMonarchTokensProvider('dotenv', { }, }); -export const Editor = forwardRef< - { - setValue: (value: string) => void; - }, - React.ComponentProps & { - uri?: monaco.Uri; - variablesUri?: monaco.Uri; - extraLibs?: string[]; - } ->((props, ref) => { +export type EditorHandle = { + setValue: (value: string) => void; +}; + +export type EditorProps = React.ComponentProps & { + uri?: monaco.Uri; + variablesUri?: monaco.Uri; + extraLibs?: string[]; +}; + +const EditorInner = forwardRef((props, ref) => { const id = useId(); const editorRef = useRef(null); - const { introspection, endpoint } = useLaboratory(); + const { introspection, endpoint, theme } = useLaboratory(); + const [typescriptReady, setTypescriptReady] = useState(!!monaco.languages.typescript); useEffect(() => { if (introspection) { @@ -147,23 +158,46 @@ export const Editor = forwardRef< }, [introspection, props.uri?.toString(), props.variablesUri?.toString()]); useEffect(() => { - if (props.extraLibs) { - for (const lib of props.extraLibs) { - monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ - target: monaco.languages.typescript.ScriptTarget.ESNext, // supports top-level await - module: monaco.languages.typescript.ModuleKind.ESNext, // treat file as module - allowNonTsExtensions: true, - allowJs: true, - lib: ['esnext', 'webworker'], // if running in sandbox - }); - - monaco.languages.typescript.typescriptDefaults.addExtraLib( - lib, - `file:///hive-lab-globals-${id}.d.ts`, - ); + void (async function () { + if (!props.extraLibs?.length) { + return; } - } - }, []); + + if (!monaco.languages.typescript) { + await import('monaco-editor/esm/vs/language/typescript/monaco.contribution'); + setTypescriptReady(true); + } + + const ts = monaco.languages.typescript; + + if (!ts) { + return; + } + + const extraLibs = Object.values(ts.typescriptDefaults.getExtraLibs()).map(lib => lib.content); + + if (props.extraLibs.every(lib => extraLibs.includes(lib))) { + return; + } + + const safeId = id.replace(/[^a-zA-Z0-9_-]/g, '_'); + + ts.typescriptDefaults.setCompilerOptions({ + target: ts.ScriptTarget.ESNext, + module: ts.ModuleKind.ESNext, + allowNonTsExtensions: true, + allowJs: true, + lib: ['esnext', 'webworker'], + }); + + ts.typescriptDefaults.setExtraLibs( + props.extraLibs.map((content, index) => ({ + content, + filePath: `file:///hive-lab-globals-${safeId}-${index}.d.ts`, + })), + ); + })(); + }, [id, props.extraLibs]); useImperativeHandle( ref, @@ -177,18 +211,25 @@ export const Editor = forwardRef< [], ); + if (!typescriptReady && props.language === 'typescript') { + return null; + } + return ( -
+
{ editorRef.current = editor; }} loading={null} options={{ ...props.options, + lineNumbers: 'on', + cursorStyle: 'line', + cursorBlinking: 'smooth', padding: { top: 16, }, @@ -206,3 +247,5 @@ export const Editor = forwardRef<
); }); + +export const Editor = EditorInner as unknown as (props: EditorProps) => JSX.Element; diff --git a/packages/web/app/src/laboratory/components/laboratory/env.tsx b/packages/libraries/laboratory/src/components/laboratory/env.tsx similarity index 81% rename from packages/web/app/src/laboratory/components/laboratory/env.tsx rename to packages/libraries/laboratory/src/components/laboratory/env.tsx index c00c2f726..db12506b8 100644 --- a/packages/web/app/src/laboratory/components/laboratory/env.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/env.tsx @@ -1,11 +1,11 @@ -import { useLaboratory } from '@/laboratory/components/laboratory/context'; -import { Editor } from '@/laboratory/components/laboratory/editor'; +import { useLaboratory } from './context'; +import { Editor } from './editor'; export const Env = () => { const { env, setEnv } = useLaboratory(); return ( -
+
`${key}=${value}`) diff --git a/packages/web/app/src/laboratory/components/laboratory/history-item.tsx b/packages/libraries/laboratory/src/components/laboratory/history-item.tsx similarity index 68% rename from packages/web/app/src/laboratory/components/laboratory/history-item.tsx rename to packages/libraries/laboratory/src/components/laboratory/history-item.tsx index 723382312..7eceeeebf 100644 --- a/packages/web/app/src/laboratory/components/laboratory/history-item.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/history-item.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; -import { Operation } from '@/laboratory/components/laboratory/operation'; -import { LaboratoryHistoryRequest } from '@/laboratory/lib/history'; +import { LaboratoryHistoryRequest } from '../../lib/history'; +import { useLaboratory } from './context'; +import { Operation } from './operation'; export const HistoryItem = () => { const { activeTab, history } = useLaboratory(); diff --git a/packages/web/app/src/laboratory/components/laboratory/history.tsx b/packages/libraries/laboratory/src/components/laboratory/history.tsx similarity index 84% rename from packages/web/app/src/laboratory/components/laboratory/history.tsx rename to packages/libraries/laboratory/src/components/laboratory/history.tsx index 0c4b88681..8689ca898 100644 --- a/packages/web/app/src/laboratory/components/laboratory/history.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/history.tsx @@ -1,7 +1,8 @@ import { useCallback, useMemo, useState } from 'react'; import { format } from 'date-fns'; import { ClockIcon, FolderClockIcon, FolderOpenIcon, HistoryIcon, TrashIcon } from 'lucide-react'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; +import type { LaboratoryHistory, LaboratoryHistoryRequest } from '../../lib/history'; +import { cn } from '../../lib/utils'; import { AlertDialog, AlertDialogAction, @@ -12,24 +13,13 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from '@/laboratory/components/ui/alert-dialog'; -import { Button } from '@/laboratory/components/ui/button'; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/laboratory/components/ui/collapsible'; -import { - Empty, - EmptyDescription, - EmptyHeader, - EmptyMedia, - EmptyTitle, -} from '@/laboratory/components/ui/empty'; -import { ScrollArea, ScrollBar } from '@/laboratory/components/ui/scroll-area'; -import { Tooltip, TooltipContent, TooltipTrigger } from '@/laboratory/components/ui/tooltip'; -import type { LaboratoryHistory, LaboratoryHistoryRequest } from '@/laboratory/lib/history'; -import { cn } from '@/laboratory/lib/utils'; +} from '../ui/alert-dialog'; +import { Button } from '../ui/button'; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../ui/collapsible'; +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty'; +import { ScrollArea, ScrollBar } from '../ui/scroll-area'; +import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'; +import { useLaboratory } from './context'; export const HistoryOperationItem = (props: { historyItem: LaboratoryHistoryRequest }) => { const { activeTab, addTab, setActiveTab, deleteHistory } = useLaboratory(); @@ -57,8 +47,8 @@ export const HistoryOperationItem = (props: { historyItem: LaboratoryHistoryRequ - + {props.group.items.map(h => { return ; })} @@ -239,7 +229,7 @@ export const History = () => { return (
-
+
History
@@ -249,7 +239,7 @@ export const History = () => {
@@ -428,46 +443,50 @@ const LaboratoryContent = () => { export type LaboratoryProps = LaboratoryApi; export const Laboratory = ( - props: Pick< - LaboratoryProps, - | 'permissions' - | 'defaultEndpoint' - | 'onEndpointChange' - | 'defaultCollections' - | 'onCollectionsChange' - | 'onCollectionCreate' - | 'onCollectionUpdate' - | 'onCollectionDelete' - | 'onCollectionOperationCreate' - | 'onCollectionOperationUpdate' - | 'onCollectionOperationDelete' - | 'defaultOperations' - | 'onOperationsChange' - | 'defaultActiveOperationId' - | 'onActiveOperationIdChange' - | 'onOperationCreate' - | 'onOperationUpdate' - | 'onOperationDelete' - | 'defaultHistory' - | 'onHistoryChange' - | 'onHistoryCreate' - | 'onHistoryUpdate' - | 'onHistoryDelete' - | 'defaultTabs' - | 'onTabsChange' - | 'defaultPreflight' - | 'onPreflightChange' - | 'defaultEnv' - | 'onEnvChange' - | 'defaultActiveTabId' - | 'onActiveTabIdChange' - | 'defaultSettings' - | 'onSettingsChange' - | 'defaultTests' - | 'onTestsChange' - | 'plugins' - | 'defaultPluginsState' - | 'onPluginsStateChange' + props: Partial< + Pick< + LaboratoryProps, + | 'permissions' + | 'defaultEndpoint' + | 'onEndpointChange' + | 'defaultCollections' + | 'onCollectionsChange' + | 'onCollectionCreate' + | 'onCollectionUpdate' + | 'onCollectionDelete' + | 'onCollectionOperationCreate' + | 'onCollectionOperationUpdate' + | 'onCollectionOperationDelete' + | 'defaultOperations' + | 'onOperationsChange' + | 'defaultActiveOperationId' + | 'onActiveOperationIdChange' + | 'onOperationCreate' + | 'onOperationUpdate' + | 'onOperationDelete' + | 'defaultHistory' + | 'onHistoryChange' + | 'onHistoryCreate' + | 'onHistoryUpdate' + | 'onHistoryDelete' + | 'defaultTabs' + | 'onTabsChange' + | 'defaultPreflight' + | 'onPreflightChange' + | 'defaultEnv' + | 'onEnvChange' + | 'defaultActiveTabId' + | 'onActiveTabIdChange' + | 'defaultSettings' + | 'onSettingsChange' + | 'defaultTests' + | 'onTestsChange' + | 'plugins' + | 'defaultPluginsState' + | 'onPluginsStateChange' + | 'theme' + | 'defaultSchemaIntrospection' + > >, ) => { const checkPermissions = useCallback( @@ -485,51 +504,11 @@ export const Laboratory = ( [props.permissions], ); - const [isPreflightPromptModalOpen, setIsPreflightPromptModalOpen] = useState(false); - - const [preflightPromptModalProps, setPreflightPromptModalProps] = useState<{ - title?: string; - description?: string; - placeholder?: string; - defaultValue?: string; - onSubmit?: (value: string | null) => void; - }>({ - title: undefined, - description: undefined, - placeholder: undefined, - defaultValue: undefined, - onSubmit: undefined, - }); - - const openPreflightPromptModal = useCallback( - (props: { - title?: string; - description?: string; - placeholder?: string; - defaultValue?: string; - onSubmit?: (value: string | null) => void; - }) => { - setPreflightPromptModalProps({ - title: props.title, - description: props.description, - placeholder: props.placeholder, - defaultValue: props.defaultValue, - onSubmit: props.onSubmit, - }); - - setTimeout(() => { - setIsPreflightPromptModalOpen(true); - }, 200); - }, - [], - ); - const settingsApi = useSettings(props); const envApi = useEnv(props); const preflightApi = usePreflight({ ...props, envApi, - openPreflightPromptModal, }); const pluginsApi = usePlugins(props); @@ -613,6 +592,37 @@ export const Laboratory = ( }, }); + const [isPreflightPromptModalOpen, setIsPreflightPromptModalOpen] = useState(false); + + const [preflightPromptModalProps, setPreflightPromptModalProps] = useState<{ + placeholder: string; + defaultValue?: string; + onSubmit?: (value: string | null) => void; + }>({ + placeholder: '', + defaultValue: undefined, + onSubmit: undefined, + }); + + const openPreflightPromptModal = useCallback( + (props: { + placeholder: string; + defaultValue?: string; + onSubmit?: (value: string | null) => void; + }) => { + setPreflightPromptModalProps({ + placeholder: props.placeholder, + defaultValue: props.defaultValue, + onSubmit: props.onSubmit, + }); + + setTimeout(() => { + setIsPreflightPromptModalOpen(true); + }, 200); + }, + [], + ); + const containerRef = useRef(null); const [isFullScreen, setIsFullScreen] = useState(false); @@ -626,193 +636,192 @@ export const Laboratory = ( }, []); return ( -
- - - - - Update endpoint - Update the endpoint of your laboratory. - -
- { - e.preventDefault(); - void updateEndpointForm.handleSubmit(); - }} - > - - - {field => { - const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; - - return ( - field.handleChange(e.target.value)} - aria-invalid={isInvalid} - placeholder="Enter endpoint" - autoComplete="off" - /> - ); - }} - - - -
- - - - - - -
-
- - - - - Add collection - - Add a new collection of operations to your laboratory. - - -
-
{ - e.preventDefault(); - void addCollectionForm.handleSubmit(); - }} - > - - - {field => { - const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; - return ( - - Name - field.handleChange(e.target.value)} - aria-invalid={isInvalid} - placeholder="Enter name of the collection" - autoComplete="off" - /> - {isInvalid && } - - ); - }} - - -
-
- - - - - - -
-
- - - - Add test - Add a new test to your laboratory. - -
-
{ - e.preventDefault(); - void addTestForm.handleSubmit(); - }} - > - - - {field => { - const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; - return ( - - Name - field.handleChange(e.target.value)} - aria-invalid={isInvalid} - placeholder="Enter name of the test" - autoComplete="off" - /> - {isInvalid && } - - ); - }} - - -
-
- - - - - - -
-
- - + +
- - -
+ + + + + Update endpoint + Update the endpoint of your laboratory. + +
+
{ + e.preventDefault(); + void updateEndpointForm.handleSubmit(); + }} + > + + + {field => { + const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + + return ( + field.handleChange(e.target.value)} + aria-invalid={isInvalid} + placeholder="Enter endpoint" + autoComplete="off" + /> + ); + }} + + +
+
+ + + + + + +
+
+ + + + + Add collection + + Add a new collection of operations to your laboratory. + + +
+
{ + e.preventDefault(); + void addCollectionForm.handleSubmit(); + }} + > + + + {field => { + const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + return ( + + Name + field.handleChange(e.target.value)} + aria-invalid={isInvalid} + placeholder="Enter name of the collection" + autoComplete="off" + /> + {isInvalid && } + + ); + }} + + +
+
+ + + + + + +
+
+ + + + Add test + Add a new test to your laboratory. + +
+
{ + e.preventDefault(); + void addTestForm.handleSubmit(); + }} + > + + + {field => { + const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + return ( + + Name + field.handleChange(e.target.value)} + aria-invalid={isInvalid} + placeholder="Enter name of the test" + autoComplete="off" + /> + {isInvalid && } + + ); + }} + + +
+
+ + + + + + +
+
+ + + + +
+ ); }; diff --git a/packages/web/app/src/laboratory/components/laboratory/operation.tsx b/packages/libraries/laboratory/src/components/laboratory/operation.tsx similarity index 87% rename from packages/web/app/src/laboratory/components/laboratory/operation.tsx rename to packages/libraries/laboratory/src/components/laboratory/operation.tsx index b0132857f..7d1d2da5c 100644 --- a/packages/web/app/src/laboratory/components/laboratory/operation.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/operation.tsx @@ -16,11 +16,17 @@ import { compressToEncodedURIComponent } from 'lz-string'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { toast } from 'sonner'; import { z } from 'zod'; -import { Builder } from '@/laboratory/components/laboratory/builder'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; -import { Editor } from '@/laboratory/components/laboratory/editor'; -import { Badge } from '@/laboratory/components/ui/badge'; -import { Button } from '@/laboratory/components/ui/button'; +import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu'; +import { useForm } from '@tanstack/react-form'; +import type { + LaboratoryHistory, + LaboratoryHistoryRequest, + LaboratoryHistorySubscription, +} from '../../lib/history'; +import type { LaboratoryOperation } from '../../lib/operations'; +import { cn } from '../../lib/utils'; +import { Badge } from '../ui/badge'; +import { Button } from '../ui/button'; import { Dialog, DialogClose, @@ -29,45 +35,19 @@ import { DialogFooter, DialogHeader, DialogTitle, -} from '@/laboratory/components/ui/dialog'; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, -} from '@/laboratory/components/ui/dropdown-menu'; -import { - Empty, - EmptyDescription, - EmptyHeader, - EmptyMedia, - EmptyTitle, -} from '@/laboratory/components/ui/empty'; -import { Field, FieldGroup, FieldLabel } from '@/laboratory/components/ui/field'; -import { - ResizableHandle, - ResizablePanel, - ResizablePanelGroup, -} from '@/laboratory/components/ui/resizable'; -import { ScrollArea, ScrollBar } from '@/laboratory/components/ui/scroll-area'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/laboratory/components/ui/select'; -import { Spinner } from '@/laboratory/components/ui/spinner'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/laboratory/components/ui/tabs'; -import { Toggle } from '@/laboratory/components/ui/toggle'; -import type { - LaboratoryHistory, - LaboratoryHistoryRequest, - LaboratoryHistorySubscription, -} from '@/laboratory/lib/history'; -import type { LaboratoryOperation } from '@/laboratory/lib/operations'; -import { cn } from '@/laboratory/lib/utils'; -import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu'; -import { useForm } from '@tanstack/react-form'; +} from '../ui/dialog'; +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from '../ui/dropdown-menu'; +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty'; +import { Field, FieldGroup, FieldLabel } from '../ui/field'; +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../ui/resizable'; +import { ScrollArea, ScrollBar } from '../ui/scroll-area'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'; +import { Spinner } from '../ui/spinner'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; +import { Toggle } from '../ui/toggle'; +import { Builder } from './builder'; +import { useLaboratory } from './context'; +import { Editor } from './editor'; const variablesUri = monaco.Uri.file('variables.json'); @@ -178,14 +158,14 @@ export const ResponsePreflight = ({ historyItem }: { historyItem?: LaboratoryHis
{historyItem?.preflightLogs?.map((log, i) => (
- {log.createdAt}{' '} + {log.createdAt}{' '} {log.level.toUpperCase()} @@ -208,7 +188,7 @@ export const ResponseSubscription = ({ return (
-
+
Subscription
{isActiveOperationLoading ? ( @@ -224,7 +204,7 @@ export const ResponseSubscription = ({
-
+
{historyItem?.responses .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) .map((response, i) => { @@ -237,11 +217,7 @@ export const ResponseSubscription = ({ const height = 20.5 * value.split('\n').length; return ( -
+
)} {historyItem?.duration && ( - + {Math.round((historyItem as LaboratoryHistoryRequest).duration!)} @@ -325,7 +301,7 @@ export const Response = ({ historyItem }: { historyItem?: LaboratoryHistoryReque )} {historyItem?.size && ( - + {Math.round((historyItem as LaboratoryHistoryRequest).size! / 1024)} @@ -336,13 +312,13 @@ export const Response = ({ historyItem }: { historyItem?: LaboratoryHistoryReque
) : null} - + - + - + @@ -549,7 +525,7 @@ export const Query = (props: { ); return ( -
+
@@ -607,7 +583,7 @@ export const Query = (props: { -
+
Operation {checkPermissions?.('collectionsOperations:create') && ( { setPreflight({ ...(preflight ?? { script: '', enabled: true }), @@ -715,23 +691,21 @@ export const Query = (props: { )}
-
- { - updateActiveOperation({ - query: value ?? '', - }); - }} - language="graphql" - theme="hive-laboratory" - options={{ - readOnly: props.isReadOnly, - }} - /> -
+ { + updateActiveOperation({ + query: value ?? '', + }); + }} + language="graphql" + theme="hive-laboratory" + options={{ + readOnly: props.isReadOnly, + }} + />
); }; @@ -761,7 +735,7 @@ export const Operation = (props: { }, [props.historyItem]); return ( -
+
@@ -773,7 +747,7 @@ export const Operation = (props: { - + @@ -786,13 +760,13 @@ export const Operation = (props: { Extensions - + - + - + @@ -813,7 +787,7 @@ export const Operation = (props: { - + No history yet diff --git a/packages/web/app/src/laboratory/components/laboratory/preflight.tsx b/packages/libraries/laboratory/src/components/laboratory/preflight.tsx similarity index 89% rename from packages/web/app/src/laboratory/components/laboratory/preflight.tsx rename to packages/libraries/laboratory/src/components/laboratory/preflight.tsx index 128d35b8b..3d3f818e3 100644 --- a/packages/web/app/src/laboratory/components/laboratory/preflight.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/preflight.tsx @@ -1,23 +1,13 @@ import { useCallback } from 'react'; import { HistoryIcon, PlayIcon } from 'lucide-react'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; -import { Editor } from '@/laboratory/components/laboratory/editor'; -import { Button } from '@/laboratory/components/ui/button'; -import { - Empty, - EmptyDescription, - EmptyHeader, - EmptyMedia, - EmptyTitle, -} from '@/laboratory/components/ui/empty'; -import { - ResizableHandle, - ResizablePanel, - ResizablePanelGroup, -} from '@/laboratory/components/ui/resizable'; -import { ScrollArea, ScrollBar } from '@/laboratory/components/ui/scroll-area'; -import { runIsolatedLabScript } from '@/laboratory/lib/preflight'; -import { cn } from '@/laboratory/lib/utils'; +import { runIsolatedLabScript } from '../../lib/preflight'; +import { cn } from '../../lib/utils'; +import { Button } from '../ui/button'; +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty'; +import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../ui/resizable'; +import { ScrollArea, ScrollBar } from '../ui/scroll-area'; +import { useLaboratory } from './context'; +import { Editor } from './editor'; export const Preflight = () => { const { @@ -41,12 +31,10 @@ export const Preflight = () => { const result = await runIsolatedLabScript( preflight?.script ?? '', env ?? { variables: {} }, - (title, defaultValue, options) => { + (placeholder, defaultValue) => { return new Promise(resolve => { openPreflightPromptModal?.({ - title, - description: options?.description, - placeholder: options?.placeholder, + placeholder, defaultValue, onSubmit: value => { resolve(value); @@ -65,9 +53,9 @@ export const Preflight = () => { return ( - +
-
+
Preflight
- + {preflight?.lastTestResult?.logs && preflight?.lastTestResult?.logs.length > 0 ? (
-
+
Logs
@@ -264,14 +252,14 @@ export const Preflight = () => {
{preflight?.lastTestResult?.logs.map((log, i) => (
- {log.createdAt}{' '} + {log.createdAt}{' '} {log.level.toUpperCase()} @@ -287,7 +275,7 @@ export const Preflight = () => { - + No logs yet diff --git a/packages/web/app/src/laboratory/components/laboratory/settings.tsx b/packages/libraries/laboratory/src/components/laboratory/settings.tsx similarity index 83% rename from packages/web/app/src/laboratory/components/laboratory/settings.tsx rename to packages/libraries/laboratory/src/components/laboratory/settings.tsx index 86adaf2ae..852f170dc 100644 --- a/packages/web/app/src/laboratory/components/laboratory/settings.tsx +++ b/packages/libraries/laboratory/src/components/laboratory/settings.tsx @@ -1,21 +1,9 @@ import { z } from 'zod'; -import { useLaboratory } from '@/laboratory/components/laboratory/context'; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from '@/laboratory/components/ui/card'; -import { Field, FieldGroup, FieldLabel } from '@/laboratory/components/ui/field'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/laboratory/components/ui/select'; import { useForm } from '@tanstack/react-form'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'; +import { Field, FieldGroup, FieldLabel } from '../ui/field'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'; +import { useLaboratory } from './context'; const settingsFormSchema = z.object({ fetch: z.object({ @@ -37,7 +25,7 @@ export const Settings = () => { }); return ( -
+
; + return ; } if (props.item.type === 'test') { @@ -239,7 +239,7 @@ export const Tab = (props: { return customTab.icon; } - return ; + return ; }, [props.item, isError]); return ( @@ -250,7 +250,7 @@ export const Tab = (props: { asHandle className={cn( 'data-dragging:opacity-0 flex h-12 w-max items-stretch', - props.isOverlay && 'bg-neutral-3', + props.isOverlay && 'bg-background', props.isOverlay && !isActive && 'h-12', )} > @@ -278,9 +278,8 @@ export const Tab = (props: { >
{ props.setActiveTab(props.item); @@ -297,9 +296,9 @@ export const Tab = (props: { {tabIcon} {tabName} {props.isOperationLoading(props.item.id) && } - {props.item.readOnly && } + {props.item.readOnly && } { e.stopPropagation(); }} @@ -310,7 +309,7 @@ export const Tab = (props: { />
-
+
@@ -412,7 +411,7 @@ export const Tabs = ({ className }: { className?: string }) => {
-
+
@@ -467,7 +466,7 @@ export const Tabs = ({ className }: { className?: string }) => {