Compare commits

...

19 Commits

Author SHA1 Message Date
filtered
2e8e9e624b nit 2025-01-12 02:29:52 +11:00
filtered
546993ffe3 Reapply "[Desktop] Native window virtual menu bar (#2215)"
This reverts commit 104c0a5ba4.
2025-01-12 02:10:40 +11:00
github-actions
c41db49f7a Update locales [skip ci] 2025-01-11 14:36:09 +00:00
filtered
a9e4f5eb22 nit 2025-01-12 01:19:25 +11:00
filtered
c5314e4dc0 nit - Remove redundant code 2025-01-12 01:19:07 +11:00
filtered
aa08f3d1cc Convert raw CSS to tailwind apply 2025-01-12 00:28:01 +11:00
filtered
9b28372501 Revert native controls separator - poor UX
Minor UX concern that can be looked at later.
2025-01-11 23:48:57 +11:00
filtered
56e18aee3e Improve desktop settings UX - window style
- No longer requires app restart on window style change
2025-01-11 23:23:52 +11:00
filtered
6df7a45d61 Fix desktop API called when frontend settings loaded 2025-01-11 23:20:44 +11:00
filtered
5f90dd9c3f Add top menu styles, logo, control offset 2025-01-11 18:08:54 +11:00
filtered
002b7df795 Add per-view app-drag / no-drag 2025-01-11 18:06:55 +11:00
filtered
9d108b80b9 Add hover colour to native window controls 2025-01-11 18:02:55 +11:00
filtered
504c661859 Fix native window control hover highlight 2025-01-11 17:09:42 +11:00
filtered
eb01fa6574 Partially Revert "[Desktop] Set window action buttons style (#2214)"
This reverts commit 04c23001fc.
- No change to BaseViewTemplate
- No change to mock electronAPI
2025-01-11 17:05:04 +11:00
filtered
104c0a5ba4 Revert "[Desktop] Native window virtual menu bar (#2215)"
This reverts commit e076783b89.
2025-01-11 16:57:04 +11:00
filtered
45596058f1 Revert "[Desktop] Native window in graph view (#2216)"
This reverts commit 8f5aa1ff08.
2025-01-11 16:51:55 +11:00
filtered
582ca967c6 Revert "[Desktop] Allow dragging window on empty titlebar (#2222)"
This reverts commit 30cd46ce1f.
2025-01-11 16:51:28 +11:00
filtered
738de3baaa Add missing electron mock API
Co-Authored-By: Chenlei Hu <hcl@comfy.org>
2025-01-11 16:51:17 +11:00
filtered
ad87080e8c Revert "[Desktop] Fix server start view layout (#2226)"
This reverts commit 44610674ee.
2025-01-11 16:50:22 +11:00
24 changed files with 387 additions and 357 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -45,6 +45,9 @@ onMounted(() => {
if (isElectron()) {
document.addEventListener('contextmenu', showContextMenu)
// Enable CSS selectors
document.documentElement.dataset['platform'] = 'electron'
}
})
</script>

View File

