Compare commits

..

1 Commits

Author SHA1 Message Date
Hunter Senft-Grupp
e587824d84 fix: remove team workspace teaser from personal pricing table 2026-05-08 15:33:40 -04:00
10 changed files with 19 additions and 159 deletions

View File

@@ -117,17 +117,7 @@
<template #body>
<NodeDragPreview />
<div class="flex h-full flex-col">
<div
v-if="hasNoMatches"
class="flex min-h-0 flex-1 items-center justify-center px-6 py-8 text-center text-sm text-muted-foreground"
>
{{
$t('sideToolbar.nodeLibraryTab.noMatchingNodes', {
query: searchQuery
})
}}
</div>
<div v-else class="min-h-0 flex-1 overflow-y-auto py-2">
<div class="min-h-0 flex-1 overflow-y-auto py-2">
<TabPanel
v-if="flags.nodeLibraryEssentialsEnabled"
:model-value="selectedTab"
@@ -284,13 +274,9 @@ const filteredNodeDefs = computed(() => {
})
const activeNodes = computed(() =>
searchQuery.value.length === 0
? nodeDefStore.visibleNodeDefs
: filteredNodeDefs.value
)
const hasNoMatches = computed(
() => searchQuery.value.length > 0 && filteredNodeDefs.value.length === 0
filteredNodeDefs.value.length > 0
? filteredNodeDefs.value
: nodeDefStore.visibleNodeDefs
)
const sections = computed(() => {

View File

@@ -903,7 +903,6 @@
"alphabetical": "A-Z",
"alphabeticalDesc": "Sort alphabetically within groups"
},
"noMatchingNodes": "No nodes match \"{query}\"",
"sections": {
"favorites": "Bookmarks",
"favoriteNode": "Bookmark Node",
@@ -2342,8 +2341,6 @@
"plansForWorkspace": "Plans for {workspace}",
"personalWorkspace": "Personal Workspace",
"teamWorkspace": "Team Workspace",
"soloUseOnly": "Solo use only",
"needTeamWorkspace": "Need team workspace?",
"inviteUpTo": "Invite up to",
"title": "Subscription",
"titleUnsubscribed": "Subscribe to Comfy Cloud",

View File

@@ -1,52 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { mapInputFileToAssetItem } from './assetMappers'
vi.mock('@/scripts/api', () => ({
api: {
apiURL: (path: string) => `/api${path}`
}
}))
vi.mock('@/platform/distribution/cloudPreviewUtil', () => ({
appendCloudResParam: vi.fn()
}))
describe('mapInputFileToAssetItem', () => {
it('preserves a clean filename', () => {
const asset = mapInputFileToAssetItem('photo.png', 0, 'input')
expect(asset.name).toBe('photo.png')
expect(asset.id).toBe('input-0-photo.png')
expect(asset.preview_url).toBe('/api/view?filename=photo.png&type=input')
})
it.each([
['photo.png [input]', 'photo.png'],
['photo.png [output]', 'photo.png'],
['photo.png [temp]', 'photo.png'],
['clip.mp4[input]', 'clip.mp4'],
['MyFile.WEBP [Input]', 'MyFile.WEBP']
])('strips ComfyUI directory annotation: %s -> %s', (input, expectedName) => {
const asset = mapInputFileToAssetItem(input, 1, 'input')
expect(asset.name).toBe(expectedName)
expect(asset.id).toBe(`input-1-${expectedName}`)
expect(asset.preview_url).toBe(
`/api/view?filename=${encodeURIComponent(expectedName)}&type=input`
)
})
it('leaves non-annotation brackets in the filename intact', () => {
const asset = mapInputFileToAssetItem('my [draft] image.png', 0, 'input')
expect(asset.name).toBe('my [draft] image.png')
})
it('uses the directory passed in for the type query param', () => {
const asset = mapInputFileToAssetItem('clip.mp4 [output]', 0, 'output')
expect(asset.preview_url).toBe('/api/view?filename=clip.mp4&type=output')
expect(asset.tags).toEqual(['output'])
})
})

View File

@@ -51,17 +51,6 @@ export function mapTaskOutputToAssetItem(
}
}
/**
* Strips ComfyUI's trailing directory-type annotation (e.g. ` [input]`,
* ` [output]`, `[temp]`) from a filename returned by the OSS internal
* `/internal/files/{type}` endpoint. The annotation is part of the wire
* format LoadImage-style widgets expect, but for the assets sidebar we
* want the canonical on-disk filename so type detection / titles work.
*/
function stripDirectoryAnnotation(filename: string): string {
return filename.replace(/\s*\[(?:input|output|temp)\]\s*$/i, '')
}
/**
* Maps input directory file to AssetItem format
* @param filename The filename
@@ -74,14 +63,13 @@ export function mapInputFileToAssetItem(
index: number,
directory: 'input' | 'output' = 'input'
): AssetItem {
const cleanName = stripDirectoryAnnotation(filename)
const params = new URLSearchParams({ filename: cleanName, type: directory })
const params = new URLSearchParams({ filename, type: directory })
const preview_url = api.apiURL(`/view?${params}`)
appendCloudResParam(params, cleanName)
appendCloudResParam(params, filename)
return {
id: `${directory}-${index}-${cleanName}`,
name: cleanName,
id: `${directory}-${index}-${filename}`,
name: filename,
size: 0,
created_at: new Date().toISOString(),
tags: [directory],

View File

@@ -52,6 +52,9 @@ export const MODEL_NODE_MAPPINGS: ReadonlyArray<
// ---- SAM3 3D segmentation (comfyui-sam3) ----
['sam3', 'LoadSAM3Model', 'model_path'],
// ---- Ultralytics detection (comfyui-impact-subpack) ----
['ultralytics', 'UltralyticsDetectorProvider', 'model_name'],
// ---- DepthAnything (comfyui-depthanythingv2, comfyui-depthanythingv3) ----
['depthanything', 'DownloadAndLoadDepthAnythingV2Model', 'model'],
['depthanything3', 'DownloadAndLoadDepthAnythingV3Model', 'model'],

View File

@@ -151,8 +151,6 @@ const i18n = createI18n({
videoEstimateHelp: 'How is this calculated?',
videoEstimateExplanation: 'Based on average usage.',
videoEstimateTryTemplate: 'Try template',
soloUseOnly: 'Solo use only',
needTeamWorkspace: 'Need team workspace?',
maxDuration: {
standard: '30 min',
creator: '30 min',
@@ -177,9 +175,6 @@ const i18n = createI18n({
function renderComponent() {
return render(PricingTable, {
props: {
onChooseTeamWorkspace: onChooseTeamWorkspace
},
global: {
plugins: [createTestingPinia({ createSpy: vi.fn }), i18n],
components: {
@@ -210,8 +205,6 @@ function renderComponent() {
})
}
const onChooseTeamWorkspace = vi.fn()
describe('PricingTable', () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -423,20 +416,4 @@ describe('PricingTable', () => {
expect(mockAccessBillingPortal).toHaveBeenCalledWith('standard-yearly')
})
})
describe('team workspace link', () => {
it('should emit chooseTeamWorkspace when clicking "Need team workspace?" link', async () => {
renderComponent()
await flushPromises()
const teamLink = screen
.getAllByRole('button')
.find((b) => b.textContent?.includes('Need team workspace?'))
expect(teamLink).toBeDefined()
await userEvent.click(teamLink!)
expect(onChooseTeamWorkspace).toHaveBeenCalledOnce()
})
})
})

View File

@@ -95,21 +95,6 @@
</div>
</div>
<p
role="note"
:aria-label="t('subscription.soloUseOnly')"
class="m-0 flex h-10 items-center rounded-lg bg-muted-foreground/30 px-3 text-sm text-muted-foreground"
>
{{ t('subscription.soloUseOnly') }}
<span class="mx-1 text-muted-foreground"></span>
<button
class="text-primary-foreground cursor-pointer border-none bg-transparent p-0 text-sm font-medium underline hover:text-base-foreground focus-visible:ring-1 focus-visible:outline-none"
@click="emit('chooseTeamWorkspace')"
>
{{ t('subscription.needTeamWorkspace') }}
</button>
</p>
<div class="flex flex-1 flex-col gap-3 pb-0">
<div class="flex flex-row items-center justify-between">
<span
@@ -318,10 +303,6 @@ interface PricingTierConfig {
isPopular?: boolean
}
const emit = defineEmits<{
chooseTeamWorkspace: []
}>()
const { t, n } = useI18n()
const billingCycleOptions: BillingCycleOption[] = [

View File

@@ -33,7 +33,7 @@
</i18n-t>
</div>
<PricingTable class="flex-1" @choose-team-workspace="handleChooseTeam" />
<PricingTable class="flex-1" />
<!-- Contact and Enterprise Links -->
<div class="flex flex-col items-center gap-2">
@@ -159,10 +159,9 @@ import { useTelemetry } from '@/platform/telemetry'
import { useCommandStore } from '@/stores/commandStore'
import type { SubscriptionDialogReason } from '@/platform/cloud/subscription/composables/useSubscriptionDialog'
const { onClose, reason, onChooseTeam } = defineProps<{
const { onClose, reason } = defineProps<{
onClose: () => void
reason?: SubscriptionDialogReason
onChooseTeam?: () => void
}>()
const emit = defineEmits<{
@@ -203,14 +202,6 @@ const handleSubscribed = () => {
emit('close', true)
}
const handleChooseTeam = () => {
if (onChooseTeam) {
onChooseTeam()
} else {
onClose()
}
}
const handleClose = () => {
onClose()
}

View File

@@ -43,12 +43,7 @@ export const useSubscriptionDialog = () => {
import('@/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue')
)
const personalProps = {
onClose: hide,
reason: options?.reason,
onChooseTeam: () => startTeamWorkspaceUpgradeFlow()
}
const workspaceProps = {
const dialogProps = {
onClose: hide,
reason: options?.reason
}
@@ -56,7 +51,7 @@ export const useSubscriptionDialog = () => {
dialogService.showLayoutDialog({
key: DIALOG_KEY,
component,
props: useWorkspaceVariant ? workspaceProps : personalProps,
props: dialogProps,
dialogComponentProps: {
style: 'width: min(1328px, 95vw); max-height: 958px;',
pt: {

View File

@@ -31,6 +31,7 @@ const EXPECTED_DEFAULT_TYPES = [
'latent_upscale_models',
'sam2',
'sams',
'ultralytics',
'depthanything',
'ipadapter',
'segformer_b2_clothes',
@@ -83,6 +84,7 @@ const MOCK_NODE_NAMES = [
'LatentUpscaleModelLoader',
'DownloadAndLoadSAM2Model',
'SAMLoader',
'UltralyticsDetectorProvider',
'DownloadAndLoadDepthAnythingV2Model',
'IPAdapterModelLoader',
'LS_LoadSegformerModel',
@@ -258,6 +260,8 @@ describe('useModelToNodeStore', () => {
['sams', 'SAMLoader', 'model_name'],
['ipadapter', 'IPAdapterModelLoader', 'ipadapter_file'],
['depthanything', 'DownloadAndLoadDepthAnythingV2Model', 'model'],
['ultralytics/bbox', 'UltralyticsDetectorProvider', 'model_name'],
['ultralytics/segm', 'UltralyticsDetectorProvider', 'model_name'],
['FlashVSR', 'FlashVSRNode', ''],
['FlashVSR-v1.1', 'FlashVSRNode', ''],
['segformer_b2_clothes', 'LS_LoadSegformerModel', 'model_name'],
@@ -273,16 +277,6 @@ describe('useModelToNodeStore', () => {
expect(provider?.key).toBe(expectedKey)
}
)
it.each([['ultralytics'], ['ultralytics/bbox'], ['ultralytics/segm']])(
'should not register %s as a default provider, so the node falls back to its static combo (regression for #8468)',
(modelType) => {
const modelToNodeStore = useModelToNodeStore()
modelToNodeStore.registerDefaults()
expect(modelToNodeStore.getNodeProvider(modelType)).toBeUndefined()
}
)
})
describe('getAllNodeProviders', () => {