mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-03-10 15:40:24 +00:00
Merge remote-tracking branch 'origin/main' into bl-semantic-jellyfish
This commit is contained in:
@@ -10,12 +10,12 @@
|
||||
class="bg-transparent"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class="tabs-container">
|
||||
<div class="tabs-container font-inter">
|
||||
<Tab
|
||||
v-for="tab in bottomPanelStore.bottomPanelTabs"
|
||||
:key="tab.id"
|
||||
:value="tab.id"
|
||||
class="m-1 mx-2 border-none"
|
||||
class="m-1 mx-2 border-none font-inter"
|
||||
:class="{
|
||||
'tab-list-single-item':
|
||||
bottomPanelStore.bottomPanelTabs.length === 1
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Tree
|
||||
v-model:expanded-keys="expandedKeys"
|
||||
v-model:selection-keys="selectionKeys"
|
||||
class="tree-explorer px-2 py-0 2xl:px-4"
|
||||
class="tree-explorer px-2 py-0 2xl:px-4 bg-transparent"
|
||||
:class="props.class"
|
||||
:value="renderedRoot.children"
|
||||
selection-mode="single"
|
||||
|
||||
@@ -138,7 +138,6 @@ useEventListener(window, 'click', hideTooltip)
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 5px rgb(0 0 0 / 0.4);
|
||||
color: var(--input-text);
|
||||
font-family: sans-serif;
|
||||
left: 0;
|
||||
max-width: 30vw;
|
||||
padding: 4px 8px;
|
||||
|
||||
@@ -1,38 +1,52 @@
|
||||
<template>
|
||||
<div
|
||||
class="help-center-menu"
|
||||
class="help-center-menu flex flex-col items-start gap-1"
|
||||
role="menu"
|
||||
:aria-label="$t('helpCenter.helpFeedback')"
|
||||
:aria-label="$t('help.helpCenterMenu')"
|
||||
>
|
||||
<!-- Main Menu Items -->
|
||||
<nav class="help-menu-section" role="menubar">
|
||||
<button
|
||||
v-for="menuItem in menuItems"
|
||||
v-show="menuItem.visible !== false"
|
||||
:key="menuItem.key"
|
||||
type="button"
|
||||
class="help-menu-item"
|
||||
:class="{ 'more-item': menuItem.key === 'more' }"
|
||||
role="menuitem"
|
||||
@click="menuItem.action"
|
||||
@mouseenter="onMenuItemHover(menuItem.key, $event)"
|
||||
@mouseleave="onMenuItemLeave(menuItem.key)"
|
||||
>
|
||||
<div class="help-menu-icon-container">
|
||||
<div class="help-menu-icon">
|
||||
<component
|
||||
:is="menuItem.icon"
|
||||
v-if="typeof menuItem.icon === 'object'"
|
||||
:size="16"
|
||||
/>
|
||||
<i v-else :class="menuItem.icon" />
|
||||
<div class="w-full">
|
||||
<nav class="flex w-full flex-col gap-2" role="menubar">
|
||||
<button
|
||||
v-for="menuItem in menuItems"
|
||||
v-show="menuItem.visible !== false"
|
||||
:key="menuItem.key"
|
||||
type="button"
|
||||
class="help-menu-item"
|
||||
:class="{ 'more-item': menuItem.key === 'more' }"
|
||||
role="menuitem"
|
||||
@click="menuItem.action"
|
||||
@mouseenter="onMenuItemHover(menuItem.key, $event)"
|
||||
@mouseleave="onMenuItemLeave(menuItem.key)"
|
||||
>
|
||||
<div class="help-menu-icon-container">
|
||||
<div class="help-menu-icon">
|
||||
<component
|
||||
:is="menuItem.icon"
|
||||
v-if="typeof menuItem.icon === 'object'"
|
||||
:size="16"
|
||||
/>
|
||||
<i v-else :class="menuItem.icon" />
|
||||
</div>
|
||||
<div v-if="menuItem.showRedDot" class="menu-red-dot" />
|
||||
</div>
|
||||
<div v-if="menuItem.showRedDot" class="menu-red-dot" />
|
||||
</div>
|
||||
<span class="menu-label">{{ menuItem.label }}</span>
|
||||
<i v-if="menuItem.key === 'more'" class="pi pi-chevron-right" />
|
||||
</button>
|
||||
</nav>
|
||||
<span class="menu-label">{{ menuItem.label }}</span>
|
||||
<i
|
||||
v-if="menuItem.showExternalIcon"
|
||||
class="icon-[lucide--external-link] text-primary w-4 h-4 ml-auto"
|
||||
/>
|
||||
<i
|
||||
v-if="menuItem.key === 'more'"
|
||||
class="pi pi-chevron-right ml-auto"
|
||||
/>
|
||||
</button>
|
||||
</nav>
|
||||
<div
|
||||
class="flex h-4 flex-col items-center justify-between self-stretch p-2"
|
||||
>
|
||||
<div class="w-full border-b border-interface-menu-stroke" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- More Submenu -->
|
||||
<Teleport to="body">
|
||||
@@ -68,26 +82,34 @@
|
||||
</Teleport>
|
||||
|
||||
<!-- What's New Section -->
|
||||
<section v-if="showVersionUpdates" class="whats-new-section">
|
||||
<h3 class="section-description">{{ $t('helpCenter.whatsNew') }}</h3>
|
||||
<section
|
||||
v-if="showVersionUpdates"
|
||||
class="w-full"
|
||||
data-testid="whats-new-section"
|
||||
>
|
||||
<h3
|
||||
class="section-description flex items-center gap-2.5 self-stretch px-8 pt-2 pb-2"
|
||||
>
|
||||
{{ $t('helpCenter.whatsNew') }}
|
||||
</h3>
|
||||
|
||||
<!-- Release Items -->
|
||||
<div
|
||||
v-if="hasReleases"
|
||||
role="group"
|
||||
:aria-label="$t('helpCenter.recentReleases')"
|
||||
:aria-label="$t('help.recentReleases')"
|
||||
>
|
||||
<article
|
||||
v-for="release in releaseStore.recentReleases"
|
||||
:key="release.id || release.version"
|
||||
class="help-menu-item release-menu-item"
|
||||
class="release-menu-item flex h-12 min-h-6 cursor-pointer items-center gap-2 self-stretch rounded p-2 transition-colors hover:bg-interface-menu-component-surface-hovered"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="onReleaseClick(release)"
|
||||
@keydown.enter="onReleaseClick(release)"
|
||||
@keydown.space.prevent="onReleaseClick(release)"
|
||||
>
|
||||
<i class="pi pi-refresh help-menu-icon" aria-hidden="true" />
|
||||
<i class="help-menu-icon icon-[lucide--package]" aria-hidden="true" />
|
||||
<div class="release-content">
|
||||
<span class="release-title">
|
||||
{{
|
||||
@@ -106,13 +128,6 @@
|
||||
</span>
|
||||
</time>
|
||||
</div>
|
||||
<Button
|
||||
v-if="shouldShowUpdateButton(release)"
|
||||
:label="$t('helpCenter.updateAvailable')"
|
||||
size="small"
|
||||
class="update-button"
|
||||
@click.stop="onUpdate(release)"
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -137,7 +152,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Button from 'primevue/button'
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import type { CSSProperties, Component } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -166,6 +180,7 @@ interface MenuItem {
|
||||
type?: 'item' | 'divider'
|
||||
items?: MenuItem[]
|
||||
showRedDot?: boolean
|
||||
showExternalIcon?: boolean
|
||||
}
|
||||
|
||||
// Constants
|
||||
@@ -192,6 +207,9 @@ const commandStore = useCommandStore()
|
||||
const settingStore = useSettingStore()
|
||||
const telemetry = useTelemetry()
|
||||
|
||||
// Track when help center was opened
|
||||
const openedAt = ref(Date.now())
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
@@ -202,7 +220,6 @@ const isSubmenuVisible = ref(false)
|
||||
const submenuRef = ref<HTMLElement | null>(null)
|
||||
const submenuStyle = ref<CSSProperties>({})
|
||||
let hoverTimeout: number | null = null
|
||||
const openedAt = ref<number>(Date.now())
|
||||
|
||||
// Computed
|
||||
const hasReleases = computed(() => releaseStore.releases.length > 0)
|
||||
@@ -273,11 +290,34 @@ const moreMenuItem = computed(() =>
|
||||
|
||||
const menuItems = computed<MenuItem[]>(() => {
|
||||
const items: MenuItem[] = [
|
||||
{
|
||||
key: 'feedback',
|
||||
type: 'item',
|
||||
icon: 'icon-[lucide--clipboard-pen]',
|
||||
label: t('helpCenter.feedback'),
|
||||
action: () => {
|
||||
trackResourceClick('help_feedback', false)
|
||||
void commandStore.execute('Comfy.ContactSupport')
|
||||
emit('close')
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'help',
|
||||
type: 'item',
|
||||
icon: 'icon-[lucide--message-circle-question]',
|
||||
label: t('helpCenter.help'),
|
||||
action: () => {
|
||||
trackResourceClick('help_feedback', false)
|
||||
void commandStore.execute('Comfy.ContactSupport')
|
||||
emit('close')
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'docs',
|
||||
type: 'item',
|
||||
icon: 'pi pi-book',
|
||||
icon: 'icon-[lucide--book-open]',
|
||||
label: t('helpCenter.docs'),
|
||||
showExternalIcon: true,
|
||||
action: () => {
|
||||
trackResourceClick('docs', true)
|
||||
const path = isCloud ? '/get_started/cloud' : '/'
|
||||
@@ -290,6 +330,7 @@ const menuItems = computed<MenuItem[]>(() => {
|
||||
type: 'item',
|
||||
icon: 'pi pi-discord',
|
||||
label: 'Discord',
|
||||
showExternalIcon: true,
|
||||
action: () => {
|
||||
trackResourceClick('discord', true)
|
||||
openExternalLink(staticUrls.discord)
|
||||
@@ -299,24 +340,14 @@ const menuItems = computed<MenuItem[]>(() => {
|
||||
{
|
||||
key: 'github',
|
||||
type: 'item',
|
||||
icon: 'pi pi-github',
|
||||
icon: 'icon-[lucide--github]',
|
||||
label: t('helpCenter.github'),
|
||||
showExternalIcon: true,
|
||||
action: () => {
|
||||
trackResourceClick('github', true)
|
||||
openExternalLink(staticUrls.github)
|
||||
emit('close')
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'help',
|
||||
type: 'item',
|
||||
icon: 'pi pi-question-circle',
|
||||
label: t('helpCenter.helpFeedback'),
|
||||
action: () => {
|
||||
trackResourceClick('help_feedback', false)
|
||||
void commandStore.execute('Comfy.ContactSupport')
|
||||
emit('close')
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -438,32 +469,22 @@ const formatReleaseDate = (dateString?: string): string => {
|
||||
const diffTime = Math.abs(now.getTime() - date.getTime())
|
||||
|
||||
const timeUnits = [
|
||||
{ unit: TIME_UNITS.YEAR, suffix: 'y' },
|
||||
{ unit: TIME_UNITS.MONTH, suffix: 'mo' },
|
||||
{ unit: TIME_UNITS.WEEK, suffix: 'w' },
|
||||
{ unit: TIME_UNITS.DAY, suffix: 'd' },
|
||||
{ unit: TIME_UNITS.HOUR, suffix: 'h' },
|
||||
{ unit: TIME_UNITS.MINUTE, suffix: 'min' }
|
||||
{ unit: TIME_UNITS.YEAR, key: 'yearsAgo' },
|
||||
{ unit: TIME_UNITS.MONTH, key: 'monthsAgo' },
|
||||
{ unit: TIME_UNITS.WEEK, key: 'weeksAgo' },
|
||||
{ unit: TIME_UNITS.DAY, key: 'daysAgo' },
|
||||
{ unit: TIME_UNITS.HOUR, key: 'hoursAgo' },
|
||||
{ unit: TIME_UNITS.MINUTE, key: 'minutesAgo' }
|
||||
]
|
||||
|
||||
for (const { unit, suffix } of timeUnits) {
|
||||
for (const { unit, key } of timeUnits) {
|
||||
const value = Math.floor(diffTime / unit)
|
||||
if (value > 0) {
|
||||
return `${value}${suffix} ago`
|
||||
return t(`g.relativeTime.${key}`, { count: value })
|
||||
}
|
||||
}
|
||||
|
||||
return 'now'
|
||||
}
|
||||
|
||||
const shouldShowUpdateButton = (release: ReleaseNote): boolean => {
|
||||
// Hide update buttons in cloud distribution
|
||||
if (isCloud) return false
|
||||
|
||||
return (
|
||||
releaseStore.shouldShowUpdateButton &&
|
||||
release === releaseStore.recentReleases[0]
|
||||
)
|
||||
return t('g.relativeTime.now')
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
@@ -533,14 +554,6 @@ const onReleaseClick = (release: ReleaseNote): void => {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
const onUpdate = (_: ReleaseNote): void => {
|
||||
trackResourceClick('docs', true)
|
||||
openExternalLink(
|
||||
buildDocsUrl('/installation/update_comfyui', { includeLocale: true })
|
||||
)
|
||||
emit('close')
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
telemetry?.trackHelpCenterOpened({ source: 'sidebar' })
|
||||
@@ -557,38 +570,37 @@ onBeforeUnmount(() => {
|
||||
|
||||
<style scoped>
|
||||
.help-center-menu {
|
||||
width: 380px;
|
||||
width: 256px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
background: var(--p-content-background);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgb(0 0 0 / 0.15);
|
||||
border: 1px solid var(--p-content-border-color);
|
||||
backdrop-filter: blur(8px);
|
||||
background: var(--interface-menu-surface);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 0.1);
|
||||
border: 1px solid var(--interface-menu-stroke);
|
||||
padding: 12px 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.help-menu-section {
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid var(--p-content-border-color);
|
||||
}
|
||||
|
||||
.help-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
height: 32px;
|
||||
min-height: 24px;
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
font-size: 0.9rem;
|
||||
color: inherit;
|
||||
color: var(--text-primary);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.help-menu-item:hover {
|
||||
background-color: #007aff26;
|
||||
background-color: var(--interface-menu-component-surface-hovered);
|
||||
}
|
||||
|
||||
.help-menu-item:focus,
|
||||
@@ -599,16 +611,16 @@ onBeforeUnmount(() => {
|
||||
|
||||
.help-menu-icon-container {
|
||||
position: relative;
|
||||
margin-right: 0.75rem;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.help-menu-icon {
|
||||
margin-right: 0.75rem;
|
||||
font-size: 1rem;
|
||||
color: var(--p-text-muted-color);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -616,7 +628,9 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.help-menu-icon svg {
|
||||
color: var(--p-text-muted-color);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.menu-red-dot {
|
||||
@@ -639,16 +653,14 @@ onBeforeUnmount(() => {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.whats-new-section {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--p-text-muted-color);
|
||||
margin: 0 0 0.5rem;
|
||||
padding: 0 1rem;
|
||||
color: var(--text-secondary);
|
||||
font-family: var(--font-inter);
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
@@ -661,7 +673,7 @@ onBeforeUnmount(() => {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -669,12 +681,22 @@ onBeforeUnmount(() => {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.2;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.release-date {
|
||||
height: 16px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--p-text-muted-color);
|
||||
color: var(--text-secondary);
|
||||
font-family: var(--font-inter);
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.release-date .hover-state {
|
||||
@@ -691,35 +713,31 @@ onBeforeUnmount(() => {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.update-button {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Submenu Styles */
|
||||
.more-submenu {
|
||||
width: 210px;
|
||||
padding: 0.5rem 0;
|
||||
background: var(--p-content-background);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--p-content-border-color);
|
||||
box-shadow: 0 8px 32px rgb(0 0 0 / 0.15);
|
||||
padding: 12px 8px;
|
||||
background: var(--interface-menu-surface);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--interface-menu-stroke);
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 0.1);
|
||||
overflow: hidden;
|
||||
transition: opacity 0.15s ease-out;
|
||||
}
|
||||
|
||||
.submenu-item {
|
||||
padding: 0.75rem 1rem;
|
||||
color: inherit;
|
||||
padding: 8px;
|
||||
height: 32px;
|
||||
min-height: 24px;
|
||||
border-radius: 4px;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.submenu-item:hover {
|
||||
background-color: #007aff26;
|
||||
background-color: var(--interface-menu-component-surface-hovered);
|
||||
}
|
||||
|
||||
.submenu-item:focus,
|
||||
@@ -730,8 +748,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
.submenu-divider {
|
||||
height: 1px;
|
||||
background: #3e3e3e;
|
||||
margin: 0.5rem 0;
|
||||
background: var(--interface-menu-stroke);
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* Scrollbar Styling */
|
||||
@@ -744,12 +762,12 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.help-center-menu::-webkit-scrollbar-thumb {
|
||||
background: var(--p-content-border-color);
|
||||
background: var(--interface-menu-stroke);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.help-center-menu::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--p-text-muted-color);
|
||||
background: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Reduced Motion */
|
||||
|
||||
@@ -200,7 +200,6 @@ const truncateDefaultValue = (value: any, charLimit: number = 32): string => {
|
||||
}
|
||||
|
||||
._sb_node_preview {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
color: var(--descrip-text);
|
||||
border: 1px solid var(--descrip-text);
|
||||
min-width: 300px;
|
||||
|
||||
@@ -182,7 +182,7 @@ function handleTitleCancel() {
|
||||
<Tab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
class="text-sm py-1 px-2"
|
||||
class="text-sm py-1 px-2 font-inter"
|
||||
:value="tab.value"
|
||||
>
|
||||
{{ tab.label() }}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex h-full flex-col bg-interface-panel-surface"
|
||||
:class="props.class"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
v-if="slots.top"
|
||||
class="flex min-h-12 items-center border-b border-interface-stroke px-4 py-2"
|
||||
>
|
||||
<slot name="top" />
|
||||
</div>
|
||||
<div v-if="slots.header" class="px-4 pb-4">
|
||||
<slot name="header" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- min-h-0 to force scrollpanel to grow -->
|
||||
<ScrollPanel class="min-h-0 grow">
|
||||
<slot name="body" />
|
||||
</ScrollPanel>
|
||||
<div v-if="slots.footer">
|
||||
<slot name="footer" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ScrollPanel from 'primevue/scrollpanel'
|
||||
import { useSlots } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: string
|
||||
}>()
|
||||
|
||||
const slots = useSlots()
|
||||
</script>
|
||||
@@ -1,19 +1,21 @@
|
||||
<template>
|
||||
<AssetsSidebarTemplate>
|
||||
<template #top>
|
||||
<span v-if="!isInFolderView" class="font-bold">
|
||||
{{ $t('sideToolbar.mediaAssets.title') }}
|
||||
</span>
|
||||
<div v-else class="flex w-full items-center justify-between gap-2">
|
||||
<SidebarTabTemplate
|
||||
:title="isInFolderView ? '' : $t('sideToolbar.mediaAssets.title')"
|
||||
>
|
||||
<template #alt-title>
|
||||
<div
|
||||
v-if="isInFolderView"
|
||||
class="flex w-full items-center justify-between gap-2"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="font-bold">{{ $t('Job ID') }}:</span>
|
||||
<span class="font-bold">{{ $t('assetBrowser.jobId') }}:</span>
|
||||
<span class="text-sm">{{ folderPromptId?.substring(0, 8) }}</span>
|
||||
<button
|
||||
class="m-0 cursor-pointer border-0 bg-transparent p-0 outline-0"
|
||||
role="button"
|
||||
@click="copyJobId"
|
||||
>
|
||||
<i class="mb-1 icon-[lucide--copy] text-sm"></i>
|
||||
<i class="icon-[lucide--copy] text-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
@@ -23,7 +25,7 @@
|
||||
</template>
|
||||
<template #header>
|
||||
<!-- Job Detail View Header -->
|
||||
<div v-if="isInFolderView" class="pt-4 pb-2">
|
||||
<div v-if="isInFolderView" class="px-2 2xl:px-4">
|
||||
<IconTextButton
|
||||
:label="$t('sideToolbar.backToAssets')"
|
||||
type="secondary"
|
||||
@@ -35,15 +37,20 @@
|
||||
</IconTextButton>
|
||||
</div>
|
||||
<!-- Normal Tab View -->
|
||||
<TabList v-else v-model="activeTab" class="pt-4 pb-1">
|
||||
<Tab value="output">{{ $t('sideToolbar.labels.generated') }}</Tab>
|
||||
<Tab value="input">{{ $t('sideToolbar.labels.imported') }}</Tab>
|
||||
<TabList v-else v-model="activeTab" class="font-inter px-2 2xl:px-4">
|
||||
<Tab class="font-inter" value="output">{{
|
||||
$t('sideToolbar.labels.generated')
|
||||
}}</Tab>
|
||||
<Tab class="font-inter" value="input">{{
|
||||
$t('sideToolbar.labels.imported')
|
||||
}}</Tab>
|
||||
</TabList>
|
||||
<!-- Filter Bar -->
|
||||
<MediaAssetFilterBar
|
||||
v-model:search-query="searchQuery"
|
||||
v-model:sort-by="sortBy"
|
||||
v-model:media-type-filters="mediaTypeFilters"
|
||||
class="pb-1 px-2 2xl:px-4"
|
||||
:show-generation-time-sort="activeTab === 'output'"
|
||||
/>
|
||||
</template>
|
||||
@@ -158,7 +165,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AssetsSidebarTemplate>
|
||||
</SidebarTabTemplate>
|
||||
<ResultGallery
|
||||
v-model:active-index="galleryActiveIndex"
|
||||
:all-gallery-items="galleryItems"
|
||||
@@ -177,6 +184,7 @@ import TextButton from '@/components/button/TextButton.vue'
|
||||
import NoResultsPlaceholder from '@/components/common/NoResultsPlaceholder.vue'
|
||||
import VirtualGrid from '@/components/common/VirtualGrid.vue'
|
||||
import Load3dViewerContent from '@/components/load3d/Load3dViewerContent.vue'
|
||||
import SidebarTabTemplate from '@/components/sidebar/tabs/SidebarTabTemplate.vue'
|
||||
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
|
||||
import Tab from '@/components/tab/Tab.vue'
|
||||
import TabList from '@/components/tab/TabList.vue'
|
||||
@@ -194,8 +202,6 @@ import { useDialogStore } from '@/stores/dialogStore'
|
||||
import { ResultItemImpl } from '@/stores/queueStore'
|
||||
import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
|
||||
|
||||
import AssetsSidebarTemplate from './AssetSidebarTemplate.vue'
|
||||
|
||||
const activeTab = ref<'input' | 'output'>('output')
|
||||
const folderPromptId = ref<string | null>(null)
|
||||
const folderExecutionTime = ref<number | undefined>(undefined)
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<SidebarTabTemplate
|
||||
:title="$t('sideToolbar.modelLibrary')"
|
||||
class="bg-(--p-tree-background)"
|
||||
>
|
||||
<SidebarTabTemplate :title="$t('sideToolbar.modelLibrary')">
|
||||
<template #tool-buttons>
|
||||
<Button
|
||||
v-tooltip.bottom="$t('g.refresh')"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
<SidebarTabTemplate
|
||||
v-if="!isHelpOpen"
|
||||
:title="$t('sideToolbar.nodeLibrary')"
|
||||
class="bg-(--p-tree-background)"
|
||||
>
|
||||
<template #tool-buttons>
|
||||
<Button
|
||||
|
||||
@@ -3,12 +3,15 @@
|
||||
class="comfy-vue-side-bar-container group/sidebar-tab flex h-full flex-col"
|
||||
:class="props.class"
|
||||
>
|
||||
<div class="comfy-vue-side-bar-header">
|
||||
<Toolbar class="min-h-8 rounded-none border-x-0 border-t-0 px-2 py-1">
|
||||
<div class="comfy-vue-side-bar-header flex flex-col gap-2">
|
||||
<Toolbar
|
||||
class="min-h-15.5 bg-transparent rounded-none border-x-0 border-t-0 px-2 2xl:px-4"
|
||||
>
|
||||
<template #start>
|
||||
<span class="truncate text-xs 2xl:text-sm" :title="props.title">
|
||||
{{ props.title.toUpperCase() }}
|
||||
<span class="truncate font-bold" :title="props.title">
|
||||
{{ props.title }}
|
||||
</span>
|
||||
<slot name="alt-title" />
|
||||
</template>
|
||||
<template #end>
|
||||
<div
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<SidebarTabTemplate
|
||||
:title="$t('sideToolbar.workflows')"
|
||||
class="workflows-sidebar-tab bg-(--p-tree-background)"
|
||||
class="workflows-sidebar-tab"
|
||||
>
|
||||
<template #tool-buttons>
|
||||
<Button
|
||||
|
||||
@@ -49,7 +49,6 @@ const modelDef = props.modelDef
|
||||
<style scoped>
|
||||
.model_preview {
|
||||
background-color: var(--comfy-menu-bg);
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
color: var(--descrip-text);
|
||||
border: 1px solid var(--descrip-text);
|
||||
min-width: 300px;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex h-full flex-col overflow-auto bg-(--p-tree-background)">
|
||||
<div class="flex h-full flex-col overflow-auto">
|
||||
<div
|
||||
class="flex items-center border-b border-(--p-divider-color) px-3 py-2"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user