@@ -765,7 +765,6 @@ audio.comfy-audio.empty-audio-widget {
padding: var(--comfy-tree-explorer-item-padding) !important;
}
/* [Desktop] Electron window specific styles */
.app-drag {
app-region: drag;
}
@@ -773,8 +772,3 @@ audio.comfy-audio.empty-audio-widget {
.no-drag {
app-region: no-drag;
}
.window-actions-spacer {
width: calc(100vw - env(titlebar-area-width, 100vw));
}
/* End of [Desktop] Electron window specific styles */

View File

@@ -1,22 +1,18 @@
<template>
<div
<Button
v-show="workspaceState.focusMode"
class="comfy-menu-hamburger no-drag"
class="comfy-menu-hamburger"
:style="positionCSS"
>
<Button
icon="pi pi-bars"
severity="secondary"
text
size="large"
v-tooltip="{ value: $t('menu.showMenu'), showDelay: 300 }"
:aria-label="$t('menu.showMenu')"
aria-live="assertive"
@click="exitFocusMode"
@contextmenu="showNativeMenu"
/>
<div v-show="menuSetting !== 'Bottom'" class="window-actions-spacer" />
</div>
icon="pi pi-bars"
severity="secondary"
text
size="large"
v-tooltip="{ value: $t('menu.showMenu'), showDelay: 300 }"
:aria-label="$t('menu.showMenu')"
aria-live="assertive"
@click="exitFocusMode"
@contextmenu="showNativeMenu"
/>
</template>
<script setup lang="ts">
@@ -50,13 +46,15 @@ const positionCSS = computed<CSSProperties>(() =>
// 'Bottom' menuSetting shows the hamburger button in the bottom right corner
// 'Disabled', 'Top' menuSetting shows the hamburger button in the top right corner
menuSetting.value === 'Bottom'
? { bottom: '0px', right: '0px' }
: { top: '0px', right: '0px' }
? { bottom: '0', right: '0' }
: { top: '0', right: 'calc(100% - env(titlebar-area-width, 100%))' }
)
</script>
<style scoped>
.comfy-menu-hamburger {
@apply pointer-events-auto fixed z-[9999] flex flex-row;
pointer-events: auto;
position: fixed;
z-index: 9999;
}
</style>

View File

@@ -3,13 +3,13 @@
<div
ref="topMenuRef"
class="comfyui-menu flex items-center"
v-show="showTopMenu"
v-show="betaMenuEnabled && !workspaceState.focusMode"
:class="{ dropzone: isDropZone, 'dropzone-active': isDroppable }"
>
<h1 class="comfyui-logo mx-2 app-drag">ComfyUI</h1>
<h1 class="comfyui-logo mx-2">ComfyUI</h1>
<CommandMenubar />
<Divider layout="vertical" class="mx-2" />
<div class="flex-grow min-w-0 app-drag h-full">
<div class="flex-grow min-w-0">
<WorkflowTabs v-if="workflowTabsPosition === 'Topbar'" />
</div>
<div class="comfyui-menu-right" ref="menuRight"></div>
@@ -25,22 +25,12 @@
@click="workspaceState.focusMode = true"
@contextmenu="showNativeMenu"
/>
<div
v-show="menuSetting !== 'Bottom'"
class="window-actions-spacer flex-shrink-0"
/>
</div>
</teleport>
<!-- Virtual top menu for native window (drag handle) -->
<div
v-show="isNativeWindow && !showTopMenu"
class="fixed top-0 left-0 app-drag w-full h-[var(--comfy-topbar-height)]"
/>
</template>
<script setup lang="ts">
import { useEventBus } from '@vueuse/core'
import { useEventBus, useResizeObserver } from '@vueuse/core'
import Button from 'primevue/button'
import Divider from 'primevue/divider'
import { computed, onMounted, provide, ref } from 'vue'
@@ -59,20 +49,14 @@ const settingStore = useSettingStore()
const workflowTabsPosition = computed(() =>
settingStore.get('Comfy.Workflow.WorkflowTabsPosition')
)
const menuSetting = computed(() => settingStore.get('Comfy.UseNewMenu'))
const betaMenuEnabled = computed(() => menuSetting.value !== 'Disabled')
const betaMenuEnabled = computed(
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
)
const teleportTarget = computed(() =>
settingStore.get('Comfy.UseNewMenu') === 'Top'
? '.comfyui-body-top'
: '.comfyui-body-bottom'
)
const isNativeWindow = computed(
() =>
isElectron() && settingStore.get('Comfy-Desktop.WindowStyle') === 'custom'
)
const showTopMenu = computed(
() => betaMenuEnabled.value && !workspaceState.focusMode
)
const menuRight = ref<HTMLDivElement | null>(null)
// Menu-right holds legacy topbar elements attached by custom scripts
@@ -94,13 +78,20 @@ eventBus.on((event: string, payload: any) => {
}
})
onMounted(() => {
if (isElectron()) {
electronAPI().changeTheme({
height: topMenuRef.value.getBoundingClientRect().height
})
}
})
/** Height of titlebar on desktop. */
if (isElectron()) {
let desktopHeight = 0
useResizeObserver(topMenuRef, (entries) => {
if (settingStore.get('Comfy.UseNewMenu') !== 'Top') return
const { height } = entries[0].contentRect
if (desktopHeight === height) return
electronAPI().changeTheme({ height })
desktopHeight = height
})
}
</script>
<style scoped>
@@ -136,3 +127,30 @@ onMounted(() => {
cursor: default;
}
</style>
<style lang="postcss">
/* Desktop: Custom window styling */
:root[data-platform='electron'] {
.comfyui-logo {
@apply flex items-center gap-2 my-1 mx-1.5;
&::before {
@apply w-7 h-7 bg-[url('/assets/images/Comfy_Logo_x256.png')] bg-no-repeat bg-contain content-[''];
}
}
.comfyui-body-top {
.comfyui-menu {
app-region: drag;
padding-right: calc(100% - env(titlebar-area-width, 0));
}
}
button,
.p-menubar,
.comfyui-menu-right > *,
.actionbar {
app-region: no-drag;
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div class="workflow-tabs-container flex flex-row max-w-full">
<div class="workflow-tabs-container flex flex-row w-full">
<ScrollPanel
class="overflow-hidden no-drag"
class="overflow-hidden"
:pt:content="{
class: 'p-0 w-full',
onwheel: handleWheel
@@ -28,7 +28,7 @@
</ScrollPanel>
<Button
v-tooltip="{ value: $t('sideToolbar.newBlankWorkflow'), showDelay: 300 }"
class="new-blank-workflow-button flex-shrink-0 no-drag"
class="new-blank-workflow-button flex-shrink-0"
icon="pi pi-plus"
text
severity="secondary"

View File

@@ -386,6 +386,8 @@ export const CORE_SETTINGS: SettingParams[] = [
category: ['Comfy', 'Menu', 'UseNewMenu'],
defaultValue: 'Top',
name: 'Use new menu',
tooltip:
'(Desktop, Windows only): When using custom window style, only Top is supported',
type: 'combo',
options: ['Disabled', 'Top', 'Bottom'],
migrateDeprecatedValue: (value: string) => {

View File

@@ -1,6 +1,7 @@
import { t } from '@/i18n'
import { app } from '@/scripts/app'
import { useDialogService } from '@/services/dialogService'
import { useSettingStore } from '@/stores/settingStore'
import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
;(async () => {
@@ -39,17 +40,22 @@ import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
id: 'Comfy-Desktop.WindowStyle',
category: ['Comfy-Desktop', 'General', 'Window Style'],
name: 'Window Style',
tooltip: 'Choose custom option to hide the system title bar',
tooltip: "Custom: Replace the system title bar with ComfyUI's Top menu",
type: 'combo',
defaultValue: 'default',
options: ['default', 'custom'],
onChange: (
newValue: 'default' | 'custom',
oldValue: 'default' | 'custom'
oldValue?: 'default' | 'custom'
) => {
electronAPI.Config.setWindowStyle(newValue)
if (!oldValue) return
onChangeRestartApp(newValue, oldValue)
// Custom window mode requires the Top menu.
if (newValue === 'custom') {
useSettingStore().set('Comfy.UseNewMenu', 'Top')
}
electronAPI.Config.setWindowStyle(newValue)
}
}
],
@@ -188,4 +194,21 @@ import { electronAPI as getElectronAPI, isElectron } from '@/utils/envUtil'
}
]
})
// TODO: Replace monkey patch with API or replace UX.
// If the user changes frontend menu type, ensure custom window style is disabled.
const menuSetting = useSettingStore().settingsById['Comfy.UseNewMenu']
if (menuSetting) {
const { onChange } = menuSetting
menuSetting.onChange = (
newValue: 'Disabled' | 'Top' | 'Bottom',
oldValue?: 'Disabled' | 'Top' | 'Bottom'
) => {
const style = useSettingStore().get('Comfy-Desktop.WindowStyle')
if (oldValue === 'Top' && style === 'custom') {
useSettingStore().set('Comfy-Desktop.WindowStyle', 'default')
}
return onChange?.(newValue, oldValue)
}
}
})()

