behavior WordSelectorClose()
on htmx:afterRequest from .word-selector
remove .hidden from me
end
on click
add .hidden to me
put '' into #word-selector
end
end
def clean_prompt(prompt)
js(prompt)
return prompt.replaceAll("\n", " ").replace(/,\s*,\s*/g, ',').replaceAll(" , ", ", ")
end
return it
end
behavior PromptBuilder()
on refresh
set value of #prompt to clean_prompt(my innerText)
end
on click
set target to closest .word to event.target
if target is null
exit
end
if event.ctrlKey
remove target
set value of #prompt to clean_prompt(my innerText)
trigger 'refresh' on #prompt-builder
end
end
end
behavior EditableWord()
on click from .word-lib in me
if event.ctrlKey or event.shiftKey or event.altKey or not closest #prompt-builder
exit
end
add .hidden to it
remove .hidden from .word-input in me
focus_contenteditable_text(first .word-input in me)
end
on keyup[keyCode==13] from .word-input in me
put value of it into innerText of .word-lib in me
remove .hidden from .word-lib in me
add .hidden to it
wait 0.05s then send 'refresh' to #prompt-builder
end
on keyup[keyCode==27] from .word-input in me
set value of it to innerText of .word-lib in me
remove .hidden from .word-lib in me
add .hidden to it
end
end
behavior Gallery()
on click from .result-img in me
if closest #modal
toggle .hidden on next .parameters from it
else
put my outerHTML into #modal-content
add .{'h-[90vh]'} to .result-img in .image-item in #modal-content
remove .hidden from .parameters in .image-item in #modal-content
remove .hidden from #modal
end
end
end
behavior GenerateBtn()
on htmx:beforeRequest
set :doc_title to document.title
hide me
remove .{"!hidden"} from #interrupt-btn
remove .{"opacity-0"} from #progress
repeat until event htmx:afterRequest from e
trigger 'poll' on #progress
set document.title to first of innerHTML of .progress-bar in #progress
wait 1s
end
show me
add .{"!hidden"} to #interrupt-btn
add .{"opacity-0"} to #progress
set document.title to :doc_title
fetch /instance/progress/reset then put it into #progress
end
end
def enable_row(targ)
add .selected to closest .preset-row to targ
set checked of targ to true
set preset_id to @preset-id of targ
remove .disabled from #preset-del-bulk
if not #{"delete-"+preset_id}
set input_template to in #preset-delete-template
set id of input_template to "delete-" + preset_id
set value of input_template to preset_id
put outerHTML of in #preset-delete-template at the end of #preset-delete
end
end
def disable_row(targ)
remove .selected from closest .preset-row to targ
set checked of targ to false
set targ_input to #{"delete-" + @preset-id of targ}
if targ_input
remove targ_input
end
if length of in #preset-delete <= 0
add .disabled to #preset-del-bulk
end
end
behavior RowCheck()
on change from in me
if checked of it
enable_row(it)
else
disable_row(it)
end
end
end
behavior RowCheckAll()
on change from in me
if checked of it
for row in .preset-row
if not row.classList.contains("!hidden")
enable_row( in row)
end
end
else
for row in .preset-row
disable_row( in row)
end
end
end
end
def disable_inputs(targ)
add @disabled to in targ
add @disabled to in targ
end
def enable_inputs(targ)
remove @disabled from in targ
remove @disabled from in targ
end
behavior EventInput()
on change debounced at 600ms
set post_event to closest .post-event
if @event-state of post_event is "disabled"
send 'post' to post_event
end
end
end
behavior EventClick()
on click
set post_event to closest .post-event
if @event-state of post_event is "disabled"
send 'post' to post_event
end
end
end
def push_state(url)
pushState({}, 'Exodia', url) the history of window
end
def is_scrolled_bot(e, threshold)
return (e.scrollHeight - e.clientHeight - e.scrollTop) < threshold
end
def popup(nav)
return open(nav, nav, 'scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, width=0,height=0,left=-1000,top=-1000`')
end
def show_toast()
add @toast-open to #toast
transition #toast's bottom to 20px over 100ms
end
def hide_toast()
if #toast do not match @toast-open
exit
end
transition #toast's bottom to initial over 100ms
set toast to #toast
js(toast)
toast.classList.remove(toast.classList[toast.classList.length - 1]);
end
put "" into .toast-content in #toast
add .hidden to #close-toast
remove @toast-open from #toast
end
def toast(msg, cls, autoclose)
put msg into .toast-content in #toast
add .{cls} to #toast
show_toast()
if not autoclose
remove .hidden from #close-toast
exit
end
wait 1.5s
hide_toast()
end
def clipboard(val)
if val is not a String
set val to val.innerText
end
navigator.clipboard.writeText(val)
end
def scene_link(scene_id)
return location.origin + '/scenes?scene_id=' + scene_id
end
def image_link(image_id)
return location.origin + '/image?image_id=' + image_id
end
def user_link(user_id)
return location.origin + '/users?user_id=' + user_id
end
def conv_link(conv_id)
return location.origin + '/conversations?conv_id=' + conv_id
end
def selected_opt(e)
for opt in in e
if @value of opt == e.value
return opt
end
end
def focus_text(e)
call e.focus()
set e.selectionStart to length of e's value
set e.selectionEnd to length of e's value
end
def focus_contenteditable_text(e)
call e.focus()
set range to document.createRange()
range.selectNodeContents(e)
range.collapse(false)
set sel to window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
set e.selectionStart to e.value.length
set e.selectionEnd to e.value.length
end
behavior ContentEditable()
on input
set @value of previous to my.innerText
end
on update_input
set @value of previous to my.innerText
end
end
behavior SelectWarning()
set :is_selected to false
on htmx:afterRequest from previous .input
for option in in previous .input
if option match @selected
set :is_selected to true
end
end
if :is_selected is false then remove .hidden from me
end
on mouseenter
remove .hidden from .tooltip in me
end
on mouseleave
add .hidden to .tooltip in me
end
end
behavior HoverToggle(targ, cls)
on mouseenter
remove .{cls} from targ
end
on mouseleave
add .{cls} to targ
end
end
behavior SpinOnReq()
on htmx:beforeRequest
put innerHTML of #spinner into #modal-content
add .hidden to #modal-close
remove .hidden from #modal
end
on htmx:beforeSwap or htmx:beforeSettle or htmx:afterRequest
add .hidden to #modal
remove .hidden from #modal-close
end
end
behavior ShowOnReq(targ)
on htmx:beforeRequest from targ
remove .hidden from me
end
on htmx:afterRequest from targ
add .hidden to me
end
end
behavior Modal()
on htmx:beforeRequest
put innerHTML of #spinner into #modal-content
add .hidden to #modal-close
remove .hidden from #modal
end
on htmx:afterRequest
remove .hidden from #modal-close
remove .hidden from #modal
end
end
behavior ModalContainer()
on click
if event.target matches .bck and #modal-close does not match .hidden
trigger click on #modal-close
end
end
on 'modal_open' from body
remove .hidden from me
add .overflow-hidden to body
remove .hidden from #modal-close
end
on 'modal_close' from body
add .hidden to me
remove .overflow-hidden from body
put '' into #modal-content
end
end
def hide_targets(targets, val, search_attr)
if val != ""
add .{"!hidden"} to targets
else
remove .{"!hidden"} from targets
end
for item in targets
set item_val to @search-val of item
if item_val == null
log item.getAttribute(search_attr)
set item_val to item.getAttribute(search_attr)
log item_val
end
if item_val == null
set item_val to innerText of item
end
if not item_val
continue
end
if item_val.toLowerCase() includes val.toLowerCase()
remove .{"!hidden"} from item
end
end
end
behavior ItemSearch(targ, search_attr)
on keyup or change from me
if targ == undefined
set targ to closest .selector
end
if targ == null
exit
end
hide_targets(.search-item in targ, my value, search_attr)
end
end
behavior Tooltip()
on mouseenter
put my @tooltip-text into #tooltip-text
remove .hidden from #tooltip
measure my top
measure my right
measure my left
set computed_x_offset to 0
set computed_y_offset to 0
set direction to @tooltip-direction
if not direction
set direction to "right"
end
if direction == "left"
set computed_x_offset to computed_x_offset - my *computed-width as Int - *computed-width as Int of #tooltip
set computed_y_offset to computed_y_offset + ((my *computed-height as Int / 2) as Int - (*computed-height as Int of #tooltip / 2) as Int)
end
if direction == "right"
set computed_x_offset to 0
set computed_y_offset to computed_y_offset + ((my *computed-height as Int / 2) as Int - (*computed-height as Int of #tooltip / 2) as Int)
end
if direction == "top"
set computed_x_offset to computed_x_offset - (my *computed-width as Int / 2) as Int - (*computed-width as Int of #tooltip / 2) as Int
set computed_y_offset to computed_y_offset - my *computed-height as Int
end
if direction == "bottom"
set computed_x_offset to computed_x_offset - (my *computed-width as Int / 2) as Int - (*computed-width as Int of #tooltip / 2) as Int
set computed_y_offset to computed_y_offset + my *computed-height as Int
end
add { left: ${(right as Int) + window.scrollX + computed_x_offset}px; } to #tooltip
add { top: ${(top as Int) + window.scrollY + computed_y_offset}px; } to #tooltip
end
on mouseleave
add .hidden to #tooltip
end
end
behavior FoldHandle()
on click
log me
log me.nextElementSibling
toggle .hidden on me.nextElementSibling
toggle .hidden on .fold-icon in me
end
on 'fold'
add .hidden to me.nextElementSibling
add .hidden to .fold in me
remove .hidden from .unfold in me
end
on 'unfold'
remove .hidden from me.nextElementSibling
remove .hidden from .fold in me
add .hidden to .unfold in me
end
end
behavior FocusOnReveal()
on intersection(intersecting)
wait 100ms
call focus_text(me)
end
end
behavior Login(txt2img)
on htmx:afterRequest
if event.detail.xhr.status == 200
set @src of #exodia to '/assets/images/exodia.gif'
if txt2img != "True"
wait 1.5s then go to url /dashboard
else
reload() the location of the window
end
else
toast('bad credentials', 'bg-red-500')
end
end
end
def construct_time(hours, minutes, seconds)
if hours is 0
set hours to "00"
end
if minutes is 0
set minutes to "00"
end
if seconds is 0
set seconds to "00"
end
return hours + ":" + minutes + ":" + seconds
end
behavior CountDown(hours, minutes, seconds)
init
repeat until hours < 0
if seconds > 0
decrement seconds
else
set seconds to 60
if minutes > 0
decrement minutes
else
set minutes to 60
decrement hours
end
end
put construct_time(hours, minutes, seconds) into .time in me
wait 1s
end
end
end
def is_boolean(val)
js(val)
return typeof val == 'boolean'
end
return it
end
def toggle_checkbox(targ, force)
if Array.isArray(targ)
repeat for check in targ
if is_boolean(force)
set check.checked to force
else
set check.checked to not check.checked
end
end
else
if is_boolean(force)
set targ.checked to force
else
set targ.checked to not targ.checked
end
end
end
behavior DeleteImage()
on 'image-delete' from body
set :event_value to event.detail.value
if not Array.isArray(event.detail.value)
set :event_value to [:event_value]
end
if my @img-id as Int is in :event_value
remove me
end
end
end
behavior DeleteScene()
on 'scene-delete' from body
set :event_value to event.detail.value
if not Array.isArray(event.detail.value)
set :event_value to [:event_value]
end
if my @scene-id as Int is in :event_value
remove me
end
end
end
behavior PanelHandle(targ)
on click
set :target_id to my @panel-target
set :target_panel to <.panel[panel-id="${:target_id}"]/> in targ
if not :target_panel
exit
end
add .hidden to .panel in targ
remove .hidden from :target_panel
remove .selected from .panel-handle
add .selected to me
end
end
behavior ClearOnReq(targ)
on htmx:afterRequest from targ
set my value to ""
end
end
behavior TabBar()
on click
set btn to closest to event.target
if not btn
exit
end
remove .tab-active from in me
add .tab-active to btn
set sel to the @data-tab of btn
for pane in <.tab-pane/> in #image-detail
if the @data-tab of pane is sel
remove .hidden from pane
else
add .hidden to pane
end
end
end
end
behavior SceneDragAndDropContainer()
on dragover or dragenter
halt the event
set the style.background of target to 'lightpink'
end
on dragleave or drop
set the style.background of target to ''
end
on drop get event.dataTransfer.getData('text/plain')
put innerHTML of #{it} at the end of me
hide #{it}
end
on click
if event.target match .scene
set :id to @data-id of event.target
show #{:id}
remove event.target
end
end
end
behavior RevealNav()
on mouseenter
if .nav-items in me exists
remove .hidden from .nav-items in me
end
end
on mouseleave or nav_close
if .nav-items in me exists
add .hidden to .nav-items in me
end
end
end
behavior NavButtons()
on mousedown[button==1] from .nav-target in me
set :target to (@nav-target of it) as String
go to url `/${:target}` in new window
end
end
behavior TriggerGalleryOnChange
on click
wait 0.1s
if checked of #tags_search_toggle
send 'refresh' to #tags-form
else
send 'refresh' to #text-form
end
end
end
behavior SwitchSearchPanel
on click
toggle .hidden on #text-form
toggle .hidden on #tags-form
toggle @checked on #tags_search_toggle
end
end
behavior TextSearch()
on keyup debounced at 500ms
set scrollTop of #gallery-content to 0
set value of #page to 1
if value of #text-search == ''
trigger reset on #gallery-reset
else
trigger send on me
end
end
on scroll from #gallery-content throttled at 100ms
if is_scrolled_bot(#gallery-content, 100)
if value of #search == '' and value of #text-search != ''
increment value of #page
trigger send on me
end
end
end
end
behavior CheckScene()
on click
if event.target do not match .check
toggle_checkbox(first .check in me)
toggle .{"!order-1"} on me
toggle .selected on me
if checked of first .check in me
increment innerText of #scene-selected-count
else
decrement innerText of #scene-selected-count
end
end
end
end
def batch_select_image(img)
add .opacity-50 to img
add .selected to img
for input in .image-input in #selected-images-container
if input.value == @data-id of img
return false
end
end
increment innerText of #selected-cnt
remove .disabled from #delete-images
put outerHTML of next .image-input from img at the end of #selected-images-container
end
def batch_unselect_image(img)
remove .opacity-50 from img
remove .selected from img
for input in .image-input in #selected-images-container
if input.value == @data-id of img
remove input
end
end
decrement innerText of #selected-cnt
if innerText of #selected-cnt <= 0
add .disabled to #delete-images
end
end
def ToggleBatchNav(batch_elem)
if batch_elem do not match .bg-amber-500
remove .bg-amber-500 from .batch-item
add .bg-amber-500 to batch_elem
add .hidden to .batch-details
remove .hidden from .batch-details in batch_elem
send 'send' to closest .batch-item to batch_elem
end
end
behavior BatchImageCard()
on 'refresh-delete' from body
if my @data-id is in event.detail.ids
remove me
end
end
end
behavior BatchImage()
on click
if I match .selected
batch_unselect_image(me)
else
batch_select_image(me)
end
end
end
behavior UserSearchItem()
on click
if I match .selected
remove .selected from .user-item
set value of #user_id to ''
else
remove .selected from .user-item
add .selected to me
set value of #user_id to my @data-id
set value of #search to my innerText
end
wait 0.1s
trigger 'user_picked'
end
end
def reset_rank_inputs()
add .hidden to .rank-input
for input in
set value of input to @data-value of input
end
remove .hidden from .rank-lib
remove .bg-amber-200 from .item-container
end
behavior ActivateWeightInput()
set :input to first in me
on click
reset_rank_inputs()
add .bg-amber-200 to me
remove .hidden from .rank-input in me
add .hidden to .rank-lib in me
call :input.focus()
end
on keydown from :input
if event.keyCode == 13
trigger 'enter' on closest .item-container
end
if event.keyCode == 27
reset_rank_inputs()
end
end
end