diff --git a/virtweb_frontend/package-lock.json b/virtweb_frontend/package-lock.json
index a8cc8d7..95ada5d 100644
--- a/virtweb_frontend/package-lock.json
+++ b/virtweb_frontend/package-lock.json
@@ -29,7 +29,8 @@
"react-syntax-highlighter": "^15.6.1",
"react-vnc": "^3.1.0",
"uuid": "^11.1.0",
- "xml-formatter": "^3.6.6"
+ "xml-formatter": "^3.6.6",
+ "yaml": "^2.8.0"
},
"devDependencies": {
"@eslint/js": "^9.27.0",
diff --git a/virtweb_frontend/package.json b/virtweb_frontend/package.json
index e4ef8ce..b105ec7 100644
--- a/virtweb_frontend/package.json
+++ b/virtweb_frontend/package.json
@@ -31,7 +31,8 @@
"react-syntax-highlighter": "^15.6.1",
"react-vnc": "^3.1.0",
"uuid": "^11.1.0",
- "xml-formatter": "^3.6.6"
+ "xml-formatter": "^3.6.6",
+ "yaml": "^2.8.0"
},
"devDependencies": {
"@eslint/js": "^9.27.0",
diff --git a/virtweb_frontend/src/widgets/forms/CloudInitEditor.tsx b/virtweb_frontend/src/widgets/forms/CloudInitEditor.tsx
index 3f654bc..a84ba8f 100644
--- a/virtweb_frontend/src/widgets/forms/CloudInitEditor.tsx
+++ b/virtweb_frontend/src/widgets/forms/CloudInitEditor.tsx
@@ -2,7 +2,9 @@ import Editor from "@monaco-editor/react";
import BookIcon from "@mui/icons-material/Book";
import RefreshIcon from "@mui/icons-material/Refresh";
import { Grid, IconButton, InputAdornment, Tooltip } from "@mui/material";
+import React from "react";
import { v4 as uuidv4 } from "uuid";
+import YAML from "yaml";
import { VMInfo } from "../../api/VMApi";
import { RouterLink } from "../RouterLink";
import { CheckboxInput } from "./CheckboxInput";
@@ -44,6 +46,10 @@ export function CloudInitEditor(p: CloudInitProps): React.ReactElement {
{...p}
editable={p.editable && p.vm.cloud_init.attach_config}
/>
+
>
);
@@ -183,3 +189,76 @@ function CloudInitNetworkConfig(p: CloudInitProps): React.ReactElement {
);
}
+
+function CloudInitUserDataAssistant(p: CloudInitProps): React.ReactElement {
+ const user_data = React.useMemo(() => {
+ return YAML.parseDocument(p.vm.cloud_init.user_data);
+ }, [p.vm.cloud_init.user_data]);
+
+ const onChange = () => {
+ p.vm.cloud_init.user_data = user_data.toString();
+
+ if (!p.vm.cloud_init.user_data.startsWith("#cloud-config"))
+ p.vm.cloud_init.user_data = `#cloud-config\n${p.vm.cloud_init.user_data}`;
+
+ p.onChange?.();
+ };
+
+ return (
+
+
+
+
+
+ );
+}
+
+function CloudInitTextInput(p: {
+ editable: boolean;
+ name: string;
+ refUrl: string;
+ attrPath: Iterable;
+ yaml: YAML.Document;
+ onChange: () => void;
+}): React.ReactElement {
+ return (
+ {
+ if (v !== undefined) p.yaml.setIn(p.attrPath, v);
+ else p.yaml.deleteIn(p.attrPath);
+ p.onChange?.();
+ }}
+ endAdornment={
+
+
+
+
+
+ }
+ />
+ );
+}