Skip to content

Commit 8e502c0

Browse files
Merge branch 'main' into fix-ogg-syntax-9256
2 parents 24f1afe + b18a071 commit 8e502c0

47 files changed

Lines changed: 585 additions & 190 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@comfyorg/comfyui-frontend",
3-
"version": "1.41.9",
3+
"version": "1.41.10",
44
"private": true,
55
"description": "Official front-end implementation of ComfyUI",
66
"homepage": "https://comfy.org",

src/components/builder/BuilderMenu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
@click="onSave(close)"
3333
>
3434
<i class="icon-[lucide--save] size-4" />
35-
{{ t('builderMenu.saveApp') }}
35+
{{ t('g.save') }}
3636
</button>
3737
<div class="my-1 border-t border-border-default" />
3838
<button

src/components/common/BackgroundImageUpload.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import InputText from 'primevue/inputtext'
4040
import { ref } from 'vue'
4141
4242
import Button from '@/components/ui/button/Button.vue'
43+
import { appendCloudResParam } from '@/platform/distribution/cloudPreviewUtil'
4344
import { useToastStore } from '@/platform/updates/common/toastStore'
4445
import { api } from '@/scripts/api'
4546
@@ -88,6 +89,7 @@ const handleFileUpload = async (event: Event) => {
8889
type: 'input',
8990
subfolder: 'backgrounds'
9091
})
92+
appendCloudResParam(params, file.name)
9193
modelValue.value = `/api/view?${params.toString()}`
9294
}
9395
} catch (error) {

src/components/common/WorkflowActionsList.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,24 @@ describe('WorkflowActionsList', () => {
7070
expect(wrapper.text()).toContain('NEW')
7171
})
7272

73+
it('does not render items with visible set to false', () => {
74+
const items: WorkflowMenuItem[] = [
75+
{
76+
id: 'hidden',
77+
label: 'Hidden Item',
78+
icon: 'pi pi-eye-slash',
79+
command: vi.fn(),
80+
visible: false
81+
},
82+
{ id: 'shown', label: 'Shown Item', icon: 'pi pi-eye', command: vi.fn() }
83+
]
84+
85+
const wrapper = createWrapper(items)
86+
87+
expect(wrapper.text()).not.toContain('Hidden Item')
88+
expect(wrapper.text()).toContain('Shown Item')
89+
})
90+
7391
it('does not render badge when absent', () => {
7492
const items: WorkflowMenuAction[] = [
7593
{ id: 'plain', label: 'Plain', icon: 'pi pi-check', command: vi.fn() }

src/components/common/WorkflowActionsList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const {
2626
/>
2727
<component
2828
:is="itemComponent"
29-
v-else
29+
v-else-if="item.visible !== false"
3030
:disabled="item.disabled"
3131
:class="
3232
cn(

src/components/painter/WidgetPainter.vue

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
/>
2929
<div
3030
v-show="cursorVisible"
31-
class="pointer-events-none absolute left-0 top-0 rounded-full border border-black/60 shadow-[0_0_0_1px_rgba(255,255,255,0.8)]"
32-
:style="cursorStyle"
31+
ref="cursorEl"
32+
class="pointer-events-none absolute left-0 top-0 rounded-full border border-black/60 shadow-[0_0_0_1px_rgba(255,255,255,0.8)] will-change-transform"
33+
:style="cursorSizeStyle"
3334
/>
3435
</div>
3536
</div>
@@ -141,7 +142,7 @@
141142
max="100"
142143
step="1"
143144
class="w-7 appearance-none border-0 bg-transparent text-right text-xs text-node-text-muted outline-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none [-moz-appearance:textfield]"
144-
@click.prevent
145+
@click.stop
145146
@change="
146147
(e) => {
147148
const val = Math.min(
@@ -281,6 +282,7 @@ const { nodeId } = defineProps<{
281282
const modelValue = defineModel<string>({ default: '' })
282283
283284
const canvasEl = useTemplateRef<HTMLCanvasElement>('canvasEl')
285+
const cursorEl = useTemplateRef<HTMLElement>('cursorEl')
284286
const controlsEl = useTemplateRef<HTMLDivElement>('controlsEl')
285287
const { width: controlsWidth } = useElementSize(controlsEl)
286288
const compact = computed(
@@ -296,8 +298,6 @@ const {
296298
backgroundColor,
297299
canvasWidth,
298300
canvasHeight,
299-
cursorX,
300-
cursorY,
301301
cursorVisible,
302302
displayBrushSize,
303303
inputImageUrl,
@@ -309,7 +309,7 @@ const {
309309
handlePointerLeave,
310310
handleInputImageLoad,
311311
handleClear
312-
} = usePainter(nodeId, { canvasEl, modelValue })
312+
} = usePainter(nodeId, { canvasEl, cursorEl, modelValue })
313313
314314
const canvasContainerStyle = computed(() => ({
315315
aspectRatio: `${canvasWidth.value} / ${canvasHeight.value}`,
@@ -318,16 +318,10 @@ const canvasContainerStyle = computed(() => ({
318318
: backgroundColor.value
319319
}))
320320
321-
const cursorStyle = computed(() => {
322-
const size = displayBrushSize.value
323-
const x = cursorX.value - size / 2
324-
const y = cursorY.value - size / 2
325-
return {
326-
width: `${size}px`,
327-
height: `${size}px`,
328-
transform: `translate(${x}px, ${y}px)`
329-
}
330-
})
321+
const cursorSizeStyle = computed(() => ({
322+
width: `${displayBrushSize.value}px`,
323+
height: `${displayBrushSize.value}px`
324+
}))
331325
332326
const brushOpacityPercent = computed({
333327
get: () => Math.round(brushOpacity.value * 100),

src/components/ui/TypeformPopoverButton.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { useTemplateRef } from 'vue'
55
import Popover from '@/components/ui/Popover.vue'
66
import Button from '@/components/ui/button/Button.vue'
77
8-
defineProps<{
8+
const { active = true } = defineProps<{
99
dataTfWidget: string
10+
active?: boolean
1011
}>()
1112
1213
const feedbackRef = useTemplateRef('feedbackRef')
@@ -40,6 +41,6 @@ whenever(feedbackRef, () => {
4041
<i class="icon-[lucide--circle-help] size-4" />
4142
</Button>
4243
</template>
43-
<div ref="feedbackRef" data-tf-auto-resize :data-tf-widget />
44+
<div v-if="active" ref="feedbackRef" data-tf-auto-resize :data-tf-widget />
4445
</Popover>
4546
</template>

src/composables/painter/usePainter.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ export const PAINTER_TOOLS: Record<string, PainterTool> = {
2727

2828
interface UsePainterOptions {
2929
canvasEl: Ref<HTMLCanvasElement | null>
30+
cursorEl: Ref<HTMLElement | null>
3031
modelValue: Ref<string>
3132
}
3233

3334
export function usePainter(nodeId: string, options: UsePainterOptions) {
34-
const { canvasEl, modelValue } = options
35+
const { canvasEl, cursorEl, modelValue } = options
3536
const { t } = useI18n()
3637
const nodeOutputStore = useNodeOutputStore()
3738
const toastStore = useToastStore()
@@ -41,8 +42,6 @@ export function usePainter(nodeId: string, options: UsePainterOptions) {
4142
const canvasWidth = ref(512)
4243
const canvasHeight = ref(512)
4344

44-
const cursorX = ref(0)
45-
const cursorY = ref(0)
4645
const cursorVisible = ref(false)
4746

4847
const inputImageUrl = ref<string | null>(null)
@@ -518,8 +517,10 @@ export function usePainter(nodeId: string, options: UsePainterOptions) {
518517
}
519518

520519
function updateCursorPos(e: PointerEvent) {
521-
cursorX.value = e.offsetX
522-
cursorY.value = e.offsetY
520+
const el = cursorEl.value
521+
if (!el) return
522+
const size = displayBrushSize.value
523+
el.style.transform = `translate(${e.offsetX - size / 2}px, ${e.offsetY - size / 2}px)`
523524
}
524525

525526
function handlePointerDown(e: PointerEvent) {
@@ -760,8 +761,6 @@ export function usePainter(nodeId: string, options: UsePainterOptions) {
760761
canvasWidth,
761762
canvasHeight,
762763

763-
cursorX,
764-
cursorY,
765764
cursorVisible,
766765
displayBrushSize,
767766

src/composables/useNewMenuItemIndicator.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,80 @@ describe('useNewMenuItemIndicator', () => {
9898
)
9999
})
100100

101+
it('does not count hidden items as unseen', () => {
102+
const items: WorkflowMenuItem[] = [
103+
{
104+
id: 'hidden-feature',
105+
label: 'Hidden',
106+
icon: 'pi pi-test',
107+
command: vi.fn(),
108+
isNew: true,
109+
badge: 'BETA',
110+
visible: false
111+
}
112+
]
113+
const { hasUnseenItems } = useNewMenuItemIndicator(() => items)
114+
115+
expect(hasUnseenItems.value).toBe(false)
116+
})
117+
118+
it('markAsSeen does not include never-seen hidden items', () => {
119+
const items: WorkflowMenuItem[] = [
120+
...createItems('feature-a'),
121+
{
122+
id: 'hidden-feature',
123+
label: 'Hidden',
124+
icon: 'pi pi-test',
125+
command: vi.fn(),
126+
isNew: true,
127+
badge: 'BETA',
128+
visible: false
129+
}
130+
]
131+
const { markAsSeen } = useNewMenuItemIndicator(() => items)
132+
133+
markAsSeen()
134+
135+
expect(mockSettingStore.set).toHaveBeenCalledWith(
136+
'Comfy.WorkflowActions.SeenItems',
137+
['feature-a']
138+
)
139+
})
140+
141+
it('markAsSeen retains previously-seen hidden items', () => {
142+
mockSettingStore.get.mockReturnValue(['hidden-feature'])
143+
const items: WorkflowMenuItem[] = [
144+
...createItems('feature-a'),
145+
{
146+
id: 'hidden-feature',
147+
label: 'Hidden',
148+
icon: 'pi pi-test',
149+
command: vi.fn(),
150+
isNew: true,
151+
badge: 'BETA',
152+
visible: false
153+
}
154+
]
155+
const { markAsSeen } = useNewMenuItemIndicator(() => items)
156+
157+
markAsSeen()
158+
159+
expect(mockSettingStore.set).toHaveBeenCalledWith(
160+
'Comfy.WorkflowActions.SeenItems',
161+
['feature-a', 'hidden-feature']
162+
)
163+
})
164+
165+
it('markAsSeen skips write when stored list already matches', () => {
166+
mockSettingStore.get.mockReturnValue(['feature-a', 'feature-b'])
167+
const items = createItems('feature-a', 'feature-b')
168+
const { markAsSeen } = useNewMenuItemIndicator(() => items)
169+
170+
markAsSeen()
171+
172+
expect(mockSettingStore.set).not.toHaveBeenCalled()
173+
})
174+
101175
it('markAsSeen does nothing when there are no new items', () => {
102176
const items: WorkflowMenuItem[] = [
103177
{ id: 'regular', label: 'Regular', icon: 'pi pi-test', command: vi.fn() }

src/composables/useNewMenuItemIndicator.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,47 @@ import type {
77
WorkflowMenuItem
88
} from '@/types/workflowMenuItem'
99

10-
function getNewItemIds(items: WorkflowMenuItem[]): string[] {
10+
function getNewActions(items: WorkflowMenuItem[]): WorkflowMenuAction[] {
1111
return items
1212
.filter((i): i is WorkflowMenuAction => !('separator' in i && i.separator))
1313
.filter((i) => i.isNew)
14-
.map((i) => i.id)
1514
}
1615

1716
export function useNewMenuItemIndicator(
1817
menuItems: MaybeRefOrGetter<WorkflowMenuItem[]>
1918
) {
2019
const settingStore = useSettingStore()
2120

22-
const newItemIds = computed(() => getNewItemIds(toValue(menuItems)))
21+
const newActions = computed(() => getNewActions(toValue(menuItems)))
2322

2423
const seenItems = computed<string[]>(
2524
() => settingStore.get('Comfy.WorkflowActions.SeenItems') ?? []
2625
)
2726

2827
const hasUnseenItems = computed(() => {
2928
const seen = new Set(seenItems.value)
30-
return newItemIds.value.some((id) => !seen.has(id))
29+
return newActions.value
30+
.filter((i) => i.visible !== false)
31+
.some((i) => !seen.has(i.id))
3132
})
3233

3334
function markAsSeen() {
34-
if (!newItemIds.value.length) return
35-
void settingStore.set('Comfy.WorkflowActions.SeenItems', [
36-
...newItemIds.value
37-
])
35+
const actions = newActions.value
36+
if (!actions.length) return
37+
38+
const seen = new Set(seenItems.value)
39+
const visibleIds = actions
40+
.filter((i) => i.visible !== false)
41+
.map((i) => i.id)
42+
const retainedIds = actions
43+
.filter((i) => i.visible === false && seen.has(i.id))
44+
.map((i) => i.id)
45+
46+
const nextSeen = [...visibleIds, ...retainedIds]
47+
if (nextSeen.length === seen.size && nextSeen.every((id) => seen.has(id)))
48+
return
49+
50+
void settingStore.set('Comfy.WorkflowActions.SeenItems', nextSeen)
3851
}
3952

4053
return { hasUnseenItems, markAsSeen }

0 commit comments

Comments
 (0)