This commit is contained in:
Brendan Kellam 2025-02-28 14:21:26 -08:00 committed by GitHub
parent bdab90ba41
commit 8f6052c8e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 57 additions and 31 deletions

View file

@ -6,15 +6,8 @@ import { useThemeNormalized } from "@/hooks/useThemeNormalized";
import { json, jsonLanguage, jsonParseLinter } from "@codemirror/lang-json";
import { linter } from "@codemirror/lint";
import { EditorView, hoverTooltip } from "@codemirror/view";
import CodeMirror, { ReactCodeMirrorRef } from "@uiw/react-codemirror";
import {
handleRefresh,
jsonCompletion,
jsonSchemaHover,
jsonSchemaLinter,
stateExtensions
} from "codemirror-json-schema";
import { useRef, forwardRef, useImperativeHandle, Ref, ReactNode, useState } from "react";
import CodeMirror, { Extension, ReactCodeMirrorRef } from "@uiw/react-codemirror";
import { useRef, forwardRef, useImperativeHandle, Ref, ReactNode, useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Schema } from "ajv";
@ -94,7 +87,7 @@ export function onQuickAction<T>(
const cursorPos = next.lastIndexOf(selectionText);
if (cursorPos >= 0) {
view.dispatch({
selection: {
selection: {
anchor: cursorPos,
head: cursorPos + selectionText.length
}
@ -120,7 +113,6 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
const editorRef = useRef<ReactCodeMirrorRef>(null);
const [isViewMoreActionsEnabled, setIsViewMoreActionsEnabled] = useState(false);
const [height, setHeight] = useState(224);
useImperativeHandle(
forwardedRef,
() => editorRef.current as ReactCodeMirrorRef
@ -129,6 +121,51 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
const keymapExtension = useKeymapExtension(editorRef.current?.view);
const { theme } = useThemeNormalized();
// ⚠️ DISGUSTING HACK AHEAD ⚠️
// Background: When navigating to the /connections/:id?tab=settings page, we were hitting a 500 error with the following
// message server side:
//
// > Internal error: Error: Element type is invalid: expected a string (for built-in components) or a class/function
// > (for composite components) but got: undefined. You likely forgot to export your component from the file it's
// > defined in, or you might have mixed up default and named imports.
//
// Why was this happening? We have no idea, but we isolated it to the extensions exported by the `codemirror-json-schema`
// package. The solution that worked was to dynamically import the package inside of the useEffect and load the extensions
// async.
//
// So, yeah. - Brendan
const [jsonSchemaExtensions, setJsonSchemaExtensions] = useState<Extension[]>([]);
useEffect(() => {
const loadExtensions = async () => {
const {
handleRefresh,
jsonCompletion,
jsonSchemaHover,
jsonSchemaLinter,
stateExtensions
} = await import('codemirror-json-schema');
return [
linter(jsonParseLinter(), {
delay: 300,
}),
linter(jsonSchemaLinter(), {
needsRefresh: handleRefresh,
}),
jsonLanguage.data.of({
autocomplete: jsonCompletion(),
}),
hoverTooltip(jsonSchemaHover()),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stateExtensions(schema as any),
]
}
loadExtensions().then((extensions) => {
console.debug('Loaded json schema extensions');
setJsonSchemaExtensions(extensions);
});
}, [schema]);
return (
<div className="border rounded-md">
<div className="flex flex-row items-center flex-wrap p-1">
@ -211,19 +248,8 @@ const ConfigEditor = <T,>(props: ConfigEditorProps<T>, forwardedRef: Ref<ReactCo
extensions={[
keymapExtension,
json(),
linter(jsonParseLinter(), {
delay: 300,
}),
linter(jsonSchemaLinter(), {
needsRefresh: handleRefresh,
}),
jsonLanguage.data.of({
autocomplete: jsonCompletion(),
}),
hoverTooltip(jsonSchemaHover()),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stateExtensions(schema as any),
customAutocompleteStyle,
...jsonSchemaExtensions,
]}
theme={theme === "dark" ? "dark" : "light"}
/>

View file

@ -126,7 +126,7 @@ export const EditorContextMenu = ({
}
}
)
}, [captureEvent, path, repoName, selection.from, selection.to, toast, view, revisionName]);
}, [selection.from, selection.to, domain, repoName, revisionName, path, toast, captureEvent, view]);
return (
<div

View file

@ -1,3 +1,5 @@
'use client';
import { getDisplayTime } from "@/lib/utils";
import Image from "next/image";
import { StatusIcon } from "../../components/statusIcon";

View file

@ -42,6 +42,7 @@ export default async function ConnectionManagementPage({ params, searchParams }:
const currentTab = searchParams.tab || "overview";
return (
<Tabs value={currentTab} className="w-full">
<Header className="mb-6" withTopMargin={false}>
@ -85,12 +86,7 @@ export default async function ConnectionManagementPage({ params, searchParams }:
</TabsContent>
<TabsContent
value="settings"
// @note: There was some bugginess with the ConfigEditor ref not being set again
// after the parent component was unmounted and remounted. This workarouns makes it
// s.t., hide the settings tab when it is inactive, instead of unmounting it.
// @see: https://github.com/radix-ui/primitives/issues/2359#issuecomment-2481321719
className="flex flex-col gap-6 data-[state=inactive]:hidden"
forceMount={true}
className="flex flex-col gap-6"
>
<DisplayNameSetting connectionId={connection.id} name={connection.name} />
<ConfigSetting

View file

@ -1,3 +1,5 @@
'use client';
import { cn, CodeHostType, getCodeHostIcon } from "@/lib/utils";
import { useMemo } from "react";
import Image from "next/image";

View file

@ -10,7 +10,7 @@ import useCaptureEvent from "@/hooks/useCaptureEvent";
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
import { useSearchHistory } from "@/hooks/useSearchHistory";
import { Repository, SearchQueryParams, SearchResultFile } from "@/lib/types";
import { createPathWithQueryParams, measureSync } from "@/lib/utils";
import { createPathWithQueryParams } from "@/lib/utils";
import { SymbolIcon } from "@radix-ui/react-icons";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "next/navigation";