mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat(lora-randomizer): add segmented scale mode to strength sliders
- Add `scaleMode` and `segments` props to DualRangeSlider component - Implement segmented scale visualization with configurable segment widths - Define strength segments for model and clip strength sliders with expanded middle range - Enable finer control in common value ranges via wheel step multipliers
This commit is contained in:
@@ -65,6 +65,8 @@
|
|||||||
:value-max="modelStrengthMax"
|
:value-max="modelStrengthMax"
|
||||||
:step="0.1"
|
:step="0.1"
|
||||||
:default-range="{ min: -2, max: 3 }"
|
:default-range="{ min: -2, max: 3 }"
|
||||||
|
:scale-mode="'segmented'"
|
||||||
|
:segments="strengthSegments"
|
||||||
@update:value-min="$emit('update:modelStrengthMin', $event)"
|
@update:value-min="$emit('update:modelStrengthMin', $event)"
|
||||||
@update:value-max="$emit('update:modelStrengthMax', $event)"
|
@update:value-max="$emit('update:modelStrengthMax', $event)"
|
||||||
/>
|
/>
|
||||||
@@ -98,6 +100,8 @@
|
|||||||
:value-max="clipStrengthMax"
|
:value-max="clipStrengthMax"
|
||||||
:step="0.1"
|
:step="0.1"
|
||||||
:default-range="{ min: -1, max: 2 }"
|
:default-range="{ min: -1, max: 2 }"
|
||||||
|
:scale-mode="'segmented'"
|
||||||
|
:segments="strengthSegments"
|
||||||
:disabled="isClipStrengthDisabled"
|
:disabled="isClipStrengthDisabled"
|
||||||
@update:value-min="$emit('update:clipStrengthMin', $event)"
|
@update:value-min="$emit('update:clipStrengthMin', $event)"
|
||||||
@update:value-max="$emit('update:clipStrengthMax', $event)"
|
@update:value-max="$emit('update:clipStrengthMax', $event)"
|
||||||
@@ -174,6 +178,12 @@ import SingleSlider from '../shared/SingleSlider.vue'
|
|||||||
import DualRangeSlider from '../shared/DualRangeSlider.vue'
|
import DualRangeSlider from '../shared/DualRangeSlider.vue'
|
||||||
import type { LoraEntry } from '../../composables/types'
|
import type { LoraEntry } from '../../composables/types'
|
||||||
|
|
||||||
|
const strengthSegments = [
|
||||||
|
{ min: -10, max: -2, widthPercent: 20 },
|
||||||
|
{ min: -2, max: 2, widthPercent: 60, wheelStepMultiplier: 0.5 },
|
||||||
|
{ min: 2, max: 10, widthPercent: 20 }
|
||||||
|
]
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
countMode: 'fixed' | 'range'
|
countMode: 'fixed' | 'range'
|
||||||
countFixed: number
|
countFixed: number
|
||||||
|
|||||||
@@ -1,8 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dual-range-slider" :class="{ disabled }" @wheel="onWheel">
|
<div class="dual-range-slider" :class="{ disabled, 'has-segments': scaleMode === 'segmented' && effectiveSegments.length > 0 }" @wheel="onWheel">
|
||||||
<div class="slider-track" ref="trackEl">
|
<div class="slider-track" ref="trackEl">
|
||||||
<!-- Background track -->
|
<!-- Background track -->
|
||||||
<div class="slider-track__bg"></div>
|
<div class="slider-track__bg"></div>
|
||||||
|
|
||||||
|
<!-- Segment backgrounds for segmented scale mode -->
|
||||||
|
<template v-if="scaleMode === 'segmented' && effectiveSegments.length > 0">
|
||||||
|
<div
|
||||||
|
v-for="(seg, index) in effectiveSegments"
|
||||||
|
:key="'segment-' + index"
|
||||||
|
class="slider-track__segment"
|
||||||
|
:class="{
|
||||||
|
'slider-track__segment--common': seg.wheelStepMultiplier && seg.wheelStepMultiplier < 1,
|
||||||
|
'slider-track__segment--expanded': seg.wheelStepMultiplier && seg.wheelStepMultiplier < 1
|
||||||
|
}"
|
||||||
|
:style="getSegmentStyle(seg, index)"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Active track (colored range between handles) -->
|
<!-- Active track (colored range between handles) -->
|
||||||
<div
|
<div
|
||||||
class="slider-track__active"
|
class="slider-track__active"
|
||||||
@@ -45,6 +60,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onUnmounted } from 'vue'
|
import { ref, computed, onUnmounted } from 'vue'
|
||||||
|
|
||||||
|
type ScaleMode = 'linear' | 'segmented'
|
||||||
|
|
||||||
|
interface Segment {
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
widthPercent: number
|
||||||
|
wheelStepMultiplier?: number
|
||||||
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
min: number
|
min: number
|
||||||
max: number
|
max: number
|
||||||
@@ -53,8 +77,12 @@ const props = withDefaults(defineProps<{
|
|||||||
step: number
|
step: number
|
||||||
defaultRange?: { min: number; max: number }
|
defaultRange?: { min: number; max: number }
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
scaleMode?: ScaleMode
|
||||||
|
segments?: Segment[]
|
||||||
}>(), {
|
}>(), {
|
||||||
disabled: false
|
disabled: false,
|
||||||
|
scaleMode: 'linear',
|
||||||
|
segments: () => []
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -65,12 +93,25 @@ const emit = defineEmits<{
|
|||||||
const trackEl = ref<HTMLElement | null>(null)
|
const trackEl = ref<HTMLElement | null>(null)
|
||||||
const dragging = ref<'min' | 'max' | null>(null)
|
const dragging = ref<'min' | 'max' | null>(null)
|
||||||
|
|
||||||
|
const effectiveSegments = computed<Segment[]>(() => {
|
||||||
|
if (props.scaleMode === 'segmented' && props.segments.length > 0) {
|
||||||
|
return props.segments
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
|
||||||
const minPercent = computed(() => {
|
const minPercent = computed(() => {
|
||||||
|
if (props.scaleMode === 'segmented' && effectiveSegments.value.length > 0) {
|
||||||
|
return valueToPercent(props.valueMin)
|
||||||
|
}
|
||||||
const range = props.max - props.min
|
const range = props.max - props.min
|
||||||
return ((props.valueMin - props.min) / range) * 100
|
return ((props.valueMin - props.min) / range) * 100
|
||||||
})
|
})
|
||||||
|
|
||||||
const maxPercent = computed(() => {
|
const maxPercent = computed(() => {
|
||||||
|
if (props.scaleMode === 'segmented' && effectiveSegments.value.length > 0) {
|
||||||
|
return valueToPercent(props.valueMax)
|
||||||
|
}
|
||||||
const range = props.max - props.min
|
const range = props.max - props.min
|
||||||
return ((props.valueMax - props.min) / range) * 100
|
return ((props.valueMax - props.min) / range) * 100
|
||||||
})
|
})
|
||||||
@@ -83,10 +124,67 @@ const defaultMinPercent = computed(() => {
|
|||||||
|
|
||||||
const defaultMaxPercent = computed(() => {
|
const defaultMaxPercent = computed(() => {
|
||||||
if (!props.defaultRange) return 100
|
if (!props.defaultRange) return 100
|
||||||
|
if (props.scaleMode === 'segmented' && effectiveSegments.value.length > 0) {
|
||||||
|
return valueToPercent(props.defaultRange.max)
|
||||||
|
}
|
||||||
const range = props.max - props.min
|
const range = props.max - props.min
|
||||||
return ((props.defaultRange.max - props.min) / range) * 100
|
return ((props.defaultRange.max - props.min) / range) * 100
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const valueToPercent = (value: number): number => {
|
||||||
|
const segments = effectiveSegments.value
|
||||||
|
if (segments.length === 0) {
|
||||||
|
const range = props.max - props.min
|
||||||
|
return ((value - props.min) / range) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
let accumulatedPercent = 0
|
||||||
|
for (const seg of segments) {
|
||||||
|
if (value >= seg.max) {
|
||||||
|
accumulatedPercent += seg.widthPercent
|
||||||
|
} else if (value >= seg.min) {
|
||||||
|
const segRange = seg.max - seg.min
|
||||||
|
const valueInSeg = value - seg.min
|
||||||
|
accumulatedPercent += (valueInSeg / segRange) * seg.widthPercent
|
||||||
|
return accumulatedPercent
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accumulatedPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
const percentToValue = (percent: number): number => {
|
||||||
|
const segments = effectiveSegments.value
|
||||||
|
if (segments.length === 0) {
|
||||||
|
const range = props.max - props.min
|
||||||
|
return props.min + (percent / 100) * range
|
||||||
|
}
|
||||||
|
|
||||||
|
let accumulatedPercent = 0
|
||||||
|
for (const seg of segments) {
|
||||||
|
const segEndPercent = accumulatedPercent + seg.widthPercent
|
||||||
|
if (percent <= segEndPercent) {
|
||||||
|
const segRange = seg.max - seg.min
|
||||||
|
const percentInSeg = (percent - accumulatedPercent) / seg.widthPercent
|
||||||
|
return seg.min + percentInSeg * segRange
|
||||||
|
}
|
||||||
|
accumulatedPercent = segEndPercent
|
||||||
|
}
|
||||||
|
return props.max
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSegmentStyle = (seg: Segment, index: number) => {
|
||||||
|
let leftPercent = 0
|
||||||
|
for (let i = 0; i < index; i++) {
|
||||||
|
leftPercent += effectiveSegments.value[i].widthPercent
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
left: leftPercent + '%',
|
||||||
|
width: seg.widthPercent + '%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const formatValue = (val: number): string => {
|
const formatValue = (val: number): string => {
|
||||||
if (Number.isInteger(val)) return val.toString()
|
if (Number.isInteger(val)) return val.toString()
|
||||||
return val.toFixed(stepToDecimals(props.step))
|
return val.toFixed(stepToDecimals(props.step))
|
||||||
@@ -98,9 +196,10 @@ const stepToDecimals = (step: number): number => {
|
|||||||
return decimalIndex === -1 ? 0 : str.length - decimalIndex - 1
|
return decimalIndex === -1 ? 0 : str.length - decimalIndex - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapToStep = (value: number): number => {
|
const snapToStep = (value: number, segmentMultiplier?: number): number => {
|
||||||
const steps = Math.round((value - props.min) / props.step)
|
const effectiveStep = segmentMultiplier ? props.step * segmentMultiplier : props.step
|
||||||
return Math.max(props.min, Math.min(props.max, props.min + steps * props.step))
|
const steps = Math.round((value - props.min) / effectiveStep)
|
||||||
|
return Math.max(props.min, Math.min(props.max, props.min + steps * effectiveStep))
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDrag = (handle: 'min' | 'max', event: MouseEvent | TouchEvent) => {
|
const startDrag = (handle: 'min' | 'max', event: MouseEvent | TouchEvent) => {
|
||||||
@@ -122,21 +221,38 @@ const onDrag = (event: MouseEvent | TouchEvent) => {
|
|||||||
|
|
||||||
const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX
|
const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX
|
||||||
const rect = trackEl.value.getBoundingClientRect()
|
const rect = trackEl.value.getBoundingClientRect()
|
||||||
const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width))
|
const percent = Math.max(0, Math.min(100, (clientX - rect.left) / rect.width * 100))
|
||||||
const rawValue = props.min + percent * (props.max - props.min)
|
|
||||||
const value = snapToStep(rawValue)
|
const rawValue = percentToValue(percent)
|
||||||
|
const multiplier = getSegmentStepMultiplier(rawValue)
|
||||||
|
const value = snapToStep(rawValue, multiplier)
|
||||||
|
|
||||||
if (dragging.value === 'min') {
|
if (dragging.value === 'min') {
|
||||||
const maxAllowed = props.valueMax - props.step
|
const maxMultiplier = getSegmentStepMultiplier(props.valueMax)
|
||||||
|
const maxAllowed = props.valueMax - (props.step * maxMultiplier)
|
||||||
const newValue = Math.min(value, maxAllowed)
|
const newValue = Math.min(value, maxAllowed)
|
||||||
emit('update:valueMin', newValue)
|
emit('update:valueMin', newValue)
|
||||||
} else {
|
} else {
|
||||||
const minAllowed = props.valueMin + props.step
|
const minMultiplier = getSegmentStepMultiplier(props.valueMin)
|
||||||
|
const minAllowed = props.valueMin + (props.step * minMultiplier)
|
||||||
const newValue = Math.max(value, minAllowed)
|
const newValue = Math.max(value, minAllowed)
|
||||||
emit('update:valueMax', newValue)
|
emit('update:valueMax', newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSegmentStepMultiplier = (value: number): number => {
|
||||||
|
if (props.scaleMode !== 'segmented' || effectiveSegments.value.length === 0) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const seg of effectiveSegments.value) {
|
||||||
|
if (value >= seg.min && value < seg.max) {
|
||||||
|
return seg.wheelStepMultiplier || 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
const onWheel = (event: WheelEvent) => {
|
const onWheel = (event: WheelEvent) => {
|
||||||
if (props.disabled) return
|
if (props.disabled) return
|
||||||
|
|
||||||
@@ -157,22 +273,31 @@ const onWheel = (event: WheelEvent) => {
|
|||||||
const maxPixel = (maxPercent.value / 100) * rangeWidth
|
const maxPixel = (maxPercent.value / 100) * rangeWidth
|
||||||
|
|
||||||
if (relativeX < minPixel) {
|
if (relativeX < minPixel) {
|
||||||
const newValue = snapToStep(props.valueMin + delta * props.step)
|
const multiplier = getSegmentStepMultiplier(props.valueMin)
|
||||||
const maxAllowed = props.valueMax - props.step
|
const effectiveStep = props.step * multiplier
|
||||||
|
const newValue = snapToStep(props.valueMin + delta * effectiveStep, multiplier)
|
||||||
|
const maxMultiplier = getSegmentStepMultiplier(props.valueMax)
|
||||||
|
const maxAllowed = props.valueMax - (props.step * maxMultiplier)
|
||||||
emit('update:valueMin', Math.min(newValue, maxAllowed))
|
emit('update:valueMin', Math.min(newValue, maxAllowed))
|
||||||
} else if (relativeX > maxPixel) {
|
} else if (relativeX > maxPixel) {
|
||||||
const newValue = snapToStep(props.valueMax + delta * props.step)
|
const multiplier = getSegmentStepMultiplier(props.valueMax)
|
||||||
const minAllowed = props.valueMin + props.step
|
const effectiveStep = props.step * multiplier
|
||||||
|
const newValue = snapToStep(props.valueMax + delta * effectiveStep, multiplier)
|
||||||
|
const minMultiplier = getSegmentStepMultiplier(props.valueMin)
|
||||||
|
const minAllowed = props.valueMin + (props.step * minMultiplier)
|
||||||
emit('update:valueMax', Math.max(newValue, minAllowed))
|
emit('update:valueMax', Math.max(newValue, minAllowed))
|
||||||
} else {
|
} else {
|
||||||
const newMin = snapToStep(props.valueMin - delta * props.step)
|
const minMultiplier = getSegmentStepMultiplier(props.valueMin)
|
||||||
const newMax = snapToStep(props.valueMax + delta * props.step)
|
const maxMultiplier = getSegmentStepMultiplier(props.valueMax)
|
||||||
|
const newMin = snapToStep(props.valueMin - delta * props.step * minMultiplier, minMultiplier)
|
||||||
|
const newMax = snapToStep(props.valueMax + delta * props.step * maxMultiplier, maxMultiplier)
|
||||||
|
|
||||||
if (newMin < props.valueMin) {
|
if (newMin < props.valueMin) {
|
||||||
emit('update:valueMin', Math.max(newMin, props.min))
|
emit('update:valueMin', Math.max(newMin, props.min))
|
||||||
emit('update:valueMax', Math.min(newMax, props.max))
|
emit('update:valueMax', Math.min(newMax, props.max))
|
||||||
} else {
|
} else {
|
||||||
if (newMin < newMax - props.step) {
|
const minAllowed = props.valueMin + (props.step * minMultiplier)
|
||||||
|
if (newMin < newMax - (props.step * minMultiplier)) {
|
||||||
emit('update:valueMin', newMin)
|
emit('update:valueMin', newMin)
|
||||||
emit('update:valueMax', newMax)
|
emit('update:valueMax', newMax)
|
||||||
}
|
}
|
||||||
@@ -240,6 +365,28 @@ onUnmounted(() => {
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slider-track__segment {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(66, 153, 225, 0.08);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-track__segment--expanded {
|
||||||
|
background: rgba(66, 153, 225, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-track__segment:not(:last-child)::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
bottom: -1px;
|
||||||
|
right: 0;
|
||||||
|
width: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.slider-handle {
|
.slider-handle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -1059,17 +1059,17 @@ to { transform: rotate(360deg);
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dual-range-slider[data-v-5c79be81] {
|
.dual-range-slider[data-v-58c2ac1d] {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
.dual-range-slider.disabled[data-v-5c79be81] {
|
.dual-range-slider.disabled[data-v-58c2ac1d] {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.slider-track[data-v-5c79be81] {
|
.slider-track[data-v-58c2ac1d] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 14px;
|
top: 14px;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -1078,13 +1078,13 @@ to { transform: rotate(360deg);
|
|||||||
background: var(--comfy-input-bg, #333);
|
background: var(--comfy-input-bg, #333);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
.slider-track__bg[data-v-5c79be81] {
|
.slider-track__bg[data-v-58c2ac1d] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: rgba(66, 153, 225, 0.15);
|
background: rgba(66, 153, 225, 0.15);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
.slider-track__active[data-v-5c79be81] {
|
.slider-track__active[data-v-58c2ac1d] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -1092,24 +1092,43 @@ to { transform: rotate(360deg);
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
transition: left 0.05s linear, width 0.05s linear;
|
transition: left 0.05s linear, width 0.05s linear;
|
||||||
}
|
}
|
||||||
.slider-track__default[data-v-5c79be81] {
|
.slider-track__default[data-v-58c2ac1d] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: rgba(66, 153, 225, 0.1);
|
background: rgba(66, 153, 225, 0.1);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
.slider-handle[data-v-5c79be81] {
|
.slider-track__segment[data-v-58c2ac1d] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(66, 153, 225, 0.08);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.slider-track__segment--expanded[data-v-58c2ac1d] {
|
||||||
|
background: rgba(66, 153, 225, 0.15);
|
||||||
|
}
|
||||||
|
.slider-track__segment[data-v-58c2ac1d]:not(:last-child)::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
bottom: -1px;
|
||||||
|
right: 0;
|
||||||
|
width: 1px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.slider-handle[data-v-58c2ac1d] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.slider-handle[data-v-5c79be81]:active {
|
.slider-handle[data-v-58c2ac1d]:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
.slider-handle__thumb[data-v-5c79be81] {
|
.slider-handle__thumb[data-v-58c2ac1d] {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background: var(--fg-color, #fff);
|
background: var(--fg-color, #fff);
|
||||||
@@ -1120,13 +1139,13 @@ to { transform: rotate(360deg);
|
|||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||||
transition: transform 0.15s ease;
|
transition: transform 0.15s ease;
|
||||||
}
|
}
|
||||||
.slider-handle:hover .slider-handle__thumb[data-v-5c79be81] {
|
.slider-handle:hover .slider-handle__thumb[data-v-58c2ac1d] {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
.slider-handle:active .slider-handle__thumb[data-v-5c79be81] {
|
.slider-handle:active .slider-handle__thumb[data-v-58c2ac1d] {
|
||||||
transform: scale(1.15);
|
transform: scale(1.15);
|
||||||
}
|
}
|
||||||
.slider-handle__value[data-v-5c79be81] {
|
.slider-handle__value[data-v-58c2ac1d] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@@ -1138,23 +1157,23 @@ to { transform: rotate(360deg);
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.slider-handle--min .slider-handle__value[data-v-5c79be81] {
|
.slider-handle--min .slider-handle__value[data-v-58c2ac1d] {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.slider-handle--max .slider-handle__value[data-v-5c79be81] {
|
.slider-handle--max .slider-handle__value[data-v-58c2ac1d] {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.randomizer-settings[data-v-3c382ac5] {
|
.randomizer-settings[data-v-d7191de5] {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
color: #e4e4e7;
|
color: #e4e4e7;
|
||||||
}
|
}
|
||||||
.settings-header[data-v-3c382ac5] {
|
.settings-header[data-v-d7191de5] {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.settings-title[data-v-3c382ac5] {
|
.settings-title[data-v-d7191de5] {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
@@ -1163,28 +1182,28 @@ to { transform: rotate(360deg);
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
.setting-section[data-v-3c382ac5] {
|
.setting-section[data-v-d7191de5] {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.setting-label[data-v-3c382ac5] {
|
.setting-label[data-v-d7191de5] {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #d4d4d8;
|
color: #d4d4d8;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.section-header-with-toggle[data-v-3c382ac5] {
|
.section-header-with-toggle[data-v-d7191de5] {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.section-header-with-toggle .setting-label[data-v-3c382ac5] {
|
.section-header-with-toggle .setting-label[data-v-d7191de5] {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Count Mode Tabs */
|
/* Count Mode Tabs */
|
||||||
.count-mode-tabs[data-v-3c382ac5] {
|
.count-mode-tabs[data-v-d7191de5] {
|
||||||
display: flex;
|
display: flex;
|
||||||
background: rgba(26, 32, 44, 0.9);
|
background: rgba(26, 32, 44, 0.9);
|
||||||
border: 1px solid rgba(226, 232, 240, 0.2);
|
border: 1px solid rgba(226, 232, 240, 0.2);
|
||||||
@@ -1192,7 +1211,7 @@ to { transform: rotate(360deg);
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.count-mode-tab[data-v-3c382ac5] {
|
.count-mode-tab[data-v-d7191de5] {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
@@ -1200,29 +1219,29 @@ to { transform: rotate(360deg);
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
.count-mode-tab input[type="radio"][data-v-3c382ac5] {
|
.count-mode-tab input[type="radio"][data-v-d7191de5] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
.count-mode-tab-label[data-v-3c382ac5] {
|
.count-mode-tab-label[data-v-d7191de5] {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: rgba(226, 232, 240, 0.7);
|
color: rgba(226, 232, 240, 0.7);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
.count-mode-tab:hover .count-mode-tab-label[data-v-3c382ac5] {
|
.count-mode-tab:hover .count-mode-tab-label[data-v-d7191de5] {
|
||||||
color: rgba(226, 232, 240, 0.9);
|
color: rgba(226, 232, 240, 0.9);
|
||||||
}
|
}
|
||||||
.count-mode-tab.active .count-mode-tab-label[data-v-3c382ac5] {
|
.count-mode-tab.active .count-mode-tab-label[data-v-d7191de5] {
|
||||||
color: rgba(191, 219, 254, 1);
|
color: rgba(191, 219, 254, 1);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.count-mode-tab.active[data-v-3c382ac5] {
|
.count-mode-tab.active[data-v-d7191de5] {
|
||||||
background: rgba(66, 153, 225, 0.2);
|
background: rgba(66, 153, 225, 0.2);
|
||||||
}
|
}
|
||||||
.count-mode-tab.active[data-v-3c382ac5]::after {
|
.count-mode-tab.active[data-v-d7191de5]::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -1231,7 +1250,7 @@ to { transform: rotate(360deg);
|
|||||||
height: 2px;
|
height: 2px;
|
||||||
background: rgba(66, 153, 225, 0.9);
|
background: rgba(66, 153, 225, 0.9);
|
||||||
}
|
}
|
||||||
.slider-container[data-v-3c382ac5] {
|
.slider-container[data-v-d7191de5] {
|
||||||
background: rgba(26, 32, 44, 0.9);
|
background: rgba(26, 32, 44, 0.9);
|
||||||
border: 1px solid rgba(226, 232, 240, 0.2);
|
border: 1px solid rgba(226, 232, 240, 0.2);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -1239,7 +1258,7 @@ to { transform: rotate(360deg);
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Toggle Switch (same style as LicenseSection) */
|
/* Toggle Switch (same style as LicenseSection) */
|
||||||
.toggle-switch[data-v-3c382ac5] {
|
.toggle-switch[data-v-d7191de5] {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@@ -1248,7 +1267,7 @@ to { transform: rotate(360deg);
|
|||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.toggle-switch__track[data-v-3c382ac5] {
|
.toggle-switch__track[data-v-d7191de5] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: var(--comfy-input-bg, #333);
|
background: var(--comfy-input-bg, #333);
|
||||||
@@ -1256,11 +1275,11 @@ to { transform: rotate(360deg);
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
.toggle-switch--active .toggle-switch__track[data-v-3c382ac5] {
|
.toggle-switch--active .toggle-switch__track[data-v-d7191de5] {
|
||||||
background: rgba(66, 153, 225, 0.3);
|
background: rgba(66, 153, 225, 0.3);
|
||||||
border-color: rgba(66, 153, 225, 0.6);
|
border-color: rgba(66, 153, 225, 0.6);
|
||||||
}
|
}
|
||||||
.toggle-switch__thumb[data-v-3c382ac5] {
|
.toggle-switch__thumb[data-v-d7191de5] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
left: 2px;
|
left: 2px;
|
||||||
@@ -1271,27 +1290,27 @@ to { transform: rotate(360deg);
|
|||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
.toggle-switch--active .toggle-switch__thumb[data-v-3c382ac5] {
|
.toggle-switch--active .toggle-switch__thumb[data-v-d7191de5] {
|
||||||
transform: translateX(16px);
|
transform: translateX(16px);
|
||||||
background: #4299e1;
|
background: #4299e1;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
.toggle-switch:hover .toggle-switch__thumb[data-v-3c382ac5] {
|
.toggle-switch:hover .toggle-switch__thumb[data-v-d7191de5] {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Roll buttons with tooltip container */
|
/* Roll buttons with tooltip container */
|
||||||
.roll-buttons-with-tooltip[data-v-3c382ac5] {
|
.roll-buttons-with-tooltip[data-v-d7191de5] {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Roll buttons container */
|
/* Roll buttons container */
|
||||||
.roll-buttons[data-v-3c382ac5] {
|
.roll-buttons[data-v-d7191de5] {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
.roll-button[data-v-3c382ac5] {
|
.roll-button[data-v-d7191de5] {
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
background: rgba(30, 30, 36, 0.6);
|
background: rgba(30, 30, 36, 0.6);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
@@ -1308,39 +1327,39 @@ to { transform: rotate(360deg);
|
|||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.roll-button[data-v-3c382ac5]:hover:not(:disabled) {
|
.roll-button[data-v-d7191de5]:hover:not(:disabled) {
|
||||||
background: rgba(66, 153, 225, 0.2);
|
background: rgba(66, 153, 225, 0.2);
|
||||||
border-color: rgba(66, 153, 225, 0.4);
|
border-color: rgba(66, 153, 225, 0.4);
|
||||||
color: #bfdbfe;
|
color: #bfdbfe;
|
||||||
}
|
}
|
||||||
.roll-button.selected[data-v-3c382ac5] {
|
.roll-button.selected[data-v-d7191de5] {
|
||||||
background: rgba(66, 153, 225, 0.3);
|
background: rgba(66, 153, 225, 0.3);
|
||||||
border-color: rgba(66, 153, 225, 0.6);
|
border-color: rgba(66, 153, 225, 0.6);
|
||||||
color: #e4e4e7;
|
color: #e4e4e7;
|
||||||
box-shadow: 0 0 0 1px rgba(66, 153, 225, 0.3);
|
box-shadow: 0 0 0 1px rgba(66, 153, 225, 0.3);
|
||||||
}
|
}
|
||||||
.roll-button[data-v-3c382ac5]:disabled {
|
.roll-button[data-v-d7191de5]:disabled {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
.roll-button__icon[data-v-3c382ac5] {
|
.roll-button__icon[data-v-d7191de5] {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.roll-button__text[data-v-3c382ac5] {
|
.roll-button__text[data-v-d7191de5] {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tooltip transitions */
|
/* Tooltip transitions */
|
||||||
.tooltip-enter-active[data-v-3c382ac5],
|
.tooltip-enter-active[data-v-d7191de5],
|
||||||
.tooltip-leave-active[data-v-3c382ac5] {
|
.tooltip-leave-active[data-v-d7191de5] {
|
||||||
transition: opacity 0.15s ease, transform 0.15s ease;
|
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||||
}
|
}
|
||||||
.tooltip-enter-from[data-v-3c382ac5],
|
.tooltip-enter-from[data-v-d7191de5],
|
||||||
.tooltip-leave-to[data-v-3c382ac5] {
|
.tooltip-leave-to[data-v-d7191de5] {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(4px);
|
transform: translateY(4px);
|
||||||
}
|
}
|
||||||
@@ -11344,7 +11363,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
valueMax: {},
|
valueMax: {},
|
||||||
step: {},
|
step: {},
|
||||||
defaultRange: {},
|
defaultRange: {},
|
||||||
disabled: { type: Boolean, default: false }
|
disabled: { type: Boolean, default: false },
|
||||||
|
scaleMode: { default: "linear" },
|
||||||
|
segments: { default: () => [] }
|
||||||
},
|
},
|
||||||
emits: ["update:valueMin", "update:valueMax"],
|
emits: ["update:valueMin", "update:valueMax"],
|
||||||
setup(__props, { emit: __emit }) {
|
setup(__props, { emit: __emit }) {
|
||||||
@@ -11352,11 +11373,23 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
const emit2 = __emit;
|
const emit2 = __emit;
|
||||||
const trackEl = ref(null);
|
const trackEl = ref(null);
|
||||||
const dragging = ref(null);
|
const dragging = ref(null);
|
||||||
|
const effectiveSegments = computed(() => {
|
||||||
|
if (props.scaleMode === "segmented" && props.segments.length > 0) {
|
||||||
|
return props.segments;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
const minPercent = computed(() => {
|
const minPercent = computed(() => {
|
||||||
|
if (props.scaleMode === "segmented" && effectiveSegments.value.length > 0) {
|
||||||
|
return valueToPercent(props.valueMin);
|
||||||
|
}
|
||||||
const range = props.max - props.min;
|
const range = props.max - props.min;
|
||||||
return (props.valueMin - props.min) / range * 100;
|
return (props.valueMin - props.min) / range * 100;
|
||||||
});
|
});
|
||||||
const maxPercent = computed(() => {
|
const maxPercent = computed(() => {
|
||||||
|
if (props.scaleMode === "segmented" && effectiveSegments.value.length > 0) {
|
||||||
|
return valueToPercent(props.valueMax);
|
||||||
|
}
|
||||||
const range = props.max - props.min;
|
const range = props.max - props.min;
|
||||||
return (props.valueMax - props.min) / range * 100;
|
return (props.valueMax - props.min) / range * 100;
|
||||||
});
|
});
|
||||||
@@ -11367,9 +11400,61 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
});
|
});
|
||||||
const defaultMaxPercent = computed(() => {
|
const defaultMaxPercent = computed(() => {
|
||||||
if (!props.defaultRange) return 100;
|
if (!props.defaultRange) return 100;
|
||||||
|
if (props.scaleMode === "segmented" && effectiveSegments.value.length > 0) {
|
||||||
|
return valueToPercent(props.defaultRange.max);
|
||||||
|
}
|
||||||
const range = props.max - props.min;
|
const range = props.max - props.min;
|
||||||
return (props.defaultRange.max - props.min) / range * 100;
|
return (props.defaultRange.max - props.min) / range * 100;
|
||||||
});
|
});
|
||||||
|
const valueToPercent = (value) => {
|
||||||
|
const segments = effectiveSegments.value;
|
||||||
|
if (segments.length === 0) {
|
||||||
|
const range = props.max - props.min;
|
||||||
|
return (value - props.min) / range * 100;
|
||||||
|
}
|
||||||
|
let accumulatedPercent = 0;
|
||||||
|
for (const seg of segments) {
|
||||||
|
if (value >= seg.max) {
|
||||||
|
accumulatedPercent += seg.widthPercent;
|
||||||
|
} else if (value >= seg.min) {
|
||||||
|
const segRange = seg.max - seg.min;
|
||||||
|
const valueInSeg = value - seg.min;
|
||||||
|
accumulatedPercent += valueInSeg / segRange * seg.widthPercent;
|
||||||
|
return accumulatedPercent;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accumulatedPercent;
|
||||||
|
};
|
||||||
|
const percentToValue = (percent) => {
|
||||||
|
const segments = effectiveSegments.value;
|
||||||
|
if (segments.length === 0) {
|
||||||
|
const range = props.max - props.min;
|
||||||
|
return props.min + percent / 100 * range;
|
||||||
|
}
|
||||||
|
let accumulatedPercent = 0;
|
||||||
|
for (const seg of segments) {
|
||||||
|
const segEndPercent = accumulatedPercent + seg.widthPercent;
|
||||||
|
if (percent <= segEndPercent) {
|
||||||
|
const segRange = seg.max - seg.min;
|
||||||
|
const percentInSeg = (percent - accumulatedPercent) / seg.widthPercent;
|
||||||
|
return seg.min + percentInSeg * segRange;
|
||||||
|
}
|
||||||
|
accumulatedPercent = segEndPercent;
|
||||||
|
}
|
||||||
|
return props.max;
|
||||||
|
};
|
||||||
|
const getSegmentStyle = (seg, index) => {
|
||||||
|
let leftPercent = 0;
|
||||||
|
for (let i2 = 0; i2 < index; i2++) {
|
||||||
|
leftPercent += effectiveSegments.value[i2].widthPercent;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
left: leftPercent + "%",
|
||||||
|
width: seg.widthPercent + "%"
|
||||||
|
};
|
||||||
|
};
|
||||||
const formatValue = (val) => {
|
const formatValue = (val) => {
|
||||||
if (Number.isInteger(val)) return val.toString();
|
if (Number.isInteger(val)) return val.toString();
|
||||||
return val.toFixed(stepToDecimals(props.step));
|
return val.toFixed(stepToDecimals(props.step));
|
||||||
@@ -11379,9 +11464,10 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
const decimalIndex = str.indexOf(".");
|
const decimalIndex = str.indexOf(".");
|
||||||
return decimalIndex === -1 ? 0 : str.length - decimalIndex - 1;
|
return decimalIndex === -1 ? 0 : str.length - decimalIndex - 1;
|
||||||
};
|
};
|
||||||
const snapToStep = (value) => {
|
const snapToStep = (value, segmentMultiplier) => {
|
||||||
const steps = Math.round((value - props.min) / props.step);
|
const effectiveStep = segmentMultiplier ? props.step * segmentMultiplier : props.step;
|
||||||
return Math.max(props.min, Math.min(props.max, props.min + steps * props.step));
|
const steps = Math.round((value - props.min) / effectiveStep);
|
||||||
|
return Math.max(props.min, Math.min(props.max, props.min + steps * effectiveStep));
|
||||||
};
|
};
|
||||||
const startDrag = (handle, event) => {
|
const startDrag = (handle, event) => {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
@@ -11397,19 +11483,33 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const clientX = "touches" in event ? event.touches[0].clientX : event.clientX;
|
const clientX = "touches" in event ? event.touches[0].clientX : event.clientX;
|
||||||
const rect = trackEl.value.getBoundingClientRect();
|
const rect = trackEl.value.getBoundingClientRect();
|
||||||
const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
const percent = Math.max(0, Math.min(100, (clientX - rect.left) / rect.width * 100));
|
||||||
const rawValue = props.min + percent * (props.max - props.min);
|
const rawValue = percentToValue(percent);
|
||||||
const value = snapToStep(rawValue);
|
const multiplier = getSegmentStepMultiplier(rawValue);
|
||||||
|
const value = snapToStep(rawValue, multiplier);
|
||||||
if (dragging.value === "min") {
|
if (dragging.value === "min") {
|
||||||
const maxAllowed = props.valueMax - props.step;
|
const maxMultiplier = getSegmentStepMultiplier(props.valueMax);
|
||||||
|
const maxAllowed = props.valueMax - props.step * maxMultiplier;
|
||||||
const newValue = Math.min(value, maxAllowed);
|
const newValue = Math.min(value, maxAllowed);
|
||||||
emit2("update:valueMin", newValue);
|
emit2("update:valueMin", newValue);
|
||||||
} else {
|
} else {
|
||||||
const minAllowed = props.valueMin + props.step;
|
const minMultiplier = getSegmentStepMultiplier(props.valueMin);
|
||||||
|
const minAllowed = props.valueMin + props.step * minMultiplier;
|
||||||
const newValue = Math.max(value, minAllowed);
|
const newValue = Math.max(value, minAllowed);
|
||||||
emit2("update:valueMax", newValue);
|
emit2("update:valueMax", newValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const getSegmentStepMultiplier = (value) => {
|
||||||
|
if (props.scaleMode !== "segmented" || effectiveSegments.value.length === 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for (const seg of effectiveSegments.value) {
|
||||||
|
if (value >= seg.min && value < seg.max) {
|
||||||
|
return seg.wheelStepMultiplier || 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
const onWheel = (event) => {
|
const onWheel = (event) => {
|
||||||
var _a;
|
var _a;
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
@@ -11424,21 +11524,30 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
const minPixel = minPercent.value / 100 * rangeWidth;
|
const minPixel = minPercent.value / 100 * rangeWidth;
|
||||||
const maxPixel = maxPercent.value / 100 * rangeWidth;
|
const maxPixel = maxPercent.value / 100 * rangeWidth;
|
||||||
if (relativeX < minPixel) {
|
if (relativeX < minPixel) {
|
||||||
const newValue = snapToStep(props.valueMin + delta * props.step);
|
const multiplier = getSegmentStepMultiplier(props.valueMin);
|
||||||
const maxAllowed = props.valueMax - props.step;
|
const effectiveStep = props.step * multiplier;
|
||||||
|
const newValue = snapToStep(props.valueMin + delta * effectiveStep, multiplier);
|
||||||
|
const maxMultiplier = getSegmentStepMultiplier(props.valueMax);
|
||||||
|
const maxAllowed = props.valueMax - props.step * maxMultiplier;
|
||||||
emit2("update:valueMin", Math.min(newValue, maxAllowed));
|
emit2("update:valueMin", Math.min(newValue, maxAllowed));
|
||||||
} else if (relativeX > maxPixel) {
|
} else if (relativeX > maxPixel) {
|
||||||
const newValue = snapToStep(props.valueMax + delta * props.step);
|
const multiplier = getSegmentStepMultiplier(props.valueMax);
|
||||||
const minAllowed = props.valueMin + props.step;
|
const effectiveStep = props.step * multiplier;
|
||||||
|
const newValue = snapToStep(props.valueMax + delta * effectiveStep, multiplier);
|
||||||
|
const minMultiplier = getSegmentStepMultiplier(props.valueMin);
|
||||||
|
const minAllowed = props.valueMin + props.step * minMultiplier;
|
||||||
emit2("update:valueMax", Math.max(newValue, minAllowed));
|
emit2("update:valueMax", Math.max(newValue, minAllowed));
|
||||||
} else {
|
} else {
|
||||||
const newMin = snapToStep(props.valueMin - delta * props.step);
|
const minMultiplier = getSegmentStepMultiplier(props.valueMin);
|
||||||
const newMax = snapToStep(props.valueMax + delta * props.step);
|
const maxMultiplier = getSegmentStepMultiplier(props.valueMax);
|
||||||
|
const newMin = snapToStep(props.valueMin - delta * props.step * minMultiplier, minMultiplier);
|
||||||
|
const newMax = snapToStep(props.valueMax + delta * props.step * maxMultiplier, maxMultiplier);
|
||||||
if (newMin < props.valueMin) {
|
if (newMin < props.valueMin) {
|
||||||
emit2("update:valueMin", Math.max(newMin, props.min));
|
emit2("update:valueMin", Math.max(newMin, props.min));
|
||||||
emit2("update:valueMax", Math.min(newMax, props.max));
|
emit2("update:valueMax", Math.min(newMax, props.max));
|
||||||
} else {
|
} else {
|
||||||
if (newMin < newMax - props.step) {
|
props.valueMin + props.step * minMultiplier;
|
||||||
|
if (newMin < newMax - props.step * minMultiplier) {
|
||||||
emit2("update:valueMin", newMin);
|
emit2("update:valueMin", newMin);
|
||||||
emit2("update:valueMax", newMax);
|
emit2("update:valueMax", newMax);
|
||||||
}
|
}
|
||||||
@@ -11457,7 +11566,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
});
|
});
|
||||||
return (_ctx, _cache) => {
|
return (_ctx, _cache) => {
|
||||||
return openBlock(), createElementBlock("div", {
|
return openBlock(), createElementBlock("div", {
|
||||||
class: normalizeClass(["dual-range-slider", { disabled: __props.disabled }]),
|
class: normalizeClass(["dual-range-slider", { disabled: __props.disabled, "has-segments": __props.scaleMode === "segmented" && effectiveSegments.value.length > 0 }]),
|
||||||
onWheel
|
onWheel
|
||||||
}, [
|
}, [
|
||||||
createBaseVNode("div", {
|
createBaseVNode("div", {
|
||||||
@@ -11466,12 +11575,22 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
ref: trackEl
|
ref: trackEl
|
||||||
}, [
|
}, [
|
||||||
_cache[4] || (_cache[4] = createBaseVNode("div", { class: "slider-track__bg" }, null, -1)),
|
_cache[4] || (_cache[4] = createBaseVNode("div", { class: "slider-track__bg" }, null, -1)),
|
||||||
|
__props.scaleMode === "segmented" && effectiveSegments.value.length > 0 ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(effectiveSegments.value, (seg, index) => {
|
||||||
|
return openBlock(), createElementBlock("div", {
|
||||||
|
key: "segment-" + index,
|
||||||
|
class: normalizeClass(["slider-track__segment", {
|
||||||
|
"slider-track__segment--common": seg.wheelStepMultiplier && seg.wheelStepMultiplier < 1,
|
||||||
|
"slider-track__segment--expanded": seg.wheelStepMultiplier && seg.wheelStepMultiplier < 1
|
||||||
|
}]),
|
||||||
|
style: normalizeStyle(getSegmentStyle(seg, index))
|
||||||
|
}, null, 6);
|
||||||
|
}), 128)) : createCommentVNode("", true),
|
||||||
createBaseVNode("div", {
|
createBaseVNode("div", {
|
||||||
class: "slider-track__active",
|
class: "slider-track__active",
|
||||||
style: normalizeStyle({ left: minPercent.value + "%", width: maxPercent.value - minPercent.value + "%" })
|
style: normalizeStyle({ left: minPercent.value + "%", width: maxPercent.value - minPercent.value + "%" })
|
||||||
}, null, 4),
|
}, null, 4),
|
||||||
__props.defaultRange ? (openBlock(), createElementBlock("div", {
|
__props.defaultRange ? (openBlock(), createElementBlock("div", {
|
||||||
key: 0,
|
key: 1,
|
||||||
class: "slider-track__default",
|
class: "slider-track__default",
|
||||||
style: normalizeStyle({
|
style: normalizeStyle({
|
||||||
left: defaultMinPercent.value + "%",
|
left: defaultMinPercent.value + "%",
|
||||||
@@ -11501,7 +11620,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const DualRangeSlider = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-5c79be81"]]);
|
const DualRangeSlider = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-58c2ac1d"]]);
|
||||||
const _hoisted_1$1 = { class: "randomizer-settings" };
|
const _hoisted_1$1 = { class: "randomizer-settings" };
|
||||||
const _hoisted_2 = { class: "setting-section" };
|
const _hoisted_2 = { class: "setting-section" };
|
||||||
const _hoisted_3 = { class: "count-mode-tabs" };
|
const _hoisted_3 = { class: "count-mode-tabs" };
|
||||||
@@ -11542,6 +11661,11 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ["update:countMode", "update:countFixed", "update:countMin", "update:countMax", "update:modelStrengthMin", "update:modelStrengthMax", "update:useSameClipStrength", "update:clipStrengthMin", "update:clipStrengthMax", "update:rollMode", "generate-fixed", "always-randomize", "reuse-last"],
|
emits: ["update:countMode", "update:countFixed", "update:countMin", "update:countMax", "update:modelStrengthMin", "update:modelStrengthMax", "update:useSameClipStrength", "update:clipStrengthMin", "update:clipStrengthMax", "update:rollMode", "generate-fixed", "always-randomize", "reuse-last"],
|
||||||
setup(__props) {
|
setup(__props) {
|
||||||
|
const strengthSegments = [
|
||||||
|
{ min: -10, max: -2, widthPercent: 20 },
|
||||||
|
{ min: -2, max: 2, widthPercent: 60, wheelStepMultiplier: 0.5 },
|
||||||
|
{ min: 2, max: 10, widthPercent: 20 }
|
||||||
|
];
|
||||||
const showTooltip = ref(false);
|
const showTooltip = ref(false);
|
||||||
const areLorasEqual = (a2, b2) => {
|
const areLorasEqual = (a2, b2) => {
|
||||||
if (!a2 || !b2) return false;
|
if (!a2 || !b2) return false;
|
||||||
@@ -11617,6 +11741,8 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|||||||
"value-max": __props.modelStrengthMax,
|
"value-max": __props.modelStrengthMax,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
"default-range": { min: -2, max: 3 },
|
"default-range": { min: -2, max: 3 },
|
||||||
|
"scale-mode": "segmented",
|
||||||
|
segments: strengthSegments,
|
||||||
"onUpdate:valueMin": _cache[5] || (_cache[5] = ($event) => _ctx.$emit("update:modelStrengthMin", $event)),
|
"onUpdate:valueMin": _cache[5] || (_cache[5] = ($event) => _ctx.$emit("update:modelStrengthMin", $event)),
|
||||||
"onUpdate:valueMax": _cache[6] || (_cache[6] = ($event) => _ctx.$emit("update:modelStrengthMax", $event))
|
"onUpdate:valueMax": _cache[6] || (_cache[6] = ($event) => _ctx.$emit("update:modelStrengthMax", $event))
|
||||||
}, null, 8, ["value-min", "value-max"])
|
}, null, 8, ["value-min", "value-max"])
|
||||||
@@ -11645,6 +11771,8 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|||||||
"value-max": __props.clipStrengthMax,
|
"value-max": __props.clipStrengthMax,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
"default-range": { min: -1, max: 2 },
|
"default-range": { min: -1, max: 2 },
|
||||||
|
"scale-mode": "segmented",
|
||||||
|
segments: strengthSegments,
|
||||||
disabled: __props.isClipStrengthDisabled,
|
disabled: __props.isClipStrengthDisabled,
|
||||||
"onUpdate:valueMin": _cache[8] || (_cache[8] = ($event) => _ctx.$emit("update:clipStrengthMin", $event)),
|
"onUpdate:valueMin": _cache[8] || (_cache[8] = ($event) => _ctx.$emit("update:clipStrengthMin", $event)),
|
||||||
"onUpdate:valueMax": _cache[9] || (_cache[9] = ($event) => _ctx.$emit("update:clipStrengthMax", $event))
|
"onUpdate:valueMax": _cache[9] || (_cache[9] = ($event) => _ctx.$emit("update:clipStrengthMax", $event))
|
||||||
@@ -11660,14 +11788,14 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|||||||
disabled: __props.isRolling,
|
disabled: __props.isRolling,
|
||||||
onClick: _cache[10] || (_cache[10] = ($event) => _ctx.$emit("generate-fixed"))
|
onClick: _cache[10] || (_cache[10] = ($event) => _ctx.$emit("generate-fixed"))
|
||||||
}, [..._cache[20] || (_cache[20] = [
|
}, [..._cache[20] || (_cache[20] = [
|
||||||
createStaticVNode('<svg class="roll-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-3c382ac5><rect x="2" y="2" width="20" height="20" rx="5" data-v-3c382ac5></rect><circle cx="12" cy="12" r="3" data-v-3c382ac5></circle><circle cx="6" cy="8" r="1.5" data-v-3c382ac5></circle><circle cx="18" cy="16" r="1.5" data-v-3c382ac5></circle></svg><span class="roll-button__text" data-v-3c382ac5>Generate Fixed</span>', 2)
|
createStaticVNode('<svg class="roll-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-d7191de5><rect x="2" y="2" width="20" height="20" rx="5" data-v-d7191de5></rect><circle cx="12" cy="12" r="3" data-v-d7191de5></circle><circle cx="6" cy="8" r="1.5" data-v-d7191de5></circle><circle cx="18" cy="16" r="1.5" data-v-d7191de5></circle></svg><span class="roll-button__text" data-v-d7191de5>Generate Fixed</span>', 2)
|
||||||
])], 10, _hoisted_17),
|
])], 10, _hoisted_17),
|
||||||
createBaseVNode("button", {
|
createBaseVNode("button", {
|
||||||
class: normalizeClass(["roll-button", { selected: __props.rollMode === "always" }]),
|
class: normalizeClass(["roll-button", { selected: __props.rollMode === "always" }]),
|
||||||
disabled: __props.isRolling,
|
disabled: __props.isRolling,
|
||||||
onClick: _cache[11] || (_cache[11] = ($event) => _ctx.$emit("always-randomize"))
|
onClick: _cache[11] || (_cache[11] = ($event) => _ctx.$emit("always-randomize"))
|
||||||
}, [..._cache[21] || (_cache[21] = [
|
}, [..._cache[21] || (_cache[21] = [
|
||||||
createStaticVNode('<svg class="roll-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-3c382ac5><path d="M21 12a9 9 0 1 1-6.219-8.56" data-v-3c382ac5></path><path d="M21 3v5h-5" data-v-3c382ac5></path><circle cx="12" cy="12" r="3" data-v-3c382ac5></circle><circle cx="6" cy="8" r="1.5" data-v-3c382ac5></circle><circle cx="18" cy="16" r="1.5" data-v-3c382ac5></circle></svg><span class="roll-button__text" data-v-3c382ac5>Always Randomize</span>', 2)
|
createStaticVNode('<svg class="roll-button__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-d7191de5><path d="M21 12a9 9 0 1 1-6.219-8.56" data-v-d7191de5></path><path d="M21 3v5h-5" data-v-d7191de5></path><circle cx="12" cy="12" r="3" data-v-d7191de5></circle><circle cx="6" cy="8" r="1.5" data-v-d7191de5></circle><circle cx="18" cy="16" r="1.5" data-v-d7191de5></circle></svg><span class="roll-button__text" data-v-d7191de5>Always Randomize</span>', 2)
|
||||||
])], 10, _hoisted_18),
|
])], 10, _hoisted_18),
|
||||||
createBaseVNode("button", {
|
createBaseVNode("button", {
|
||||||
class: normalizeClass(["roll-button", { selected: __props.rollMode === "fixed" && __props.canReuseLast && areLorasEqual(__props.currentLoras, __props.lastUsed) }]),
|
class: normalizeClass(["roll-button", { selected: __props.rollMode === "fixed" && __props.canReuseLast && areLorasEqual(__props.currentLoras, __props.lastUsed) }]),
|
||||||
@@ -11706,7 +11834,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const LoraRandomizerSettingsView = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-3c382ac5"]]);
|
const LoraRandomizerSettingsView = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-d7191de5"]]);
|
||||||
function useLoraRandomizerState(widget) {
|
function useLoraRandomizerState(widget) {
|
||||||
const countMode = ref("range");
|
const countMode = ref("range");
|
||||||
const countFixed = ref(3);
|
const countFixed = ref(3);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user