aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src/components/runbooks/editor
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@atuin.sh>2024-07-30 16:54:10 +0100
committerGitHub <noreply@github.com>2024-07-30 16:54:10 +0100
commit808138de633e410c1d3867d4fb7cb74967647605 (patch)
treef180b7066b91d8d8d8006219a118439be1621d74 /ui/src/components/runbooks/editor
parentchore(deps): bump debian (#2320) (diff)
downloadatuin-808138de633e410c1d3867d4fb7cb74967647605.zip
chore: remove ui directory (#2329)
This is still in development, but rather than clutter the commit history and issues with an unreleased project I've split the UI into its own repo. Once ready for release, I'll either merge the ui code back in, or just make the repo public.
Diffstat (limited to 'ui/src/components/runbooks/editor')
-rw-r--r--ui/src/components/runbooks/editor/Editor.tsx200
-rw-r--r--ui/src/components/runbooks/editor/blocks/Directory/index.tsx89
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/extensions.ts158
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/index.css9
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/index.tsx229
-rw-r--r--ui/src/components/runbooks/editor/blocks/Run/terminal.tsx113
-rw-r--r--ui/src/components/runbooks/editor/index.css7
-rw-r--r--ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx28
8 files changed, 0 insertions, 833 deletions
diff --git a/ui/src/components/runbooks/editor/Editor.tsx b/ui/src/components/runbooks/editor/Editor.tsx
deleted file mode 100644
index 6b0522f5..00000000
--- a/ui/src/components/runbooks/editor/Editor.tsx
+++ /dev/null
@@ -1,200 +0,0 @@
-import { useEffect, useMemo, useState } from "react";
-
-import "./index.css";
-
-import { Spinner } from "@nextui-org/react";
-
-// Errors, but it all works fine and is there. Maybe missing ts defs?
-// I'll figure it out later
-import {
- // @ts-ignore
- BlockNoteSchema,
- // @ts-ignore
- BlockNoteEditor,
- // @ts-ignore
- defaultBlockSpecs,
- // @ts-ignore
- filterSuggestionItems,
- // @ts-ignore
- insertOrUpdateBlock,
-} from "@blocknote/core";
-
-import {
- //@ts-ignore
- SuggestionMenuController,
- // @ts-ignore
- AddBlockButton,
- // @ts-ignore
- getDefaultReactSlashMenuItems,
- // @ts-ignore
- SideMenu,
- // @ts-ignore
- SideMenuController,
-} from "@blocknote/react";
-import { BlockNoteView } from "@blocknote/mantine";
-
-import "@blocknote/core/fonts/inter.css";
-import "@blocknote/mantine/style.css";
-
-import { CodeIcon, FolderOpenIcon } from "lucide-react";
-import { useDebounceCallback } from "usehooks-ts";
-
-import Run from "@/components/runbooks/editor/blocks/Run";
-import Directory from "@/components/runbooks/editor/blocks/Directory";
-
-import { DeleteBlock } from "@/components/runbooks/editor/ui/DeleteBlockButton";
-import { AtuinState, useStore } from "@/state/store";
-import Runbook from "@/state/runbooks/runbook";
-
-// Our schema with block specs, which contain the configs and implementations for blocks
-// that we want our editor to use.
-const schema = BlockNoteSchema.create({
- blockSpecs: {
- // Adds all default blocks.
- ...defaultBlockSpecs,
-
- // Adds the code block.
- run: Run,
- directory: Directory,
- },
-});
-
-// Slash menu item to insert an Alert block
-const insertRun = (editor: typeof schema.BlockNoteEditor) => ({
- title: "Code",
- onItemClick: () => {
- insertOrUpdateBlock(editor, {
- type: "run",
- });
- },
- icon: <CodeIcon size={18} />,
- aliases: ["code", "run"],
- group: "Execute",
-});
-
-const insertDirectory = (editor: typeof schema.BlockNoteEditor) => ({
- title: "Directory",
- onItemClick: () => {
- insertOrUpdateBlock(editor, {
- type: "directory",
- });
- },
- icon: <FolderOpenIcon size={18} />,
- aliases: ["directory", "dir", "folder"],
- group: "Execute",
-});
-
-export default function Editor() {
- const runbookId = useStore((store: AtuinState) => store.currentRunbook);
- const refreshRunbooks = useStore(
- (store: AtuinState) => store.refreshRunbooks,
- );
- let [runbook, setRunbook] = useState<Runbook | null>(null);
-
- useEffect(() => {
- if (!runbookId) return;
-
- const fetchRunbook = async () => {
- let rb = await Runbook.load(runbookId);
-
- setRunbook(rb);
- };
-
- fetchRunbook();
- }, [runbookId]);
-
- const onChange = async () => {
- if (!runbook) return;
-
- console.log("saved!");
- runbook.name = fetchName();
- if (editor) runbook.content = JSON.stringify(editor.document);
-
- await runbook.save();
- refreshRunbooks();
- };
-
- const debouncedOnChange = useDebounceCallback(onChange, 1000);
-
- const editor = useMemo(() => {
- if (!runbook) return undefined;
- if (runbook.content) {
- return BlockNoteEditor.create({
- initialContent: JSON.parse(runbook.content),
- schema,
- });
- }
-
- return BlockNoteEditor.create({ schema });
- }, [runbook]);
-
- const fetchName = (): string => {
- // Infer the title from the first text block
- if (!editor) return "Untitled";
-
- let blocks = editor.document;
- for (const block of blocks) {
- if (block.type == "heading" || block.type == "paragraph") {
- if (block.content.length == 0) continue;
- // @ts-ignore
- if (block.content[0].text.length == 0) continue;
-
- // @ts-ignore
- return block.content[0].text;
- }
- }
-
- return "Untitled";
- };
-
- if (!runbook) {
- return (
- <div className="flex w-full h-full flex-col justify-center items-center">
- <Spinner />
- </div>
- );
- }
-
- if (editor === undefined) {
- return (
- <div className="flex w-full h-full flex-col justify-center items-center">
- <Spinner />
- </div>
- );
- }
-
- // Renders the editor instance.
- return (
- <div className="overflow-y-scroll w-full">
- <BlockNoteView
- editor={editor}
- slashMenu={false}
- sideMenu={false}
- onChange={debouncedOnChange}
- >
- <SuggestionMenuController
- triggerCharacter={"/"}
- getItems={async (query: any) =>
- filterSuggestionItems(
- [
- ...getDefaultReactSlashMenuItems(editor),
- insertRun(editor),
- insertDirectory(editor),
- ],
- query,
- )
- }
- />
-
- <SideMenuController
- sideMenu={(props: any) => (
- <SideMenu {...props}>
- <AddBlockButton {...props} />
- <DeleteBlock {...props} />
- </SideMenu>
- )}
- />
- </BlockNoteView>
- </div>
- );
-}
diff --git a/ui/src/components/runbooks/editor/blocks/Directory/index.tsx b/ui/src/components/runbooks/editor/blocks/Directory/index.tsx
deleted file mode 100644
index 3e4f93d9..00000000
--- a/ui/src/components/runbooks/editor/blocks/Directory/index.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-import { useState } from "react";
-import { Input, Tooltip, Button } from "@nextui-org/react";
-import { FolderInputIcon } from "lucide-react";
-
-// @ts-ignore
-import { createReactBlockSpec } from "@blocknote/react";
-
-import { open } from "@tauri-apps/plugin-dialog";
-
-interface DirectoryProps {
- path: string;
- onInputChange: (val: string) => void;
-}
-
-const Directory = ({ path, onInputChange }: DirectoryProps) => {
- const [value, setValue] = useState(path);
-
- const selectFolder = async () => {
- const path = await open({
- multiple: false,
- directory: true,
- });
-
- setValue(path || "");
- onInputChange(path || "");
- };
-
- return (
- <div className="w-full !max-w-full !outline-none overflow-none">
- <Tooltip
- content="Change working directory for all subsequent code blocks"
- delay={1000}
- >
- <div className="flex flex-row">
- <div className="mr-2">
- <Button
- isIconOnly
- variant="flat"
- aria-label="Select folder"
- onPress={selectFolder}
- >
- <FolderInputIcon />
- </Button>
- </div>
-
- <div className="w-full">
- <Input
- placeholder="~"
- value={value}
- autoComplete="off"
- autoCapitalize="off"
- autoCorrect="off"
- spellCheck="false"
- onValueChange={(val) => {
- setValue(val);
- onInputChange(val);
- }}
- />
- </div>
- </div>
- </Tooltip>
- </div>
- );
-};
-
-export default createReactBlockSpec(
- {
- type: "directory",
- propSchema: {
- path: { default: "" },
- },
- content: "none",
- },
- {
- // @ts-ignore
- render: ({ block, editor, code, type }) => {
- const onInputChange = (val: string) => {
- editor.updateBlock(block, {
- // @ts-ignore
- props: { ...block.props, path: val },
- });
- };
-
- return (
- <Directory path={block.props.path} onInputChange={onInputChange} />
- );
- },
- },
-);
diff --git a/ui/src/components/runbooks/editor/blocks/Run/extensions.ts b/ui/src/components/runbooks/editor/blocks/Run/extensions.ts
deleted file mode 100644
index 76fc4343..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/extensions.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-// Based on the basicSetup extension, as suggested by the source. Customized for Atuin.
-
-import {
- KeyBinding,
- lineNumbers,
- highlightActiveLineGutter,
- highlightSpecialChars,
- drawSelection,
- dropCursor,
- rectangularSelection,
- crosshairCursor,
- highlightActiveLine,
- keymap,
-} from "@codemirror/view";
-import { EditorState, Extension } from "@codemirror/state";
-import { history, defaultKeymap, historyKeymap } from "@codemirror/commands";
-import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
-
-import {
- closeBrackets,
- autocompletion,
- closeBracketsKeymap,
- completionKeymap,
- CompletionContext,
-} from "@codemirror/autocomplete";
-
-import {
- foldGutter,
- indentOnInput,
- syntaxHighlighting,
- defaultHighlightStyle,
- bracketMatching,
- indentUnit,
- foldKeymap,
-} from "@codemirror/language";
-
-import { lintKeymap } from "@codemirror/lint";
-import { invoke } from "@tauri-apps/api/core";
-
-export interface MinimalSetupOptions {
- highlightSpecialChars?: boolean;
- history?: boolean;
- drawSelection?: boolean;
- syntaxHighlighting?: boolean;
-
- defaultKeymap?: boolean;
- historyKeymap?: boolean;
-}
-
-export interface BasicSetupOptions extends MinimalSetupOptions {
- lineNumbers?: boolean;
- highlightActiveLineGutter?: boolean;
- foldGutter?: boolean;
- dropCursor?: boolean;
- allowMultipleSelections?: boolean;
- indentOnInput?: boolean;
- bracketMatching?: boolean;
- closeBrackets?: boolean;
- autocompletion?: boolean;
- rectangularSelection?: boolean;
- crosshairCursor?: boolean;
- highlightActiveLine?: boolean;
- highlightSelectionMatches?: boolean;
-
- closeBracketsKeymap?: boolean;
- searchKeymap?: boolean;
- foldKeymap?: boolean;
- completionKeymap?: boolean;
- lintKeymap?: boolean;
- tabSize?: number;
-}
-
-function myCompletions(context: CompletionContext) {
- let word = context.matchBefore(/^.*/);
-
- if (!word) return null;
- if (word.from == word.to && !context.explicit) return null;
-
- return invoke("prefix_search", { query: word.text }).then(
- // @ts-ignore
- (results: string[]) => {
- let options = results.map((i) => {
- return { label: i, type: "text" };
- });
-
- return {
- from: word.from,
- options,
- };
- },
- );
-}
-
-const buildAutocomplete = (): Extension => {
- let ac = autocompletion({ override: [myCompletions] });
-
- return ac;
-};
-
-export const extensions = (options: BasicSetupOptions = {}): Extension[] => {
- const { crosshairCursor: initCrosshairCursor = false } = options;
-
- let keymaps: KeyBinding[] = [];
- if (options.closeBracketsKeymap !== false) {
- keymaps = keymaps.concat(closeBracketsKeymap);
- }
- if (options.defaultKeymap !== false) {
- keymaps = keymaps.concat(defaultKeymap);
- }
- if (options.searchKeymap !== false) {
- keymaps = keymaps.concat(searchKeymap);
- }
- if (options.historyKeymap !== false) {
- keymaps = keymaps.concat(historyKeymap);
- }
- if (options.foldKeymap !== false) {
- keymaps = keymaps.concat(foldKeymap);
- }
- if (options.completionKeymap !== false) {
- keymaps = keymaps.concat(completionKeymap);
- }
- if (options.lintKeymap !== false) {
- keymaps = keymaps.concat(lintKeymap);
- }
- const extensions: Extension[] = [];
- if (options.lineNumbers !== false) extensions.push(lineNumbers());
- if (options.highlightActiveLineGutter !== false)
- extensions.push(highlightActiveLineGutter());
- if (options.highlightSpecialChars !== false)
- extensions.push(highlightSpecialChars());
- if (options.history !== false) extensions.push(history());
- if (options.foldGutter !== false) extensions.push(foldGutter());
- if (options.drawSelection !== false) extensions.push(drawSelection());
- if (options.dropCursor !== false) extensions.push(dropCursor());
- if (options.allowMultipleSelections !== false)
- extensions.push(EditorState.allowMultipleSelections.of(true));
- if (options.indentOnInput !== false) extensions.push(indentOnInput());
- if (options.syntaxHighlighting !== false)
- extensions.push(
- syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
- );
-
- if (options.bracketMatching !== false) extensions.push(bracketMatching());
- if (options.closeBrackets !== false) extensions.push(closeBrackets());
- if (options.autocompletion !== false) extensions.push(buildAutocomplete());
-
- if (options.rectangularSelection !== false)
- extensions.push(rectangularSelection());
- if (initCrosshairCursor !== false) extensions.push(crosshairCursor());
- if (options.highlightActiveLine !== false)
- extensions.push(highlightActiveLine());
- if (options.highlightSelectionMatches !== false)
- extensions.push(highlightSelectionMatches());
- if (options.tabSize && typeof options.tabSize === "number")
- extensions.push(indentUnit.of(" ".repeat(options.tabSize)));
-
- return extensions.concat([keymap.of(keymaps.flat())]).filter(Boolean);
-};
diff --git a/ui/src/components/runbooks/editor/blocks/Run/index.css b/ui/src/components/runbooks/editor/blocks/Run/index.css
deleted file mode 100644
index e854c03b..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/index.css
+++ /dev/null
@@ -1,9 +0,0 @@
-ProseMirror-focused {
- outline: none !important;
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1) !important;
-}
-
-.cm-editor.cm-focused {
- outline: none !important;
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1) !important;
-}
diff --git a/ui/src/components/runbooks/editor/blocks/Run/index.tsx b/ui/src/components/runbooks/editor/blocks/Run/index.tsx
deleted file mode 100644
index bef083ba..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/index.tsx
+++ /dev/null
@@ -1,229 +0,0 @@
-// @ts-ignore
-import { createReactBlockSpec } from "@blocknote/react";
-
-import "./index.css";
-
-import CodeMirror from "@uiw/react-codemirror";
-import { keymap } from "@codemirror/view";
-import { langs } from "@uiw/codemirror-extensions-langs";
-
-import { Play, Square } from "lucide-react";
-import { useState } from "react";
-
-import { extensions } from "./extensions";
-import { platform } from "@tauri-apps/plugin-os";
-import { invoke } from "@tauri-apps/api/core";
-import Terminal from "./terminal.tsx";
-
-import "@xterm/xterm/css/xterm.css";
-import { AtuinState, useStore } from "@/state/store.ts";
-
-interface RunBlockProps {
- onChange: (val: string) => void;
- onRun?: (pty: string) => void;
- onStop?: (pty: string) => void;
- id: string;
- code: string;
- type: string;
- pty: string;
- isEditable: boolean;
- editor: any;
-}
-
-const findFirstParentOfType = (editor: any, id: string, type: string): any => {
- // TODO: the types for blocknote aren't working. Now I'm doing this sort of shit,
- // really need to fix that.
- const document = editor.document;
- var lastOfType = null;
-
- // Iterate through ALL of the blocks.
- for (let i = 0; i < document.length; i++) {
- if (document[i].id == id) return lastOfType;
-
- if (document[i].type == type) lastOfType = document[i];
- }
-
- return lastOfType;
-};
-
-const RunBlock = ({
- onChange,
- id,
- code,
- isEditable,
- onRun,
- onStop,
- pty,
- editor,
-}: RunBlockProps) => {
- const [value, setValue] = useState<String>(code);
- const cleanupPtyTerm = useStore((store: AtuinState) => store.cleanupPtyTerm);
- const terminals = useStore((store: AtuinState) => store.terminals);
-
- const [currentRunbook, incRunbookPty, decRunbookPty] = useStore(
- (store: AtuinState) => [
- store.currentRunbook,
- store.incRunbookPty,
- store.decRunbookPty,
- ],
- );
-
- const isRunning = pty !== null && pty !== "";
-
- const handleToggle = async (event: any | null) => {
- if (event) event.stopPropagation();
-
- // If there's no code, don't do anything
- if (!value) return;
-
- if (isRunning) {
- await invoke("pty_kill", { pid: pty });
-
- terminals[pty].terminal.dispose();
- cleanupPtyTerm(pty);
-
- if (onStop) onStop(pty);
- if (currentRunbook) decRunbookPty(currentRunbook);
- }
-
- if (!isRunning) {
- let cwd = findFirstParentOfType(editor, id, "directory");
-
- if (cwd) {
- cwd = cwd.props.path;
- } else {
- cwd = "~";
- }
-
- let pty = await invoke<string>("pty_open", { cwd });
- if (onRun) onRun(pty);
-
- if (currentRunbook) incRunbookPty(currentRunbook);
-
- let isWindows = platform() == "windows";
- let cmdEnd = isWindows ? "\r\n" : "\n";
-
- let val = !value.endsWith("\n") ? value + cmdEnd : value;
- await invoke("pty_write", { pid: pty, data: val });
- }
- };
-
- const handleCmdEnter = () => {
- handleToggle(null);
- return true;
- };
-
- const customKeymap = keymap.of([
- {
- key: "Mod-Enter",
- run: handleCmdEnter,
- },
- ]);
-
- return (
- <div className="w-full !max-w-full !outline-none overflow-none">
- <div className="flex flex-row items-start">
- <div className="flex">
- <button
- onClick={handleToggle}
- className={`flex items-center justify-center flex-shrink-0 w-8 h-8 mr-2 rounded border focus:outline-none focus:ring-2 transition-all duration-300 ease-in-out ${
- isRunning
- ? "border-red-200 bg-red-50 text-red-600 hover:bg-red-100 hover:border-red-300 focus:ring-red-300"
- : "border-green-200 bg-green-50 text-green-600 hover:bg-green-100 hover:border-green-300 focus:ring-green-300"
- }`}
- aria-label={isRunning ? "Stop code" : "Run code"}
- >
- <span
- className={`inline-block transition-transform duration-300 ease-in-out ${isRunning ? "rotate-180" : ""}`}
- >
- {isRunning ? <Square size={16} /> : <Play size={16} />}
- </span>
- </button>
- </div>
- <div className="flex-1 min-w-0 w-40">
- <CodeMirror
- id={id}
- placeholder={"Write your code here..."}
- className="!pt-0 max-w-full border border-gray-300 rounded"
- value={code}
- editable={isEditable}
- autoFocus
- onChange={(val) => {
- setValue(val);
- onChange(val);
- }}
- extensions={[customKeymap, ...extensions(), langs.shell()]}
- basicSetup={false}
- />
- <div
- className={`overflow-hidden transition-all duration-300 ease-in-out min-w-0 ${
- isRunning ? "block" : "hidden"
- }`}
- >
- {pty && <Terminal pty={pty} />}
- </div>
- </div>
- </div>
- </div>
- );
-};
-
-export default createReactBlockSpec(
- {
- type: "run",
- propSchema: {
- type: {
- default: "bash",
- },
- code: { default: "" },
- pty: { default: "" },
- global: { default: false },
- },
- content: "none",
- },
- {
- // @ts-ignore
- render: ({ block, editor, code, type }) => {
- const onInputChange = (val: string) => {
- editor.updateBlock(block, {
- // @ts-ignore
- props: { ...block.props, code: val },
- });
- };
-
- const onRun = (pty: string) => {
- editor.updateBlock(block, {
- // @ts-ignore
- props: { ...block.props, pty: pty },
- });
- };
-
- const onStop = (_pty: string) => {
- editor?.updateBlock(block, {
- props: { ...block.props, pty: "" },
- });
- };
-
- return (
- <RunBlock
- onChange={onInputChange}
- id={block?.id}
- code={block.props.code}
- type={block.props.type}
- pty={block.props.pty}
- isEditable={editor.isEditable}
- onRun={onRun}
- onStop={onStop}
- editor={editor}
- />
- );
- },
- toExternalHTML: ({ block }) => {
- return (
- <pre lang="beep boop">
- <code lang="bash">{block?.props?.code}</code>
- </pre>
- );
- },
- },
-);
diff --git a/ui/src/components/runbooks/editor/blocks/Run/terminal.tsx b/ui/src/components/runbooks/editor/blocks/Run/terminal.tsx
deleted file mode 100644
index a6dc589f..00000000
--- a/ui/src/components/runbooks/editor/blocks/Run/terminal.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { useState, useEffect, useRef } from "react";
-import { listen } from "@tauri-apps/api/event";
-import "@xterm/xterm/css/xterm.css";
-import { useStore } from "@/state/store";
-import { invoke } from "@tauri-apps/api/core";
-import { IDisposable } from "@xterm/xterm";
-
-const usePersistentTerminal = (pty: string) => {
- const newPtyTerm = useStore((store) => store.newPtyTerm);
- const terminals = useStore((store) => store.terminals);
- const [isReady, setIsReady] = useState(false);
-
- useEffect(() => {
- if (!terminals.hasOwnProperty(pty)) {
- // create a new terminal and store it in the store.
- // this means we can resume the same instance even across mount/dismount
- newPtyTerm(pty);
- }
-
- setIsReady(true);
-
- return () => {
- // We don't dispose of the terminal when the component unmounts
- };
- }, [pty, terminals, newPtyTerm]);
-
- return { terminalData: terminals[pty], isReady };
-};
-
-const TerminalComponent = ({ pty }: any) => {
- const terminalRef = useRef(null);
- const { terminalData, isReady } = usePersistentTerminal(pty);
- const [isAttached, setIsAttached] = useState(false);
- const cleanupListenerRef = useRef<(() => void) | null>(null);
- const keyDispose = useRef<IDisposable | null>(null);
-
- useEffect(() => {
- // no pty? no terminal
- if (pty == null) return;
-
- // the terminal may still be being created so hold off
- if (!isReady) return;
-
- const windowResize = () => {
- if (!terminalData || !terminalData.fitAddon) return;
-
- terminalData.fitAddon.fit();
- };
-
- // terminal object needs attaching to a ref to a div
- if (!isAttached && terminalData && terminalData.terminal) {
- // If it's never been attached, attach it
- if (!terminalData.terminal.element && terminalRef.current) {
- terminalData.terminal.open(terminalRef.current);
-
- // it might have been previously attached, but need moving elsewhere
- } else if (terminalData && terminalRef.current) {
- // @ts-ignore
- terminalRef.current.appendChild(terminalData.terminal.element);
- }
-
- terminalData.fitAddon.fit();
- setIsAttached(true);
-
- window.addEventListener("resize", windowResize);
-
- const disposeOnKey = terminalData.terminal.onKey(async (event) => {
- await invoke("pty_write", { pid: pty, data: event.key });
- });
-
- keyDispose.current = disposeOnKey;
- }
-
- listen(`pty-${pty}`, (event: any) => {
- terminalData.terminal.write(event.payload);
- }).then((ul) => {
- cleanupListenerRef.current = ul;
- });
-
- // Customize further as needed
- return () => {
- if (
- terminalData &&
- terminalData.terminal &&
- terminalData.terminal.element
- ) {
- // Instead of removing, we just detach
- if (terminalData.terminal.element.parentElement) {
- terminalData.terminal.element.parentElement.removeChild(
- terminalData.terminal.element,
- );
- }
- setIsAttached(false);
- }
-
- if (cleanupListenerRef.current) {
- cleanupListenerRef.current();
- }
-
- if (keyDispose.current) keyDispose.current.dispose();
-
- window.removeEventListener("resize", windowResize);
- };
- }, [terminalData, isReady]);
-
- if (!isReady) return null;
-
- return (
- <div className="!max-w-full min-w-0 overflow-hidden" ref={terminalRef} />
- );
-};
-
-export default TerminalComponent;
diff --git a/ui/src/components/runbooks/editor/index.css b/ui/src/components/runbooks/editor/index.css
deleted file mode 100644
index 067cc500..00000000
--- a/ui/src/components/runbooks/editor/index.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.editor a {
- color: #0000ee;
-}
-
-.editor a:hover {
- cursor: pointer;
-}
diff --git a/ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx b/ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx
deleted file mode 100644
index 84a9f5c8..00000000
--- a/ui/src/components/runbooks/editor/ui/DeleteBlockButton.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import {
- SideMenuProps,
- useBlockNoteEditor,
- useComponentsContext,
-} from "@blocknote/react";
-import { TrashIcon } from "lucide-react";
-
-// Custom Side Menu button to remove the hovered block.
-export function DeleteBlock(props: SideMenuProps) {
- const editor = useBlockNoteEditor();
-
- const Components = useComponentsContext()!;
-
- return (
- <Components.SideMenu.Button
- label="Remove block"
- className="mx-1"
- icon={
- <TrashIcon
- size={24}
- onClick={() => {
- editor.removeBlocks([props.block]);
- }}
- />
- }
- />
- );
-}