+ {#if $layoutState.attrs.queuePromptButtonName != ""}
+
+ {/if}
diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts
index 671987c..ea0948a 100644
--- a/src/lib/components/ComfyApp.ts
+++ b/src/lib/components/ComfyApp.ts
@@ -28,7 +28,7 @@ import ComfyGraph from "$lib/ComfyGraph";
import { ComfyBackendNode } from "$lib/nodes/ComfyBackendNode";
import { get } from "svelte/store";
import uiState from "$lib/stores/uiState";
-import { promptToGraphVis, workflowToGraphVis } from "$lib/utils";
+import { download, promptToGraphVis, workflowToGraphVis } from "$lib/utils";
import notify from "$lib/notify";
export const COMFYBOX_SERIAL_VERSION = 1;
@@ -151,6 +151,7 @@ export default class ComfyApp {
}
} catch (err) {
console.error("Error loading previous workflow", err);
+ notify(`Error loading previous workflow:\n${err}`, { type: "error", timeout: null })
}
// We failed to restore a workflow so load the default
@@ -172,6 +173,8 @@ export default class ComfyApp {
this.resizeCanvas();
window.addEventListener("resize", this.resizeCanvas.bind(this));
+ this.requestPermissions();
+
this.alreadySetup = true;
return Promise.resolve();
@@ -348,6 +351,13 @@ export default class ComfyApp {
});
}
+ private requestPermissions() {
+ if (Notification.permission === "default") {
+ Notification.requestPermission()
+ .then((result) => console.log("Notification status:", result));
+ }
+ }
+
private setupColorScheme() {
const setColor = (type: any, color: string) => {
LGraphCanvas.DEFAULT_LINK_TYPE_COLORS[type] = color
@@ -454,6 +464,37 @@ export default class ComfyApp {
layoutState.initDefaultLayout();
}
+ runDefaultQueueAction() {
+ for (const node of this.lGraph.iterateNodesInOrder()) {
+ if ("onDefaultQueueAction" in node) {
+ (node as ComfyGraphNode).onDefaultQueueAction()
+ }
+ }
+ }
+
+ querySave() {
+ const promptFilename = true; // TODO
+
+ let filename = "workflow.json";
+ if (promptFilename) {
+ filename = prompt("Save workflow as:", filename);
+ if (!filename) return;
+ if (!filename.toLowerCase().endsWith(".json")) {
+ filename += ".json";
+ }
+ }
+ else {
+ const date = new Date();
+ const formattedDate = date.toISOString().replace(/:/g, '-').replace(/\.\d{3}/g, '').replace('T', '_').replace("Z", "");
+ filename = `workflow-${formattedDate}.json`
+ }
+
+ const indent = 2
+ const json = JSON.stringify(this.serialize(), null, indent)
+
+ download(filename, json, "application/json")
+ }
+
/**
* Converts the current graph workflow for sending to the API
* @returns The workflow and node links
@@ -644,7 +685,7 @@ export default class ComfyApp {
} catch (error) {
// this.ui.dialog.show(error.response || error.toString());
const mes = error.response || error.toString()
- notify(`Error queuing prompt:\n${mes}`, null, "error")
+ notify(`Error queuing prompt:\n${mes}`, { type: "error" })
console.error(promptToGraphVis(p))
console.error("Error queuing prompt", mes, num, p)
break;
@@ -682,7 +723,7 @@ export default class ComfyApp {
}
else {
console.error("No metadata found in image file.", pngInfo)
- notify("No metadata found in image file.")
+ notify("No metadata found in image file.", { type: "error" })
}
}
} else if (file.type === "application/json" || file.name.endsWith(".json")) {
@@ -727,7 +768,7 @@ export default class ComfyApp {
if (inputNode && "doAutoConfig" in inputNode && comfyInput.widgetNodeType === inputNode.type) {
console.debug("[ComfyApp] Reconfiguring combo widget", inputNode.type, comfyInput.config.values)
const comfyComboNode = inputNode as nodes.ComfyComboNode;
- comfyComboNode.doAutoConfig(comfyInput)
+ comfyComboNode.doAutoConfig(comfyInput, { includeProperties: new Set(["values"]), setWidgetTitle: false })
if (!comfyInput.config.values.includes(get(comfyComboNode.value))) {
comfyComboNode.setValue(comfyInput.config.defaultValue || comfyInput.config.values[0])
}
diff --git a/src/lib/nodes/ComfyActionNodes.ts b/src/lib/nodes/ComfyActionNodes.ts
index a71bd1b..bc47b30 100644
--- a/src/lib/nodes/ComfyActionNodes.ts
+++ b/src/lib/nodes/ComfyActionNodes.ts
@@ -6,24 +6,27 @@ import { BuiltInSlotType, LiteGraph, NodeMode, type ITextWidget, type IToggleWid
import { get } from "svelte/store";
import ComfyGraphNode, { type ComfyGraphNodeProperties } from "./ComfyGraphNode";
import type { ComfyWidgetNode, GalleryOutput } from "./ComfyWidgetNodes";
+import type { NotifyOptions } from "$lib/notify";
+import { convertComfyOutputToGradio } from "$lib/utils";
export class ComfyQueueEvents extends ComfyGraphNode {
static slotLayout: SlotLayout = {
outputs: [
{ name: "beforeQueued", type: BuiltInSlotType.EVENT },
- { name: "afterQueued", type: BuiltInSlotType.EVENT }
+ { name: "afterQueued", type: BuiltInSlotType.EVENT },
+ { name: "onDefaultQueueAction", type: BuiltInSlotType.EVENT },
],
}
private getActionParams(subgraph: string | null): any {
let queue = get(queueState)
- let remaining = 0;
+ let queueRemaining = 0;
if (typeof queue.queueRemaining === "number")
- remaining = queue.queueRemaining
+ queueRemaining = queue.queueRemaining
return {
- queueRemaining: remaining,
+ queueRemaining,
subgraph
}
}
@@ -36,6 +39,16 @@ export class ComfyQueueEvents extends ComfyGraphNode {
this.triggerSlot(1, this.getActionParams(subgraph))
}
+ override onDefaultQueueAction() {
+ let queue = get(queueState)
+ let queueRemaining = 0;
+
+ if (typeof queue.queueRemaining === "number")
+ queueRemaining = queue.queueRemaining
+
+ this.triggerSlot(2, { queueRemaining })
+ }
+
override onSerialize(o: SerializedLGraphNode) {
super.onSerialize(o)
}
@@ -54,6 +67,7 @@ export interface ComfyStoreImagesActionProperties extends ComfyGraphNodeProperti
export class ComfyStoreImagesAction extends ComfyGraphNode {
override properties: ComfyStoreImagesActionProperties = {
+ tags: [],
images: null
}
@@ -175,13 +189,15 @@ LiteGraph.registerNodeType({
})
export interface ComfyNotifyActionProperties extends ComfyGraphNodeProperties {
- message: string
+ message: string,
+ type: string
}
export class ComfyNotifyAction extends ComfyGraphNode {
override properties: ComfyNotifyActionProperties = {
+ tags: [],
message: "Nya.",
- tags: []
+ type: "info"
}
static slotLayout: SlotLayout = {
@@ -192,10 +208,27 @@ export class ComfyNotifyAction extends ComfyGraphNode {
}
override onAction(action: any, param: any) {
- const message = this.getInputData(0);
- if (message) {
- notify(message);
+ const message = this.getInputData(0) || this.properties.message;
+ if (!message)
+ return;
+
+ const options: NotifyOptions = {
+ type: this.properties.type
}
+
+ // Check if this event was triggered from a backend node and has the
+ // onExecuted arguments. If so then use the first image as the icon for
+ // native notifications.
+ if (param != null && typeof param === "object") {
+ if ("images" in param) {
+ const output = param as GalleryOutput;
+ const converted = convertComfyOutputToGradio(output);
+ if (converted.length > 0)
+ options.imageUrl = converted[0].data;
+ }
+ }
+
+ notify(message, options);
};
}
@@ -206,6 +239,40 @@ LiteGraph.registerNodeType({
type: "actions/notify"
})
+export interface ComfyPlaySoundActionProperties extends ComfyGraphNodeProperties {
+ sound: string,
+}
+
+export class ComfyPlaySoundAction extends ComfyGraphNode {
+ override properties: ComfyPlaySoundActionProperties = {
+ tags: [],
+ sound: "notification.mp3"
+ }
+
+ static slotLayout: SlotLayout = {
+ inputs: [
+ { name: "sound", type: "string" },
+ { name: "trigger", type: BuiltInSlotType.ACTION }
+ ],
+ }
+
+ override onAction(action: any, param: any) {
+ const sound = this.getInputData(0) || this.properties.sound;
+ if (sound) {
+ const url = `${location.origin}/sound/${sound}`;
+ const audio = new Audio(url);
+ audio.play();
+ }
+ };
+}
+
+LiteGraph.registerNodeType({
+ class: ComfyPlaySoundAction,
+ title: "Comfy.PlaySoundAction",
+ desc: "Plays a sound located under the sound/ directory.",
+ type: "actions/play_sound"
+})
+
export interface ComfyExecuteSubgraphActionProperties extends ComfyGraphNodeProperties {
targetTag: string
}
diff --git a/src/lib/nodes/ComfyConfigureQueuePromptButton.ts b/src/lib/nodes/ComfyConfigureQueuePromptButton.ts
new file mode 100644
index 0000000..d465ecd
--- /dev/null
+++ b/src/lib/nodes/ComfyConfigureQueuePromptButton.ts
@@ -0,0 +1,47 @@
+import layoutState from "$lib/stores/layoutState"
+import { BuiltInSlotType, LGraphNode, LiteGraph, type ITextWidget, type OptionalSlots, type PropertyLayout, type SlotLayout, type Vector2 } from "@litegraph-ts/core"
+
+export interface ComfyConfigureQueuePromptButtonProperties extends Record
{
+}
+
+export default class ComfyConfigureQueuePromptButton extends LGraphNode {
+ override properties: ComfyConfigureQueuePromptButtonProperties = {
+ }
+
+ static slotLayout: SlotLayout = {
+ inputs: [
+ { name: "config", type: BuiltInSlotType.ACTION },
+ ],
+ }
+
+ static propertyLayout: PropertyLayout = [
+ ]
+
+ static optionalSlots: OptionalSlots = {
+ }
+
+ override size: Vector2 = [60, 30];
+
+ constructor(title?: string) {
+ super(title)
+ }
+
+ override onAction(action: any, param: any, options: { action_call?: string }) {
+ if (action === "config" && param != null) {
+ layoutState.update(state => {
+ if (typeof param === "string")
+ state.attrs.queuePromptButtonName = param || ""
+ else if (typeof param === "object" && "buttonName" in param)
+ state.attrs.queuePromptButtonName = param.buttonName || ""
+ return state
+ })
+ }
+ }
+}
+
+LiteGraph.registerNodeType({
+ class: ComfyConfigureQueuePromptButton,
+ title: "Comfy.ConfigureQueuePromptButton",
+ desc: "Sets the properties of the global queue prompt button",
+ type: "workflow/configure_queue_prompt_button"
+})
diff --git a/src/lib/nodes/ComfyGraphNode.ts b/src/lib/nodes/ComfyGraphNode.ts
index 5ece8af..59cbda3 100644
--- a/src/lib/nodes/ComfyGraphNode.ts
+++ b/src/lib/nodes/ComfyGraphNode.ts
@@ -28,10 +28,36 @@ export default class ComfyGraphNode extends LGraphNode {
isBackendNode?: boolean;
+ /*
+ * Triggered when the user presses the global "Queue Prompt" button in the fixed toolbar.
+ */
+ onDefaultQueueAction?(): void;
+
+ /*
+ * Triggered before a prompt containing this node is passed to the backend.
+ */
beforeQueued?(subgraph: string | null): void;
+
+ /*
+ * Triggered after a prompt containing this node is passed to the backend.
+ */
afterQueued?(prompt: SerializedPrompt, subgraph: string | null): void;
+
+ /*
+ * Triggered when the backend sends a finished output back with this node's ID.
+ * Valid for output nodes like SaveImage and PreviewImage.
+ */
onExecuted?(output: any): void;
+ /*
+ * Allows you to manually specify an auto-config for certain input slot
+ * indices, so that when a ComfyWidgetNode is connected to the input slot it
+ * receives the specified min/max/values/etc.
+ * Otherwise the config passed from the backend is used.
+ *
+ * Use this if you're creating a frontend-only node and want some input
+ * slots to have auto-configs, like for connected combo box widgets.
+ */
defaultWidgets?: DefaultWidgetLayout
/*
@@ -71,6 +97,12 @@ export default class ComfyGraphNode extends LGraphNode {
this.addProperty("tags", [], "array")
}
+ /*
+ * Adjusts output slot types to have the same type as the first connected
+ * input. Used for frontend-only nodes with inputs and outputs that act as
+ * wildcards, so that they can be connected to ComfyBackendNodes without
+ * rejection.
+ */
private inheritSlotTypes(type: LConnectionKind, isConnected: boolean) {
// Prevent multiple connections to different types when we have no input
if (isConnected && type === LConnectionKind.OUTPUT) {
@@ -229,6 +261,7 @@ export default class ComfyGraphNode extends LGraphNode {
}
override onResize(size: Vector2) {
+ // Snap to grid if shift is held down.
if ((window as any)?.app?.shiftDown) {
const w = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.size[0] / LiteGraph.CANVAS_GRID_SIZE);
const h = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.size[1] / LiteGraph.CANVAS_GRID_SIZE);
@@ -241,6 +274,8 @@ export default class ComfyGraphNode extends LGraphNode {
}
override onSerialize(o: SerializedLGraphNode) {
+ // Resync the widget node types for each input.
+ // This is so combo widget nodes will be correctly detected by ComfyApp.refreshComboInNodes().
for (let index = 0; index < this.inputs.length; index++) {
const input = this.inputs[index]
const serInput = o.inputs[index]
@@ -254,6 +289,7 @@ export default class ComfyGraphNode extends LGraphNode {
(serInput as any).defaultWidgetNode = null
}
}
+
(o as any).saveUserState = this.saveUserState
if (!this.saveUserState) {
this.stripUserState(o)
@@ -262,6 +298,7 @@ export default class ComfyGraphNode extends LGraphNode {
}
override onConfigure(o: SerializedLGraphNode) {
+ // Save the litegraph type of the default ComfyWidgetNode for each input.
for (let index = 0; index < this.inputs.length; index++) {
const input = this.inputs[index]
const serInput = o.inputs[index]
diff --git a/src/lib/nodes/ComfyWidgetNodes.ts b/src/lib/nodes/ComfyWidgetNodes.ts
index 24d93c2..b5545dc 100644
--- a/src/lib/nodes/ComfyWidgetNodes.ts
+++ b/src/lib/nodes/ComfyWidgetNodes.ts
@@ -18,6 +18,12 @@ import CheckboxWidget from "$lib/widgets/CheckboxWidget.svelte";
import RadioWidget from "$lib/widgets/RadioWidget.svelte";
import ImageUploadWidget from "$lib/widgets/ImageUploadWidget.svelte";
+export type AutoConfigOptions = {
+ includeProperties?: Set | null,
+ setDefaultValue?: boolean
+ setWidgetTitle?: boolean
+}
+
/*
* NOTE: If you want to add a new widget but it has the same input/output type
* as another one of the existing widgets, best to create a new "variant" of
@@ -165,7 +171,9 @@ export abstract class ComfyWidgetNode extends ComfyGraphNode {
setValue(value: any, noChangedEvent: boolean = false) {
if (noChangedEvent)
this._noChangedEvent = true;
- this.value.set(this.parseValue(value))
+
+ const parsed = this.parseValue(value)
+ this.value.set(parsed)
// In case value.set() does not trigger onValueUpdated, we need to reset
// the counter here also.
@@ -222,8 +230,6 @@ export abstract class ComfyWidgetNode extends ComfyGraphNode {
if ("noChangedEvent" in param)
noChangedEvent = Boolean(param.noChangedEvent)
}
- value = this.parseValue(value);
- console.warn("[Widget] Store!", param, "=>", value, noChangedEvent)
this.setValue(value, noChangedEvent)
}
}
@@ -244,18 +250,24 @@ export abstract class ComfyWidgetNode extends ComfyGraphNode {
return true;
}
- doAutoConfig(input: IComfyInputSlot) {
+ doAutoConfig(input: IComfyInputSlot, options: AutoConfigOptions = { setDefaultValue: true, setWidgetTitle: true }) {
// Copy properties from default config in input slot
const comfyInput = input as IComfyInputSlot;
- for (const key in comfyInput.config)
- this.setProperty(key, comfyInput.config[key])
+ for (const key in comfyInput.config) {
+ if (options.includeProperties == null || options.includeProperties.has(key))
+ this.setProperty(key, comfyInput.config[key])
+ }
- if ("defaultValue" in this.properties)
- this.setValue(this.properties.defaultValue)
+ if (options.setDefaultValue) {
+ if ("defaultValue" in this.properties)
+ this.setValue(this.properties.defaultValue)
+ }
- const widget = layoutState.findLayoutForNode(this.id)
- if (widget && input.name !== "") {
- widget.attrs.title = input.name;
+ if (options.setWidgetTitle) {
+ const widget = layoutState.findLayoutForNode(this.id)
+ if (widget && input.name !== "") {
+ widget.attrs.title = input.name;
+ }
}
console.debug("Property copy", input, this.properties)
diff --git a/src/lib/nodes/index.ts b/src/lib/nodes/index.ts
index edbebcc..1ccbfd0 100644
--- a/src/lib/nodes/index.ts
+++ b/src/lib/nodes/index.ts
@@ -1,8 +1,19 @@
export { default as ComfyReroute } from "./ComfyReroute"
export { ComfyWidgetNode, ComfySliderNode, ComfyComboNode, ComfyTextNode } from "./ComfyWidgetNodes"
-export { ComfyQueueEvents, ComfyCopyAction, ComfySwapAction, ComfyNotifyAction, ComfyStoreImagesAction, ComfyExecuteSubgraphAction } from "./ComfyActionNodes"
+export {
+ ComfyQueueEvents,
+ ComfyCopyAction,
+ ComfySwapAction,
+ ComfyNotifyAction,
+ ComfyPlaySoundAction,
+ ComfyStoreImagesAction,
+ ComfyExecuteSubgraphAction,
+ ComfySetNodeModeAction,
+ ComfySetNodeModeAdvancedAction
+} from "./ComfyActionNodes"
export { default as ComfyPickFirstNode } from "./ComfyPickFirstNode"
export { default as ComfyValueControl } from "./ComfyValueControl"
export { default as ComfySelector } from "./ComfySelector"
export { default as ComfyImageCacheNode } from "./ComfyImageCacheNode"
export { default as ComfyTriggerNewEventNode } from "./ComfyTriggerNewEventNode"
+export { default as ComfyConfigureQueuePromptButton } from "./ComfyConfigureQueuePromptButton"
diff --git a/src/lib/notify.ts b/src/lib/notify.ts
index 290434a..97fc382 100644
--- a/src/lib/notify.ts
+++ b/src/lib/notify.ts
@@ -1,40 +1,82 @@
import { toast } from "@zerodevx/svelte-toast";
import type { SvelteToastOptions } from "@zerodevx/svelte-toast/stores";
+import { type Notification } from "framework7/components/notification"
import { f7 } from "framework7-svelte"
-let notification;
+export type NotifyOptions = {
+ title?: string,
+ type?: "neutral" | "info" | "warning" | "error" | "success",
+ imageUrl?: string,
+ timeout?: number | null
+}
-function notifyf7(text: string, title?: string, type?: string) {
+function notifyf7(text: string, options: NotifyOptions) {
if (!f7)
return;
- if (!notification) {
- notification = f7.notification.create({
- title: title,
- titleRightText: 'now',
- // subtitle: 'Notification with close on click',
- text: text,
- closeOnClick: true,
- closeTimeout: 3000,
- });
- }
- // Open it
+ let closeTimeout = options.timeout
+ if (closeTimeout === undefined)
+ closeTimeout = 3000;
+
+ const notification = f7.notification.create({
+ title: options.title,
+ titleRightText: 'now',
+ // subtitle: 'Notification with close on click',
+ text: text,
+ closeOnClick: true,
+ closeTimeout
+ });
notification.open();
}
-function notifyToast(text: string, title?: string, type?: string) {
- const options: SvelteToastOptions = {}
+function notifyToast(text: string, options: NotifyOptions) {
+ const toastOptions: SvelteToastOptions = {
+ dismissable: options.timeout !== null,
+ }
- if (type === "error") {
- options.theme = {
+ if (options.type === "success") {
+ toastOptions.theme = {
+ '--toastBackground': 'var(--color-green-600)',
+ }
+ }
+ else if (options.type === "info") {
+ toastOptions.theme = {
+ '--toastBackground': 'var(--color-blue-500)',
+ }
+ }
+ else if (options.type === "error") {
+ toastOptions.theme = {
'--toastBackground': 'var(--color-red-500)',
}
}
- toast.push(text, options);
+ toast.push(text, toastOptions);
}
-export default function notify(text: string, title?: string, type?: string) {
- notifyf7(text, title, type);
- notifyToast(text, title, type);
+function notifyNative(text: string, options: NotifyOptions) {
+ if (document.hasFocus())
+ return;
+
+ const title = options.title || "ComfyBox"
+ const nativeOptions: NotificationOptions = {
+ body: text,
+ }
+
+ if (options.imageUrl) {
+ nativeOptions.icon = options.imageUrl
+ nativeOptions.image = options.imageUrl
+ }
+ if (options.timeout === null) {
+ nativeOptions.requireInteraction = true;
+ }
+
+ const notification = new Notification(title, nativeOptions);
+
+ notification.onclick = () => window.focus();
+}
+
+export default function notify(text: string, options: NotifyOptions = {}) {
+ notifyf7(text, options);
+ notifyToast(text, options);
+ notifyNative(text, options)
}
diff --git a/src/lib/stores/layoutState.ts b/src/lib/stores/layoutState.ts
index 4fd94c2..fae9cdd 100644
--- a/src/lib/stores/layoutState.ts
+++ b/src/lib/stores/layoutState.ts
@@ -28,13 +28,9 @@ type DragItemEntry = {
*/
export type LayoutAttributes = {
/*
- * Default subgraph to run when the "Queue Prompt" button in the bottom bar
- * is pressed.
- *
- * If it's an empty string, all backend nodes will be included in the prompt
- * instead.
+ * Name of the "Queue Prompt" button. Set to blank to hide the button.
*/
- defaultSubgraph: string
+ queuePromptButtonName: string,
}
/*
@@ -522,11 +518,11 @@ const ALL_ATTRIBUTES: AttributesSpecList = [
// Workflow
{
- name: "defaultSubgraph",
+ name: "queuePromptButtonName",
type: "string",
location: "workflow",
editable: true,
- defaultValue: ""
+ defaultValue: "Queue Prompt"
}
]
}
@@ -544,10 +540,16 @@ for (const cat of Object.values(ALL_ATTRIBUTES)) {
export { ALL_ATTRIBUTES };
const defaultWidgetAttributes: Attributes = {} as any
+const defaultWorkflowAttributes: LayoutAttributes = {} as any
for (const cat of Object.values(ALL_ATTRIBUTES)) {
for (const spec of Object.values(cat.specs)) {
- if (spec.location === "widget" && spec.defaultValue != null) {
- defaultWidgetAttributes[spec.name] = spec.defaultValue;
+ if (spec.defaultValue != null) {
+ if (spec.location === "widget") {
+ defaultWidgetAttributes[spec.name] = spec.defaultValue;
+ }
+ else if (spec.location === "workflow") {
+ defaultWorkflowAttributes[spec.name] = spec.defaultValue;
+ }
}
}
}
@@ -634,7 +636,7 @@ const store: Writable = writable({
isMenuOpen: false,
isConfiguring: true,
attrs: {
- defaultSubgraph: ""
+ ...defaultWorkflowAttributes
}
})
@@ -890,7 +892,7 @@ function initDefaultLayout() {
isMenuOpen: false,
isConfiguring: false,
attrs: {
- defaultSubgraph: ""
+ ...defaultWorkflowAttributes
}
})
@@ -1004,7 +1006,7 @@ function deserialize(data: SerializedLayoutState, graph: LGraph) {
currentSelectionNodes: [],
isMenuOpen: false,
isConfiguring: false,
- attrs: data.attrs
+ attrs: { ...defaultWorkflowAttributes, ...data.attrs }
}
console.debug("[layoutState] deserialize", data, state)
diff --git a/src/lib/widgets/CheckboxWidget.svelte b/src/lib/widgets/CheckboxWidget.svelte
index 503ffae..1b84c5b 100644
--- a/src/lib/widgets/CheckboxWidget.svelte
+++ b/src/lib/widgets/CheckboxWidget.svelte
@@ -54,11 +54,11 @@
> .inner {
padding: 2px;
width: 100%;
+ height: 100%;
display: flex;
flex-direction: row;
- height: min-content;
- :global(> label) {
+ :global(> .block > label) {
height: 100%;
}
}
diff --git a/src/lib/widgets/GalleryWidget.svelte b/src/lib/widgets/GalleryWidget.svelte
index ae167b5..32e646b 100644
--- a/src/lib/widgets/GalleryWidget.svelte
+++ b/src/lib/widgets/GalleryWidget.svelte
@@ -84,6 +84,7 @@
}
})
+ history.pushState({ type: "gallery" }, "");
mobileLightbox = f7.photoBrowser.create({
photos: images,
@@ -152,7 +153,7 @@
$: node.anyImageSelected = selected_image != null;
-{#if widget && node && nodeValue}
+{#if widget && node && nodeValue && $nodeValue}
{#if widget.attrs.variant === "image"}