More template work & configure backend URL

This commit is contained in:
space-nuko
2023-05-24 23:21:06 -05:00
parent 4ae4e71616
commit 0fedef30c0
17 changed files with 764 additions and 623 deletions

7
public/config.json Normal file
View File

@@ -0,0 +1,7 @@
{
"comfyUIHostname": "localhost",
"comfyUIPort": 8188,
"alwaysStripUserState": false,
"promptForWorkflowName": false,
"confirmWhenUnloadingUnsavedChanges": true
}

View File

@@ -1,10 +1,11 @@
import { Subgraph, type LGraphNode, type LLink, type SerializedLGraphNode, type SerializedLLink, LGraph, type NodeID, type UUID } from "@litegraph-ts/core" import { Subgraph, type LGraphNode, type LLink, type SerializedLGraphNode, type SerializedLLink, LGraph, type NodeID, type UUID, type Vector2 } from "@litegraph-ts/core"
import layoutStates, { isComfyWidgetNode, type ContainerLayout, type SerializedDragEntry, type WidgetLayout, type DragItemID, type WritableLayoutStateStore, type DragItemEntry, type SerializedLayoutState } from "./stores/layoutStates" import layoutStates, { isComfyWidgetNode, type ContainerLayout, type SerializedDragEntry, type WidgetLayout, type DragItemID, type WritableLayoutStateStore, type DragItemEntry, type SerializedLayoutState } from "./stores/layoutStates"
import type { ComfyWidgetNode } from "./nodes/widgets" import type { ComfyWidgetNode } from "./nodes/widgets"
import type ComfyGraphCanvas from "./ComfyGraphCanvas" import type ComfyGraphCanvas from "./ComfyGraphCanvas"
import C2S from "canvas-to-svg"; import C2S from "canvas-to-svg";
import { download } from "./utils"; import { calcNodesBoundingBox, download } from "./utils";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import uiState from "./stores/uiState";
/* /*
* In ComfyBox a template contains a subset of nodes in the graph and the set of * In ComfyBox a template contains a subset of nodes in the graph and the set of
@@ -270,6 +271,19 @@ export function embedTemplateInSvg(template: SerializedComfyBoxTemplate): string
return svg return svg
} }
/*
* Moves nodes so their origin is at (0, 0)
*/
function relocateNodes(nodes: SerializedLGraphNode[]): SerializedLGraphNode[] {
let [min_x, min_y, max_x, max_y] = calcNodesBoundingBox(nodes);
for (const node of nodes) {
node.pos = [node.pos[0] - min_x, node.pos[1] - min_y];
}
return nodes;
}
function pruneDetachedLinks(nodes: SerializedLGraphNode[], links: SerializedTemplateLink[]): [SerializedLGraphNode[], SerializedTemplateLink[]] { function pruneDetachedLinks(nodes: SerializedLGraphNode[], links: SerializedTemplateLink[]): [SerializedLGraphNode[], SerializedTemplateLink[]] {
const nodeIds = new Set(nodes.map(n => n.id)); const nodeIds = new Set(nodes.map(n => n.id));
@@ -313,11 +327,16 @@ export function serializeTemplate(canvas: ComfyGraphCanvas, template: ComfyBoxTe
if (layoutState == null) if (layoutState == null)
throw "Couldn't find layout for template being serialized!" throw "Couldn't find layout for template being serialized!"
uiState.update(s => { s.forceSaveUserState = false; return s; });
const metadata = template.metadata; const metadata = template.metadata;
let nodes = template.nodes.map(n => n.serialize()); let nodes = template.nodes.map(n => n.serialize());
let links = template.links.map(convLinkForTemplate); let links = template.links.map(convLinkForTemplate);
const layout = layoutState.serializeAtRoot(template.container.dragItem.id); const layout = layoutState.serializeAtRoot(template.container.dragItem.id);
uiState.update(s => { s.forceSaveUserState = null; return s; });
nodes = relocateNodes(nodes);
[nodes, links] = pruneDetachedLinks(nodes, links); [nodes, links] = pruneDetachedLinks(nodes, links);
const svg = renderSvg(canvas, graph, TEMPLATE_SVG_PADDING); const svg = renderSvg(canvas, graph, TEMPLATE_SVG_PADDING);

View File

@@ -16,30 +16,6 @@ import workflowState from "./stores/workflowState";
import type { SerializedComfyBoxTemplate } from "./ComfyBoxTemplate"; import type { SerializedComfyBoxTemplate } from "./ComfyBoxTemplate";
import { v4 as uuidv4 } from "uuid" import { v4 as uuidv4 } from "uuid"
function calculateMinPosOfNodes(nodes: SerializedLGraphNode[]): Vector2 {
var posMin: Vector2 = [0, 0]
var posMinIndexes: [number, number] | null = null;
for (var i = 0; i < nodes.length; ++i) {
if (posMin) {
if (posMin[0] > nodes[i].pos[0]) {
posMin[0] = nodes[i].pos[0];
posMinIndexes[0] = i;
}
if (posMin[1] > nodes[i].pos[1]) {
posMin[1] = nodes[i].pos[1];
posMinIndexes[1] = i;
}
}
else {
posMin = [nodes[i].pos[0], nodes[i].pos[1]];
posMinIndexes = [i, i];
}
}
return posMin;
}
type ComfyGraphEvents = { type ComfyGraphEvents = {
configured: (graph: LGraph) => void configured: (graph: LGraph) => void
nodeAdded: (node: LGraphNode) => void nodeAdded: (node: LGraphNode) => void
@@ -250,10 +226,11 @@ export default class ComfyGraph extends LGraph {
/* /*
* Inserts a template. * Inserts a template.
* Layout deserialization must be handled afterwards! * Layout deserialization must be handled afterwards.
* NOTE: Modifies the template in-place, be sure you cloned it beforehand!
*/ */
insertTemplate(template: SerializedComfyBoxTemplate, pos: Vector2): Record<NodeID, LGraphNode> { insertTemplate(template: SerializedComfyBoxTemplate, pos: Vector2): Record<NodeID, LGraphNode> {
const minPos = calculateMinPosOfNodes(template.nodes); const minPos = [0, 0]
const templateNodeIDToNewNode: Record<NodeID, LGraphNode> = {} const templateNodeIDToNewNode: Record<NodeID, LGraphNode> = {}

View File

@@ -9,6 +9,7 @@ import selectionState from "./stores/selectionState";
import templateState from "./stores/templateState"; import templateState from "./stores/templateState";
import { createTemplate, type ComfyBoxTemplate, serializeTemplate, type SerializedComfyBoxTemplate } from "./ComfyBoxTemplate"; import { createTemplate, type ComfyBoxTemplate, serializeTemplate, type SerializedComfyBoxTemplate } from "./ComfyBoxTemplate";
import notify from "./notify"; import notify from "./notify";
import { calcNodesBoundingBox } from "./utils";
export type SerializedGraphCanvasState = { export type SerializedGraphCanvasState = {
offset: Vector2, offset: Vector2,
@@ -448,14 +449,24 @@ export default class ComfyGraphCanvas extends LGraphCanvas {
insertTemplate(template: SerializedComfyBoxTemplate, pos: Vector2, container: ContainerLayout, containerIndex: number): [LGraphNode[], IDragItem] { insertTemplate(template: SerializedComfyBoxTemplate, pos: Vector2, container: ContainerLayout, containerIndex: number): [LGraphNode[], IDragItem] {
const comfyGraph = this.graph as ComfyGraph; const comfyGraph = this.graph as ComfyGraph;
let [min_x, min_y, max_x, max_y] = calcNodesBoundingBox(template.nodes);
const width = max_x - min_x
const height = max_y - min_y
pos[0] -= width / 2
pos[1] -= height / 2
const layout = comfyGraph.layout; const layout = comfyGraph.layout;
if (layout == null) { if (layout == null) {
console.error("[ComfyGraphCanvas] graph has no layout!", comfyGraph) console.error("[ComfyGraphCanvas] graph has no layout!", comfyGraph)
return; return;
} }
const nodeMapping = comfyGraph.insertTemplate(template, pos); // The following operations modify the template in-place, so be sure it's been cloned first
const templateLayoutRoot = layout.insertTemplate(template, comfyGraph, nodeMapping, container, containerIndex); const cloned = LiteGraph.cloneObject(template)
const nodeMapping = comfyGraph.insertTemplate(cloned, pos);
const templateLayoutRoot = layout.insertTemplate(cloned, comfyGraph, nodeMapping, container, containerIndex);
this.selectNodes(Object.values(nodeMapping).filter(n => n.graph === this.graph)); this.selectNodes(Object.values(nodeMapping).filter(n => n.graph === this.graph));

View File

@@ -22,7 +22,7 @@ import type ComfyGraphNode from "$lib/nodes/ComfyGraphNode";
import { ComfyComboNode } from "$lib/nodes/widgets"; import { ComfyComboNode } from "$lib/nodes/widgets";
import notify from "$lib/notify"; import notify from "$lib/notify";
import parseA1111, { type A1111ParsedInfotext } from "$lib/parseA1111"; import parseA1111, { type A1111ParsedInfotext } from "$lib/parseA1111";
import configState from "$lib/stores/configState"; import configState, { type ConfigState } from "$lib/stores/configState";
import layoutStates, { defaultWorkflowAttributes, type SerializedLayoutState } from "$lib/stores/layoutStates"; import layoutStates, { defaultWorkflowAttributes, type SerializedLayoutState } from "$lib/stores/layoutStates";
import modalState from "$lib/stores/modalState"; import modalState from "$lib/stores/modalState";
import queueState from "$lib/stores/queueState"; import queueState from "$lib/stores/queueState";
@@ -192,6 +192,11 @@ export default class ComfyApp {
return; return;
} }
await this.loadConfig();
this.api.hostname = get(configState).comfyUIHostname
this.api.port = get(configState).comfyUIPort
this.setupColorScheme() this.setupColorScheme()
this.rootEl = document.getElementById("app-root") as HTMLDivElement; this.rootEl = document.getElementById("app-root") as HTMLDivElement;
@@ -252,6 +257,20 @@ export default class ComfyApp {
return Promise.resolve(); return Promise.resolve();
} }
/*
* TODO
*/
async loadConfig() {
try {
const config = await fetch(`/config.json`);
const state = await config.json() as ConfigState;
configState.set(state);
}
catch (error) {
console.error(`Failed to load config`, error)
}
}
resizeCanvas() { resizeCanvas() {
this.canvasEl.width = this.canvasEl.parentElement.offsetWidth; this.canvasEl.width = this.canvasEl.parentElement.offsetWidth;
this.canvasEl.height = this.canvasEl.parentElement.offsetHeight; this.canvasEl.height = this.canvasEl.parentElement.offsetHeight;
@@ -282,7 +301,7 @@ export default class ComfyApp {
saveStateToLocalStorage() { saveStateToLocalStorage() {
try { try {
uiState.update(s => { s.isSavingToLocalStorage = true; return s; }) uiState.update(s => { s.forceSaveUserState = true; return s; })
const state = get(workflowState) const state = get(workflowState)
const workflows = state.openedWorkflows const workflows = state.openedWorkflows
const savedWorkflows = workflows.map(w => this.serialize(w)); const savedWorkflows = workflows.map(w => this.serialize(w));
@@ -298,7 +317,7 @@ export default class ComfyApp {
notify(`Failed saving to local storage:\n${err}`, { type: "error" }) notify(`Failed saving to local storage:\n${err}`, { type: "error" })
} }
finally { finally {
uiState.update(s => { s.isSavingToLocalStorage = false; return s; }) uiState.update(s => { s.forceSaveUserState = null; return s; })
} }
} }
@@ -1007,6 +1026,21 @@ export default class ComfyApp {
reader.readAsText(file); reader.readAsText(file);
} else if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) { } else if (file.type === "image/svg+xml" || file.name.endsWith(".svg")) {
const templateAndSvg = await deserializeTemplateFromSVG(file); const templateAndSvg = await deserializeTemplateFromSVG(file);
const importTemplate = () => {
try {
if (templateState.add(templateAndSvg)) {
notify("Template imported successfully!", { type: "success" })
}
else {
notify("Template already exists in saved list.", { type: "warning" })
}
}
catch (error) {
notify(`Error importing template: ${error}`, { type: "error", timeout: 10000 })
}
}
modalState.pushModal({ modalState.pushModal({
title: "ComfyBox Template Preview", title: "ComfyBox Template Preview",
svelteComponent: EditTemplateModal, svelteComponent: EditTemplateModal,
@@ -1017,6 +1051,11 @@ export default class ComfyApp {
editable: false editable: false
}, },
buttons: [ buttons: [
{
name: "Import",
variant: "primary",
onClick: importTemplate
},
{ {
name: "Close", name: "Close",
variant: "secondary", variant: "secondary",

View File

@@ -31,7 +31,6 @@
$: rebuildTemplates($templateState.templates); $: rebuildTemplates($templateState.templates);
function rebuildTemplates(templates: SerializedComfyBoxTemplate[]) { function rebuildTemplates(templates: SerializedComfyBoxTemplate[]) {
console.error("recreate");
_sorted = Array.from(templates).map(t => { _sorted = Array.from(templates).map(t => {
return { return {
type: "template", id: uuidv4(), template: t, attrs: {...defaultWidgetAttributes}, attrsChanged: writable(0) type: "template", id: uuidv4(), template: t, attrs: {...defaultWidgetAttributes}, attrsChanged: writable(0)
@@ -76,6 +75,9 @@
} }
function handleClick(layout: TemplateLayout) { function handleClick(layout: TemplateLayout) {
if ($uiState.uiUnlocked)
return;
const updateTemplate = (modal: ModalData) => { const updateTemplate = (modal: ModalData) => {
const state = get(modal.state); const state = get(modal.state);
layout.template.metadata.title = state.name || layout.template.metadata.title layout.template.metadata.title = state.name || layout.template.metadata.title
@@ -94,6 +96,23 @@
} }
} }
const deleteTemplate = (modal: ModalData) => {
if (!confirm("Are you sure you want to delete this template?"))
return false;
try {
if (templateState.remove(layout.template.id)) {
notify("Template deleted!", { type: "success" })
}
else {
notify("Failed to delete template: not saved to local storage.", { type: "warning" })
}
}
catch (error) {
notify(`Failed to delete template: ${error}`, { type: "error", timeout: 10000 })
}
}
const downloadTemplate = (modal: ModalData) => { const downloadTemplate = (modal: ModalData) => {
updateTemplate(modal); updateTemplate(modal);
const svg = embedTemplateInSvg(layout.template); const svg = embedTemplateInSvg(layout.template);
@@ -120,6 +139,11 @@
onClick: downloadTemplate, onClick: downloadTemplate,
closeOnClick: false closeOnClick: false
}, },
{
name: "Delete",
variant: "secondary",
onClick: deleteTemplate
},
{ {
name: "Close", name: "Close",
variant: "secondary", variant: "secondary",

View File

@@ -13,7 +13,8 @@
} }
function onButtonClicked(modal: ModalData, button: ModalButton, closeDialog: Function) { function onButtonClicked(modal: ModalData, button: ModalButton, closeDialog: Function) {
button.onClick(modal); if (button.onClick(modal) === false)
return
if (button.closeOnClick !== false) { if (button.closeOnClick !== false) {
closeDialog() closeDialog()

View File

@@ -2,6 +2,7 @@
import UploadText from "$lib/components/gradio/app/UploadText.svelte"; import UploadText from "$lib/components/gradio/app/UploadText.svelte";
import type { ComfyImageLocation } from "$lib/nodes/ComfyWidgetNodes"; import type { ComfyImageLocation } from "$lib/nodes/ComfyWidgetNodes";
import notify from "$lib/notify"; import notify from "$lib/notify";
import configState from "$lib/stores/configState";
import { convertComfyOutputEntryToGradio, convertComfyOutputToComfyURL, type ComfyUploadImageAPIResponse } from "$lib/utils"; import { convertComfyOutputEntryToGradio, convertComfyOutputToComfyURL, type ComfyUploadImageAPIResponse } from "$lib/utils";
import { Block, BlockLabel } from "@gradio/atoms"; import { Block, BlockLabel } from "@gradio/atoms";
import { File as FileIcon } from "@gradio/icons"; import { File as FileIcon } from "@gradio/icons";
@@ -68,7 +69,7 @@
dispatch("uploading") dispatch("uploading")
const url = `http://${location.hostname}:8188` // TODO make configurable const url = configState.getBackendURL();
const requests = files.map(async (file) => { const requests = files.map(async (file) => {
const formData = new FormData(); const formData = new FormData();

View File

@@ -1,6 +1,6 @@
import type ComfyGraphCanvas from "$lib/ComfyGraphCanvas"; import type ComfyGraphCanvas from "$lib/ComfyGraphCanvas";
import { type ContainerLayout, type IDragItem, type TemplateLayout, type WritableLayoutStateStore } from "$lib/stores/layoutStates" import { type ContainerLayout, type IDragItem, type TemplateLayout, type WritableLayoutStateStore } from "$lib/stores/layoutStates"
import type { LGraphCanvas } from "@litegraph-ts/core"; import type { LGraphCanvas, Vector2 } from "@litegraph-ts/core";
import { get } from "svelte/store"; import { get } from "svelte/store";
export function handleContainerConsider(layoutState: WritableLayoutStateStore, container: ContainerLayout, evt: CustomEvent<DndEvent<IDragItem>>): IDragItem[] { export function handleContainerConsider(layoutState: WritableLayoutStateStore, container: ContainerLayout, evt: CustomEvent<DndEvent<IDragItem>>): IDragItem[] {
@@ -39,12 +39,9 @@ function doInsertTemplate(layoutState: WritableLayoutStateStore, droppedTemplate
layoutState.updateChildren(container, newChildren); layoutState.updateChildren(container, newChildren);
const rect = canvas.ds.element.getBoundingClientRect(); const newPos: Vector2 = [canvas.visible_area[0] + canvas.visible_area[2] / 2, canvas.visible_area[1] + canvas.visible_area[3] / 2]
const width = rect?.width || 1;
const height = rect?.height || 1;
const center = canvas.convertOffsetToCanvas([width * 0.5, height * 0.5]);
canvas.insertTemplate(droppedTemplate.template, center, container, templateItemIndex); canvas.insertTemplate(droppedTemplate.template, newPos, container, templateItemIndex);
return get(layoutState).allItems[container.id].children; return get(layoutState).allItems[container.id].children;
} }

View File

@@ -312,7 +312,13 @@ export default class ComfyGraphNode extends LGraphNode {
} }
(o as any).saveUserState = this.saveUserState (o as any).saveUserState = this.saveUserState
if (!this.saveUserState && (!get(uiState).isSavingToLocalStorage || get(configState).alwaysStripUserState)) {
let saveUserState = this.saveUserState || get(configState).alwaysStripUserState;
const forceSaveUserState = get(uiState).forceSaveUserState;
if (forceSaveUserState !== null)
saveUserState = forceSaveUserState;
if (!saveUserState) {
this.stripUserState(o) this.stripUserState(o)
console.debug("[ComfyGraphNode] stripUserState", this, o) console.debug("[ComfyGraphNode] stripUserState", this, o)
} }

View File

@@ -3,6 +3,12 @@ import { get, writable } from 'svelte/store';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
export type ConfigState = { export type ConfigState = {
/** Backend domain for ComfyUI */
comfyUIHostname: string,
/** Backend port for ComfyUI */
comfyUIPort: number,
/** Strip user state even if saving to local storage */ /** Strip user state even if saving to local storage */
alwaysStripUserState: boolean, alwaysStripUserState: boolean,
@@ -14,18 +20,27 @@ export type ConfigState = {
} }
type ConfigStateOps = { type ConfigStateOps = {
getBackendURL: () => string
} }
export type WritableConfigStateStore = Writable<ConfigState> & ConfigStateOps; export type WritableConfigStateStore = Writable<ConfigState> & ConfigStateOps;
const store: Writable<ConfigState> = writable( const store: Writable<ConfigState> = writable(
{ {
comfyUIHostname: "localhost",
comfyUIPort: 8188,
alwaysStripUserState: false, alwaysStripUserState: false,
promptForWorkflowName: false, promptForWorkflowName: false,
confirmWhenUnloadingUnsavedChanges: true confirmWhenUnloadingUnsavedChanges: true
}) })
function getBackendURL(): string {
const state = get(store);
return `${window.location.protocol}://${state.comfyUIHostname}:${state.comfyUIPort}`
}
const configStateStore: WritableConfigStateStore = const configStateStore: WritableConfigStateStore =
{ {
...store ...store,
getBackendURL
} }
export default configStateStore; export default configStateStore;

View File

@@ -1005,6 +1005,9 @@ function createRaw(workflow: ComfyBoxWorkflow | null = null): WritableLayoutStat
store.set(state) store.set(state)
} }
/*
* NOTE: Modifies the template in-place, be sure you cloned it beforehand!
*/
function insertTemplate(template: SerializedComfyBoxTemplate, graph: LGraph, templateNodeIDToNode: Record<NodeID, LGraphNode>, container: ContainerLayout, childIndex: number): IDragItem { function insertTemplate(template: SerializedComfyBoxTemplate, graph: LGraph, templateNodeIDToNode: Record<NodeID, LGraphNode>, container: ContainerLayout, childIndex: number): IDragItem {
const idMapping: Record<DragItemID, DragItemID> = {}; const idMapping: Record<DragItemID, DragItemID> = {};

View File

@@ -7,7 +7,7 @@ export type ModalState = Record<string, any>;
export type ModalButton = { export type ModalButton = {
name: string, name: string,
variant: "primary" | "secondary", variant: "primary" | "secondary",
onClick: (state: ModalData) => void, onClick: (state: ModalData) => boolean | void,
closeOnClick?: boolean closeOnClick?: boolean
} }
export interface ModalData { export interface ModalData {

View File

@@ -11,7 +11,7 @@ export type UIState = {
uiEditMode: UIEditMode, uiEditMode: UIEditMode,
reconnecting: boolean, reconnecting: boolean,
isSavingToLocalStorage: boolean forceSaveUserState: boolean | null
} }
type UIStateOps = { type UIStateOps = {
@@ -29,7 +29,7 @@ const store: Writable<UIState> = writable(
uiEditMode: "widgets", uiEditMode: "widgets",
reconnecting: false, reconnecting: false,
isSavingToLocalStorage: false, forceSaveUserState: null,
}) })
function reconnecting() { function reconnecting() {

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
import "klecks/style/style.scss"; import "klecks/style/style.scss";
import ImageUpload from "$lib/components/ImageUpload.svelte"; import ImageUpload from "$lib/components/ImageUpload.svelte";
import { uploadImageToComfyUI, type ComfyBoxImageMetadata, comfyFileToComfyBoxMetadata, comfyBoxImageToComfyURL, comfyBoxImageToComfyFile, type ComfyUploadImageType, type ComfyImageLocation } from "$lib/utils"; import { uploadImageToComfyUI, type ComfyBoxImageMetadata, comfyFileToComfyBoxMetadata, comfyBoxImageToComfyURL, comfyBoxImageToComfyFile, type ComfyUploadImageType, type ComfyImageLocation } from "$lib/utils";
import configState from "$lib/stores/configState";
import notify from "$lib/notify"; import notify from "$lib/notify";
import NumberInput from "$lib/components/NumberInput.svelte"; import NumberInput from "$lib/components/NumberInput.svelte";
import type { ComfyImageEditorNode } from "$lib/nodes/widgets"; import type { ComfyImageEditorNode } from "$lib/nodes/widgets";
@@ -102,7 +103,7 @@
showModal = true; showModal = true;
const url = `http://${location.hostname}:8188` // TODO make configurable const url = configState.getBackendURL();
kl = new Klecks({ kl = new Klecks({
embedUrl: url, embedUrl: url,

View File

@@ -12,6 +12,7 @@
import { basicSetup } from "./TextWidgetCodeVariant"; import { basicSetup } from "./TextWidgetCodeVariant";
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import { TAG_CATEGORY_COLORS } from "$lib/DanbooruTags"; import { TAG_CATEGORY_COLORS } from "$lib/DanbooruTags";
import { Block, BlockTitle } from "@gradio/atoms";
export let widget: WidgetLayout; export let widget: WidgetLayout;
export let node: ComfyTextNode; export let node: ComfyTextNode;
@@ -176,7 +177,7 @@
function getExtensions(): Extension[] { function getExtensions(): Extension[] {
// TODO // TODO
const readonly = false; const readonly = false;
const placeholder = "Placeholder..." const placeholder = ""
const dark_mode = true; const dark_mode = true;
const stateExtensions: Extension[] = [ const stateExtensions: Extension[] = [
@@ -206,12 +207,22 @@
} }
</script> </script>
<div class="wrap"> <div class="code-editor-wrapper">
<div class="codemirror-wrapper {classNames}" bind:this={element} /> <Block>
<BlockTitle>{widget.attrs.title}</BlockTitle>
<div class="wrap">
<div class="codemirror-wrapper {classNames}" bind:this={element} />
</div>
</Block>
</div> </div>
<!-- <CodeMirror bind:value={$nodeValue} {styles} /> -->
<style lang="scss"> <style lang="scss">
.code-editor-wrapper {
:global(> .block) {
background: var(--panel-background-fill) !important;
}
}
.wrap { .wrap {
display: flex; display: flex;
flex-direction: column; flex-direction: column;