aboutsummaryrefslogtreecommitdiffstats
path: root/ui/src/components/runbooks/editor
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/components/runbooks/editor')
-rw-r--r--ui/src/components/runbooks/editor/Editor.tsx94
-rw-r--r--ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx23
2 files changed, 92 insertions, 25 deletions
diff --git a/ui/src/components/runbooks/editor/Editor.tsx b/ui/src/components/runbooks/editor/Editor.tsx
index 3f05d9f3..8ed52660 100644
--- a/ui/src/components/runbooks/editor/Editor.tsx
+++ b/ui/src/components/runbooks/editor/Editor.tsx
@@ -1,13 +1,17 @@
+import { useEffect, useMemo, useState } from "react";
+
import "@blocknote/core/fonts/inter.css";
import "@blocknote/mantine/style.css";
import "./index.css";
import {
BlockNoteSchema,
+ BlockNoteEditor,
defaultBlockSpecs,
filterSuggestionItems,
insertOrUpdateBlock,
} from "@blocknote/core";
+
import "@blocknote/core/fonts/inter.css";
import {
@@ -21,9 +25,12 @@ import {
import { BlockNoteView } from "@blocknote/mantine";
import { Code } from "lucide-react";
+import { useDebounceCallback } from "usehooks-ts";
import RunBlock from "@/components/runbooks/editor/blocks/RunBlock";
import { DeleteBlock } from "@/components/runbooks/editor/ui/DeleteBlockButton";
+import { 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.
@@ -51,26 +58,79 @@ const insertRun = (editor: typeof schema.BlockNoteEditor) => ({
});
export default function Editor() {
- // Creates a new editor instance.
- const editor = useCreateBlockNote({
- schema,
- initialContent: [
- {
- type: "heading",
- content: "Atuin runbooks",
- id: "foo",
- },
- {
- type: "run",
- id: "bar",
- },
- ],
- });
+ const runbookId = useStore((store) => store.currentRunbook);
+ const refreshRunbooks = useStore((store) => 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 editor = useMemo(() => {
+ if (!runbook) {
+ return undefined;
+ }
+
+ if (runbook.content) {
+ return BlockNoteEditor.create({
+ initialContent: JSON.parse(runbook.content),
+ schema,
+ });
+ }
+
+ return BlockNoteEditor.create({ schema });
+ }, [runbook]);
+
+ const onChange = async () => {
+ if (!runbook) return;
+
+ console.log("saved!");
+ runbook.name = fetchName();
+ runbook.content = JSON.stringify(editor.document);
+
+ await runbook.save();
+ await refreshRunbooks();
+ };
+
+ const debouncedOnChange = useDebounceCallback(onChange, 1000);
+
+ const fetchName = (): String => {
+ // Infer the title from the first text block
+
+ let blocks = editor.document;
+ for (const block of blocks) {
+ if (block.type == "heading" || block.type == "paragraph") {
+ if (block.content.length == 0) continue;
+ if (block.content[0].text.length == 0) continue;
+
+ return block.content[0].text;
+ }
+ }
+
+ return "Untitled";
+ };
+
+ if (editor === undefined) {
+ return "Loading content...";
+ }
// Renders the editor instance.
return (
- <div>
- <BlockNoteView editor={editor} slashMenu={false} sideMenu={false}>
+ <div className="p-4 w-full">
+ <BlockNoteView
+ editor={editor}
+ slashMenu={false}
+ sideMenu={false}
+ onChange={debouncedOnChange}
+ >
<SuggestionMenuController
triggerCharacter={"/"}
getItems={async (query) =>
diff --git a/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx b/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx
index 78928876..9b2fe515 100644
--- a/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx
+++ b/ui/src/components/runbooks/editor/blocks/RunBlock/index.tsx
@@ -23,17 +23,20 @@ interface RunBlockProps {
isEditable: boolean;
}
-const RunBlock = ({ onPlay, id, code, isEditable }: RunBlockProps) => {
+const RunBlock = ({
+ onChange,
+ onPlay,
+ id,
+ code,
+ isEditable,
+}: RunBlockProps) => {
+ console.log(code);
const [isRunning, setIsRunning] = useState(false);
const [showTerminal, setShowTerminal] = useState(false);
const [value, setValue] = useState<String>(code);
const [pty, setPty] = useState<string | null>(null);
- const onChange = (val: any) => {
- setValue(val);
- };
-
const handleToggle = async (event: any) => {
event.stopPropagation();
@@ -88,7 +91,10 @@ const RunBlock = ({ onPlay, id, code, isEditable }: RunBlockProps) => {
editable={isEditable}
width="100%"
autoFocus
- onChange={onChange}
+ onChange={(val) => {
+ setValue(val);
+ onChange(val);
+ }}
extensions={[...extensions(), langs.shell()]}
basicSetup={false}
/>
@@ -123,14 +129,15 @@ export default createReactBlockSpec(
editor.updateBlock(block, {
props: { ...block.props, code: val },
});
+ console.log(block.props);
};
return (
<RunBlock
onChange={onInputChange}
id={block?.id}
- code={code}
- type={type}
+ code={block.props.code}
+ type={block.props.type}
isEditable={editor.isEditable}
/>
);