View File

@@ -7,7 +7,7 @@
},
"Comfy-Desktop_WindowStyle": {
"name": "Window Style",
"tooltip": "Choose custom option to hide the system title bar",
"tooltip": "Custom: Replace the system title bar with ComfyUI's Top menu",
"options": {
"default": "default",
"custom": "custom"
@@ -261,6 +261,7 @@
},
"Comfy_UseNewMenu": {
"name": "Use new menu",
"tooltip": "(Desktop, Windows only): When using custom window style, only Top is supported",
"options": {
"Disabled": "Disabled",
"Top": "Top",

View File

@@ -265,7 +265,8 @@
"Bottom": "Bas",
"Disabled": "Désactivé",
"Top": "Haut"
}
},
"tooltip": "(Bureau, uniquement Windows): Lors de l'utilisation d'un style de fenêtre personnalisé, seul Top est pris en charge"
},
"Comfy_Validation_NodeDefs": {
"name": "Valider les définitions de nœuds (lent)",

View File

@@ -265,7 +265,8 @@
"Bottom": "下",
"Disabled": "無効",
"Top": "上"
}
},
"tooltip": "(デスクトップ、Windowsのみ): カスタムウィンドウスタイルを使用する場合、Topのみがサポートされます"
},
"Comfy_Validation_NodeDefs": {
"name": "ノード定義を検証(遅い)",

View File

@@ -265,7 +265,8 @@
"Bottom": "하단",
"Disabled": "비활성화",
"Top": "상단"
}
},
"tooltip": "(데스크톱, 윈도우 전용): 사용자 정의 창 스타일을 사용할 때는 Top만 지원됩니다"
},
"Comfy_Validation_NodeDefs": {
"name": "노드 정의 유효성 검사 (느림)",

View File

@@ -265,7 +265,8 @@
"Bottom": "Внизу",
"Disabled": "Отключено",
"Top": "Вверху"
}
},
"tooltip": "(Рабочий стол, только для Windows): При использовании пользовательского стиля окна поддерживается только верхняя часть"
},
"Comfy_Validation_NodeDefs": {
"name": "Проверка определений узлов (медленно)",

View File

@@ -265,7 +265,8 @@
"Bottom": "底部",
"Disabled": "禁用",
"Top": "顶部"
}
},
"tooltip": "(仅限桌面Windows): 使用自定义窗口样式时,只支持顶部"
},
"Comfy_Validation_NodeDefs": {
"name": "校验节点定义(慢)",

View File

@@ -411,231 +411,223 @@ export class ComfyUI {
}
})
this.menuContainer = $el(
'div.comfy-menu.no-drag',
{ parent: containerElement },
[
$el(
'div.drag-handle.comfy-menu-header',
{
style: {
overflow: 'hidden',
position: 'relative',
width: '100%',
cursor: 'default'
}
},
[
$el('span.drag-handle'),
$el('span.comfy-menu-queue-size', {
$: (q) => (this.queueSize = q)
this.menuContainer = $el('div.comfy-menu', { parent: containerElement }, [
$el(
'div.drag-handle.comfy-menu-header',
{
style: {
overflow: 'hidden',
position: 'relative',
width: '100%',
cursor: 'default'
}
},
[
$el('span.drag-handle'),
$el('span.comfy-menu-queue-size', { $: (q) => (this.queueSize = q) }),
$el('div.comfy-menu-actions', [
$el('button.comfy-settings-btn', {
textContent: '⚙️',
onclick: () => {
useDialogService().showSettingsDialog()
}
}),
$el('div.comfy-menu-actions', [
$el('button.comfy-settings-btn', {
textContent: '⚙️',
onclick: () => {
useDialogService().showSettingsDialog()
}
}),
$el('button.comfy-close-menu-btn', {
textContent: '\u00d7',
onclick: () => {
useWorkspaceStore().focusMode = true
}
})
])
]
),
$el('button.comfy-queue-btn', {
id: 'queue-button',
textContent: 'Queue Prompt',
onclick: () => app.queuePrompt(0, this.batchCount)
}),
$el('div', {}, [
$el('label', { innerHTML: 'Extra options' }, [
$el('input', {
type: 'checkbox',
onchange: (i) => {
document.getElementById('extraOptions').style.display = i
.srcElement.checked
? 'block'
: 'none'
this.batchCount = i.srcElement.checked
? Number.parseInt(
(
document.getElementById(
'batchCountInputRange'
) as HTMLInputElement
).value
)
: 1
;(
document.getElementById(
'autoQueueCheckbox'
) as HTMLInputElement
).checked = false
this.autoQueueEnabled = false
$el('button.comfy-close-menu-btn', {
textContent: '\u00d7',
onclick: () => {
useWorkspaceStore().focusMode = true
}
})
])
]),
$el(
'div',
{ id: 'extraOptions', style: { width: '100%', display: 'none' } },
[
$el('div', [
$el('label', { innerHTML: 'Batch count' }),
$el('input', {
id: 'batchCountInputNumber',
type: 'number',
value: this.batchCount,
min: '1',
style: { width: '35%', marginLeft: '0.4em' },
oninput: (i) => {
this.batchCount = i.target.value
/* Even though an <input> element with a type of range logically represents a number (since
]
),
$el('button.comfy-queue-btn', {
id: 'queue-button',
textContent: 'Queue Prompt',
onclick: () => app.queuePrompt(0, this.batchCount)
}),
$el('div', {}, [
$el('label', { innerHTML: 'Extra options' }, [
$el('input', {
type: 'checkbox',
onchange: (i) => {
document.getElementById('extraOptions').style.display = i
.srcElement.checked
? 'block'
: 'none'
this.batchCount = i.srcElement.checked
? Number.parseInt(
(
document.getElementById(
'batchCountInputRange'
) as HTMLInputElement
).value
)
: 1
;(
document.getElementById('autoQueueCheckbox') as HTMLInputElement
).checked = false
this.autoQueueEnabled = false
}
})
])
]),
$el(
'div',
{ id: 'extraOptions', style: { width: '100%', display: 'none' } },
[
$el('div', [
$el('label', { innerHTML: 'Batch count' }),
$el('input', {
id: 'batchCountInputNumber',
type: 'number',
value: this.batchCount,
min: '1',
style: { width: '35%', marginLeft: '0.4em' },
oninput: (i) => {
this.batchCount = i.target.value
/* Even though an <input> element with a type of range logically represents a number (since
it's used for numeric input), the value it holds is still treated as a string in HTML and
JavaScript. This behavior is consistent across all <input> elements regardless of their type
(like text, number, or range), where the .value property is always a string. */
;(
document.getElementById(
'batchCountInputRange'
) as HTMLInputElement
).value = this.batchCount.toString()
}
}),
$el('input', {
id: 'batchCountInputRange',
type: 'range',
min: '1',
max: '100',
value: this.batchCount,
oninput: (i) => {
this.batchCount = i.srcElement.value
// Note
;(
document.getElementById(
'batchCountInputNumber'
) as HTMLInputElement
).value = i.srcElement.value
}
})
]),
$el('div', [
$el('label', {
for: 'autoQueueCheckbox',
innerHTML: 'Auto Queue'
}),
$el('input', {
id: 'autoQueueCheckbox',
type: 'checkbox',
checked: false,
title: 'Automatically queue prompt when the queue size hits 0',
onchange: (e) => {
this.autoQueueEnabled = e.target.checked
autoQueueModeEl.style.display = this.autoQueueEnabled
? ''
: 'none'
}
}),
autoQueueModeEl
])
]
),
$el('div.comfy-menu-btns', [
$el('button', {
id: 'queue-front-button',
textContent: 'Queue Front',
onclick: () => app.queuePrompt(-1, this.batchCount)
}),
$el('button', {
$: (b) => (this.queue.button = b as HTMLButtonElement),
id: 'comfy-view-queue-button',
textContent: 'View Queue',
onclick: () => {
this.history.hide()
this.queue.toggle()
}
}),
$el('button', {
$: (b) => (this.history.button = b as HTMLButtonElement),
id: 'comfy-view-history-button',
textContent: 'View History',
onclick: () => {
this.queue.hide()
this.history.toggle()
}
})
]),
this.queue.element,
this.history.element,
;(
document.getElementById(
'batchCountInputRange'
) as HTMLInputElement
).value = this.batchCount.toString()
}
}),
$el('input', {
id: 'batchCountInputRange',
type: 'range',
min: '1',
max: '100',
value: this.batchCount,
oninput: (i) => {
this.batchCount = i.srcElement.value
// Note
;(
document.getElementById(
'batchCountInputNumber'
) as HTMLInputElement
).value = i.srcElement.value
}
})
]),
$el('div', [
$el('label', {
for: 'autoQueueCheckbox',
innerHTML: 'Auto Queue'
}),
$el('input', {
id: 'autoQueueCheckbox',
type: 'checkbox',
checked: false,
title: 'Automatically queue prompt when the queue size hits 0',
onchange: (e) => {
this.autoQueueEnabled = e.target.checked
autoQueueModeEl.style.display = this.autoQueueEnabled
? ''
: 'none'
}
}),
autoQueueModeEl
])
]
),
$el('div.comfy-menu-btns', [
$el('button', {
id: 'comfy-save-button',
textContent: 'Save',
id: 'queue-front-button',
textContent: 'Queue Front',
onclick: () => app.queuePrompt(-1, this.batchCount)
}),
$el('button', {
$: (b) => (this.queue.button = b as HTMLButtonElement),
id: 'comfy-view-queue-button',
textContent: 'View Queue',
onclick: () => {
useCommandStore().execute('Comfy.ExportWorkflow')
this.history.hide()
this.queue.toggle()
}
}),
$el('button', {
id: 'comfy-dev-save-api-button',
textContent: 'Save (API Format)',
style: { width: '100%', display: 'none' },
$: (b) => (this.history.button = b as HTMLButtonElement),
id: 'comfy-view-history-button',
textContent: 'View History',
onclick: () => {
useCommandStore().execute('Comfy.ExportWorkflowAPI')
}
}),
$el('button', {
id: 'comfy-load-button',
textContent: 'Load',
onclick: () => fileInput.click()
}),
$el('button', {
id: 'comfy-refresh-button',
textContent: 'Refresh',
onclick: () => app.refreshComboInNodes()
}),
$el('button', {
id: 'comfy-clipspace-button',
textContent: 'Clipspace',
onclick: () => app.openClipspace()
}),
$el('button', {
id: 'comfy-clear-button',
textContent: 'Clear',
onclick: () => {
if (
!useSettingStore().get('Comfy.ConfirmClear') ||
confirm('Clear workflow?')
) {
app.clean()
app.graph.clear()
app.resetView()
api.dispatchCustomEvent('graphCleared')
}
}
}),
$el('button', {
id: 'comfy-load-default-button',
textContent: 'Load Default',
onclick: async () => {
if (
!useSettingStore().get('Comfy.ConfirmClear') ||
confirm('Load default workflow?')
) {
app.resetView()
await app.loadGraphData()
}
}
}),
$el('button', {
id: 'comfy-reset-view-button',
textContent: 'Reset View',
onclick: async () => {
app.resetView()
this.queue.hide()
this.history.toggle()
}
})
]
) as HTMLDivElement
]),
this.queue.element,
this.history.element,
$el('button', {
id: 'comfy-save-button',
textContent: 'Save',
onclick: () => {
useCommandStore().execute('Comfy.ExportWorkflow')
}
}),
$el('button', {
id: 'comfy-dev-save-api-button',
textContent: 'Save (API Format)',
style: { width: '100%', display: 'none' },
onclick: () => {
useCommandStore().execute('Comfy.ExportWorkflowAPI')
}
}),
$el('button', {
id: 'comfy-load-button',
textContent: 'Load',
onclick: () => fileInput.click()
}),
$el('button', {
id: 'comfy-refresh-button',
textContent: 'Refresh',
onclick: () => app.refreshComboInNodes()
}),
$el('button', {
id: 'comfy-clipspace-button',
textContent: 'Clipspace',
onclick: () => app.openClipspace()
}),
$el('button', {
id: 'comfy-clear-button',
textContent: 'Clear',
onclick: () => {
if (
!useSettingStore().get('Comfy.ConfirmClear') ||
confirm('Clear workflow?')
) {
app.clean()
app.graph.clear()
app.resetView()
api.dispatchCustomEvent('graphCleared')
}
}
}),
$el('button', {
id: 'comfy-load-default-button',
textContent: 'Load Default',
onclick: async () => {
if (
!useSettingStore().get('Comfy.ConfirmClear') ||
confirm('Load default workflow?')
) {
app.resetView()
await app.loadGraphData()
}
}
}),
$el('button', {
id: 'comfy-reset-view-button',
textContent: 'Reset View',
onclick: async () => {
app.resetView()
}
})
]) as HTMLDivElement
// Hide by default on construction so it does not interfere with other views.
this.menuContainer.style.display = 'none'

View File

@@ -1,7 +1,7 @@
<template>
<BaseViewTemplate>
<BaseViewTemplate class="app-drag">
<div
class="max-w-screen-sm flex flex-col gap-8 p-8 bg-[url('/assets/images/Git-Logo-White.svg')] bg-no-repeat bg-right-top bg-origin-padding"
class="no-drag max-w-screen-sm flex flex-col gap-8 p-8 bg-[url('/assets/images/Git-Logo-White.svg')] bg-no-repeat bg-right-top bg-origin-padding"
>
<!-- Header -->
<h1 class="mt-24 text-4xl font-bold text-red-500">

View File

@@ -66,11 +66,23 @@ watch(
document.body.classList.add(DARK_THEME_CLASS)
}
// Native window control theme
if (isElectron()) {
electronAPI().changeTheme({
color: 'rgba(0, 0, 0, 0)',
symbolColor: newTheme.colors.comfy_base['input-text']
})
const cssVars = newTheme.colors.comfy_base
// Allow OS to set matching hover colour
const color = setZeroAlpha(cssVars['comfy-menu-bg'])
const symbolColor = cssVars['input-text']
electronAPI().changeTheme({ color, symbolColor })
}
function setZeroAlpha(color: string) {
if (!color.startsWith('#')) return color
if (color.length === 4) {
const [_, r, g, b] = color
return `#${r}${r}${g}${g}${b}${b}00`
}
return `#${color.substring(1, 7)}00`
}
},
{ immediate: true }

View File

@@ -1,10 +1,10 @@
<template>
<BaseViewTemplate dark>
<BaseViewTemplate dark class="app-drag">
<!-- h-full to make sure the stepper does not layout shift between steps
as for each step the stepper height is different. Inherit the center element
placement from BaseViewTemplate would cause layout shift. -->
<Stepper
class="h-full p-8 2xl:p-16"
class="no-drag h-full p-8 2xl:p-16"
value="0"
@update:value="setHighestStep"
>

View File

@@ -1,8 +1,8 @@
<template>
<BaseViewTemplate dark>
<BaseViewTemplate dark class="app-drag">
<!-- Installation Path Section -->
<div
class="comfy-installer grow flex flex-col gap-4 text-neutral-300 max-w-110"
class="no-drag comfy-installer grow flex flex-col gap-4 text-neutral-300 max-w-110"
>
<h2 class="text-2xl font-semibold text-neutral-100">
{{ $t('install.manualConfiguration.title') }}

View File

@@ -1,6 +1,6 @@
<template>
<BaseViewTemplate>
<div class="sad-container">
<BaseViewTemplate class="app-drag">
<div class="no-drag sad-container">
<!-- Right side image -->
<img
class="sad-girl"

View File

@@ -1,45 +1,47 @@
<template>
<BaseViewTemplate dark class="flex-col">
<div class="flex flex-col w-full h-full items-center">
<h2 class="text-2xl font-bold">
{{ t(`serverStart.process.${status}`) }}
<span v-if="status === ProgressStatus.ERROR">
v{{ electronVersion }}
</span>
</h2>
<div
v-if="status === ProgressStatus.ERROR"
class="flex flex-col items-center gap-4"
>
<div class="flex items-center my-4 gap-2">
<Button
icon="pi pi-flag"
severity="secondary"
:label="t('serverStart.reportIssue')"
@click="reportIssue"
/>
<Button
icon="pi pi-file"
severity="secondary"
:label="t('serverStart.openLogs')"
@click="openLogs"
/>
<Button
icon="pi pi-refresh"
:label="t('serverStart.reinstall')"
@click="reinstall"
/>
</div>
<BaseViewTemplate dark class="app-drag flex-col">
<h2 class="text-2xl font-bold">
{{ t(`serverStart.process.${status}`) }}
<span v-if="status === ProgressStatus.ERROR">
v{{ electronVersion }}
</span>
</h2>
<div
v-if="status === ProgressStatus.ERROR"
class="no-drag flex flex-col items-center gap-4"
>
<div class="flex items-center my-4 gap-2">
<Button
v-if="!terminalVisible"
icon="pi pi-search"
icon="pi pi-flag"
severity="secondary"
:label="t('serverStart.showTerminal')"
@click="terminalVisible = true"
:label="t('serverStart.reportIssue')"
@click="reportIssue"
/>
<Button
icon="pi pi-file"
severity="secondary"
:label="t('serverStart.openLogs')"
@click="openLogs"
/>
<Button
icon="pi pi-refresh"
:label="t('serverStart.reinstall')"
@click="reinstall"
/>
</div>
<BaseTerminal v-show="terminalVisible" @created="terminalCreated" />
<Button
v-if="!terminalVisible"
icon="pi pi-search"
severity="secondary"
:label="t('serverStart.showTerminal')"
@click="terminalVisible = true"
/>
</div>
<BaseTerminal
v-show="terminalVisible"
class="no-drag"
@created="terminalCreated"
/>
</BaseViewTemplate>
</template>

View File

@@ -1,8 +1,8 @@
<template>
<BaseViewTemplate dark>
<BaseViewTemplate dark class="app-drag">
<main
id="comfy-user-selection"
class="min-w-84 relative rounded-lg bg-[var(--comfy-menu-bg)] p-5 px-10 shadow-lg"
class="no-drag min-w-84 relative rounded-lg bg-[var(--comfy-menu-bg)] p-5 px-10 shadow-lg"
>
<h1 class="my-2.5 mb-7 font-normal">ComfyUI</h1>
<div class="flex w-full flex-col items-center">

View File

@@ -1,6 +1,6 @@
<template>
<BaseViewTemplate dark>
<div class="flex flex-col items-center justify-center gap-8 p-8">
<BaseViewTemplate dark class="app-drag">
<div class="no-drag flex flex-col items-center justify-center gap-8 p-8">
<!-- Header -->
<h1 class="animated-gradient-text text-glow select-none">
{{ $t('welcome.title') }}

View File

@@ -1,28 +1,18 @@
<template>
<div
class="font-sans w-screen h-screen flex flex-col pointer-events-auto"
class="font-sans w-screen h-screen flex items-center justify-center pointer-events-auto overflow-auto"
:class="[
props.dark
? 'text-neutral-300 bg-neutral-900 dark-theme'
: 'text-neutral-900 bg-neutral-300'
]"
>
<!-- Virtual top menu for native window (drag handle) -->
<div
v-show="isNativeWindow"
ref="topMenuRef"
class="app-drag w-full h-[var(--comfy-topbar-height)]"
/>
<div
class="flex-grow w-full flex items-center justify-center overflow-auto"
>
<slot></slot>
</div>
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue'
import { onMounted } from 'vue'
import { electronAPI, isElectron } from '@/utils/envUtil'
@@ -36,28 +26,18 @@ const props = withDefaults(
)
const darkTheme = {
color: 'rgba(0, 0, 0, 0)',
color: 'rgba(23, 23, 23, 0)',
symbolColor: '#d4d4d4'
}
const lightTheme = {
color: 'rgba(0, 0, 0, 0)',
color: 'rgba(212, 212, 212, 0)',
symbolColor: '#171717'
}
const topMenuRef = ref<HTMLDivElement | null>(null)
const isNativeWindow = ref(false)
onMounted(async () => {
onMounted(() => {
if (isElectron()) {
const windowStyle = await electronAPI().Config.getWindowStyle()
isNativeWindow.value = windowStyle === 'custom'
await nextTick()
electronAPI().changeTheme({
...(props.dark ? darkTheme : lightTheme),
height: topMenuRef.value.getBoundingClientRect().height
})
electronAPI().changeTheme(props.dark ? darkTheme : lightTheme)
}
})
</script>