ComfyAssets controls: first production consumer
The Local Image Loader uses core.js and controls.css for the approved shared
blue controls and host-following light/dark themes. ../views/image-browser.js
composes these primitives; ../kiko_local_image_loader.js retains the controller,
API calls and existing selection persistence.
Grid thumbnails default to 224 px with square preview areas and preserve the entire image. The Thumbnail size slider adjusts the grid from 128–384 px. A full page scrolls within the gallery instead of compressing the rows to fit the node. Thumbnail size is a per-node view setting for the current session. New nodes start at 800 × 900 to leave room for the larger previews. Existing workflows retain their saved node dimensions and can be resized normally.
createScope() owns listeners, delayed work and a fetch cancellation signal.
Dispose it on node removal. mountTheme() observes host palette changes and
removes its observers/listeners with that scope. The module has no ComfyUI imports;
the extension passes the host settings interface explicitly.
Both projects will use the same theme and controls. This is the first consumer,
not a published package yet. Keep routes, node definitions and business state out
of ui/; add abstractions only as demonstrated by the next consumer.
Architecture and control organization were inspired by Pixaroma. This implementation and styling are original; none of his toolkit code or assets are vendored.
Floating windows
floating-window.js owns pointer capture, keyboard movement/resizing, viewport
bounds and observer cleanup. window.css consumes the same control tokens as the
loader. Kiko Save Image is its first consumer; the controller has no node, API or
gallery dependencies.
Drag the title or bottom-right resize handle. Focus either handle and use arrow keys (Shift for larger steps). Escape closes the focused window. The viewer stays nonmodal so the canvas remains usable. Window position and size survive new results and minimize/reopen within the session; they are not written into a workflow or persisted across browser reloads.
The owning node controls removal. Another node may take over the shared viewer, but deleting the previous owner must not remove it. Closing or deleting a viewer, or replacing its results, cancels delayed bulk-open actions.
Seed History
../views/seed-history.js composes the shared buttons and theme tokens. The
extension keeps seed generation, callbacks, history and workflow persistence.
History entries are native buttons: Tab to an entry and press Enter or Space to
reuse its seed. The selected entry has a visible outline and pressed state.
The list fills the available node space and scrolls as needed. It automatically hides after 2.5 seconds of inactivity, except while hovered or keyboard-focused. Use Show history / Hide history to control it explicitly. Feedback appears in the node's status area instead of temporary overlays on the canvas.
The existing browser storage key and serialized seedHistory field are retained.
Up to ten unique valid seeds are kept, including zero; malformed entries are
ignored. Browser storage failures leave in-memory/workflow history usable.
New nodes start at 320 × 420; saved workflows retain their node dimensions.
Node removal disposes theme observers, event listeners, polling and setup/hide
timers through createScope().
Workflow Timer and color controls
Workflow Timer uses the shared shell and theme without overriding node title or
background colors. New nodes start at 340 × 180; saved dimensions and the
elapsed_time_str property are restored with the workflow. Serialization saves
a plain snapshot instead of a reference to live node properties.
workflow-clock.js coordinates one polling interval for all visible timer
nodes. New timers join the current execution; the interval stops when the last
node is removed. Completion, interruption and errors freeze the same elapsed
time across nodes. Color/glow changes apply immediately during execution.
The existing kikotools.workflow_timer.color and .glow settings are retained.
Configured colors are preserved; the default is shared blue. Glow also respects
the system's reduced-motion preference.
createColorPicker() provides native Hue/Saturation/Brightness sliders, validated
six-digit hex input and keyboard-operable presets inside a native modal dialog.
Changes apply immediately on input; hovering does not change the saved color.
Done or Escape closes the picker and returns focus to its trigger. Disconnecting
the settings control closes the dialog and disposes its listeners/theme scope.
The setting renderer reuses its control across value updates. Its dialog lives
in the document body so those re-renders preserve focus and native slider
interaction. Cleanup waits one microtask to distinguish synchronous reparenting
from actually closing/removing the settings control.
Dimension swapping
Width Height Selector and Empty Latent Batch share dimension-swap.js and
dimensions.js. Both use the same native blue Swap W×H button with keyboard and
touch behavior. The selector no longer adds canvas drawing or mouse hit areas.
Repeated mounting reuses one control; node removal disposes its theme/listeners.
The supplied preset labels determine dimensions, including Unicode ×, ASCII
x and formatted metadata. A matching reversed preset is retained; otherwise
the node switches to custom dimensions. Custom values swap directly. Malformed
preset dimensions leave the node unchanged. Each width/height callback receives
the host context exactly once, and existing preset callback results are retained.
The control does not change node dimensions, batch size, native input order or backend interfaces. Workflow persistence remains with ComfyUI's native widgets; the DOM control does not serialize its UI state.
Node Dimensions and modal dialogs
The ComfyAssets-only Node Dimensions context menu uses dialog.js for a native
modal shell, shared theme tokens, focus restoration and scoped cleanup. Native
modality contains keyboard focus; Escape and Cancel close without applying.
../views/node-dimensions.js owns the form and node lifecycle binding.
Enter or Apply accepts positive whole pixel values and clamps them to the node's computed content minimum. Invalid values keep the form open with an associated error. Opening the same node again reuses its dialog; node removal closes it and preserves the previous removal hook's context, arguments and return value. ComfyUI continues to serialize the resulting node size.
The global KikoToolsExtensions.createDialog(htmlContent, onOK, onCancel) helper
remains available for trusted HTML templates. It returns the native dialog with
an immediate close() method. OK/Enter supplies the dialog and input/select
values; Cancel/Escape invokes cancellation once. Manual close() dismisses
without invoking cancellation. New views should build DOM through shared
factories rather than pass HTML to this compatibility helper.
Display Text and Display Any
Both nodes use ../views/text-output.js for labelled, selectable read-only text
areas and shared Copy buttons. Display Text detects paired positive/negative
prompts and offers a copy action for each. Display Any preserves the complete
formatted output in one scrollable monospace field instead of separate chunks.
The view fills the available node area; updating output preserves node size.
Copy success and failure appear in a polite status area. Replacement and removal
cancel delayed feedback and ignore pending clipboard results from the old view.
../controllers/display-node.js preserves native input/mode widgets and existing
lifecycle hook arguments, context and return values. Complete output, including
empty text, is saved in a kikoDisplay: { version: 1, text } workflow snapshot.
Legacy widget values remain readable: converted input prefixes, prompt header
pairs, native Display Any modes and all saved output chunks are recognized.
The new output DOM widget does not serialize presentation state into native
widget values. Node removal disposes the view, its listeners and theme observers.
Embedding Autocomplete
autocomplete-suggestions.js supplies the shared themed listbox, type badges,
selected state and viewport placement. The controller retains resource fetching,
matching, sorting, triggers and insertion preferences. Focus stays in the native
textarea; arrow keys select suggestions, configured Enter/Tab keys insert, and
Escape dismisses. New input clears the previous selection. Completion names are
rendered as text, including directory names, without interpreting markup.
text-widget-adapter.js binds editable Nodes 2.0 replacement textareas through
their node ID and associated label. It leaves the original widget/input intact,
cleans up disconnected replacements, and preserves Classic behavior when switching
back. Read-only and disabled fields are excluded. Each widget owns its listeners,
blur work and temporary accessibility attributes; removing a different widget
does not dismiss the active list. Page cleanup cancels pending refresh work.
../views/autocomplete-settings.js uses the shared native dialog and switches.
Only Save updates the existing kikotools.embedding_autocomplete.settings key;
Cancel/Escape discards edits. All advanced saved preferences are retained.
Storage failures keep the dialog open and restore the previous live preferences.
The settings launcher owns dialog cleanup, tolerates synchronous reparenting and
returns keyboard focus when the modal closes. The shared modal shell contains
pointer events so nested dialogs do not dismiss ComfyUI's parent settings panel.
Tag packs and autocomplete preferences
The settings view stages CSV/TXT file and URL imports until Save Settings.
autocomplete/tags.js parses quoted four-column Danbooru data and legacy
word/frequency lists, validates counts/categories, deduplicates tags and aliases,
and limits search results without sorting every matching tag on each keystroke.
autocomplete/tag-worker.js performs parsing outside the UI thread; its owner
terminates it on completion, error, or dialog removal. Records expand to the
existing suggestion shape only for returned results.
autocomplete/tag-storage.js persists the active source text in browser
IndexedDB. The controller restores matching cached sources before requesting a
URL, guards stale loads, and retains the active list when an import fails.
Advanced trigger, matching and insertion controls use the existing settings key.
For dataset provenance, format and installation, see
Tag packs.
Node removal uses one hook per node, shared by its active text widgets. Detaching widgets in any order restores the original hook after the last widget leaves. General queries accept Unicode and punctuation between whitespace/comma boundaries. Literal bracketed names are tried before interpreting leading parentheses/brackets as prompt weighting; insertion preserves surrounding weight syntax. URL downloads enforce their byte limit while reading the body, cancel oversized/aborted streams, and decode UTF-8 across chunk boundaries.
Custom Colors
../kiko_custom_colors.js opens the same shared color picker as Workflow Timer
from the host's node and group context menus. Hex entry, HSV sliders and presets
apply immediately. Full/Title/Background options, selected-node behavior, title
auto-shading and existing setting IDs are retained. Closing or replacing the
picker removes its dialog and theme observers; removing its target node also
closes it without replacing other node removal behavior. Keyboard focus returns
to the available opener, with the canvas as a fallback.