Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ecab4c8
feat(assets): add ModelInfoPanel for asset browser right panel
DrJKL Jan 16, 2026
213774e
Add tests for ModelInfoPanel and assetMetadataUtils
DrJKL Jan 16, 2026
8ae753a
refactor(BaseModalLayout): convert right panel state to defineModel
DrJKL Jan 16, 2026
1204eaa
refactor: improve modal layout and add class prop to PropertiesAccord…
DrJKL Jan 16, 2026
3f9ccce
style: add border separators to ModelInfoPanel accordion items
DrJKL Jan 16, 2026
409a492
style(ModelInfoPanel): improve source link styling with Civitai logo
DrJKL Jan 16, 2026
02b6feb
fix: update asset metadata to use correct user_metadata fields
DrJKL Jan 16, 2026
ae1c319
feat(ModelInfoPanel): use TagsInput for Compatible Base Models
DrJKL Jan 16, 2026
a09ce02
feat(ModelInfoPanel): use TagsInput for Additional Tags
DrJKL Jan 16, 2026
3eff550
feat(assetsStore): add updateAssetMetadata with optimistic cache update
DrJKL Jan 16, 2026
004580a
feat: add editable Model Type select to ModelInfoPanel
DrJKL Jan 17, 2026
5a26072
fix: show collapse button in AssetBrowserModal right panel header
DrJKL Jan 17, 2026
77c1ecc
feat: add slide-in animation for right panel in BaseModalLayout
DrJKL Jan 17, 2026
206a3b6
feat: add editable Description field to model user_metadata
DrJKL Jan 17, 2026
0ed66dd
refactor: use computed properties in ModelInfoPanel
DrJKL Jan 17, 2026
8fbf8be
fix: handle optional created_at in asset schema
DrJKL Jan 17, 2026
9f24174
Knip fix
DrJKL Jan 17, 2026
6225559
Fix the color of the accordion header.
DrJKL Jan 17, 2026
4c6b2c3
fix: clear focusedAsset when side panel is closed via button
DrJKL Jan 17, 2026
356e101
fix: prevent panel close when interacting with select dropdown
DrJKL Jan 17, 2026
92d27e2
fix: prevent Escape key in TagsInput from closing parent modal
DrJKL Jan 17, 2026
a974a54
fix: blur textarea on Escape
DrJKL Jan 17, 2026
9403a65
Escape when in the main modal closes the modal
DrJKL Jan 17, 2026
f04d79a
feat: make display name editable in ModelInfoPanel
DrJKL Jan 17, 2026
9fb1e8e
fix: update display name optimistically from pendingUpdates
DrJKL Jan 17, 2026
ea2080c
feat: use Select component in ModelInfoPanel for model type selection
DrJKL Jan 17, 2026
301dd9e
Non-muted text for editable Display Name
DrJKL Jan 17, 2026
4d61ce2
feat: replace double-click with Use button on Asset Cards
DrJKL Jan 17, 2026
1e469b8
Only color the display name when it is editable.
DrJKL Jan 17, 2026
8aa27f7
fix: update ModelInfoPanel tests to use correct field names and real …
DrJKL Jan 17, 2026
e1f0393
fix: use keydown.escape in NodeHeader test to match component handler
DrJKL Jan 17, 2026
95fd72b
fix: Consistent keydown use for EditableText events.
DrJKL Jan 17, 2026
0a233a7
refactor: remove unused fade transition CSS from BaseModalLayout
DrJKL Jan 17, 2026
ef2254a
refactor: restructure BaseModalLayout from flexbox to CSS Grid
DrJKL Jan 17, 2026
4ff3158
fix: add min-w-72 to right panel inner div for proper animation
DrJKL Jan 17, 2026
78ef2c0
fix: use fixed rem units for grid columns to enable animation
DrJKL Jan 17, 2026
0df6013
fix: ensure left panel background fills full height in BaseModalLayout
DrJKL Jan 18, 2026
ff49060
feat: control right panel via button instead of asset focus
DrJKL Jan 18, 2026
8e5b73c
feat: add placeholder content to right panel when no asset selected
DrJKL Jan 18, 2026
cf62a13
fix: use toBeInViewport for clipped nav visibility check in templates…
DrJKL Jan 19, 2026
948cd1c
Add empty and immutable description, set cursor to not-allowed
DrJKL Jan 19, 2026
38b58b4
refactor: remove asset rename functionality from AssetCard
DrJKL Jan 19, 2026
3a1ec15
test: add unit tests for getAssetBaseModels, getAssetModelType, getAs…
DrJKL Jan 19, 2026
5fc713f
test: parameterize remaining assetMetadataUtils tests with it.for
DrJKL Jan 19, 2026
bdbc0c2
fix: add accessible labels to icon-only buttons in BaseModalLayout
DrJKL Jan 19, 2026
dbe559f
Consistent return type (Promise<void>)
DrJKL Jan 19, 2026
0204cb0
feat: add info button to asset cards to open model info panel
DrJKL Jan 20, 2026
9a00fc4
fix: handle base_models as array in asset filtering
DrJKL Jan 20, 2026
d291273
feat: add All vs Imported navigation to AssetBrowserModal
DrJKL Jan 20, 2026
c9bfc16
Update icon and i18n function
DrJKL Jan 20, 2026
6f08fd4
fix: test expectation
DrJKL Jan 20, 2026
6999679
fix: revert i18n change to use direct import from @/i18n
DrJKL Jan 20, 2026
5626b35
fix: Mark comfy-api-plugin as build-time only
DrJKL Jan 20, 2026
de1db1a
fix(test): i18n plugin instead of global mock
DrJKL Jan 20, 2026
e21edcd
Update src/components/widget/layout/BaseModalLayout.vue
DrJKL Jan 20, 2026
1d98d49
cleanup: Use a real SortOption, fix the type to require it.
DrJKL Jan 20, 2026
31d2941
Change NavId to a hinted string
DrJKL Jan 20, 2026
12a49ca
Update src/platform/assets/components/AssetFilterBar.test.ts
DrJKL Jan 20, 2026
dc44184
Update src/components/widget/layout/BaseModalLayout.vue
DrJKL Jan 20, 2026
b8fda31
a11y: Add aria-label for model info button
DrJKL Jan 20, 2026
ad5cd4a
fix: Bad coderabbit commit
DrJKL Jan 20, 2026
9448d4d
feat: Add user editable fields to search
DrJKL Jan 20, 2026
9d1cba7
fix: use display name for asset name sorting
DrJKL Jan 20, 2026
bf73f0d
feat: add session download tracking to assetDownloadStore
DrJKL Jan 20, 2026
d602b5a
feat: add badge prop to NavItemData interface
DrJKL Jan 20, 2026
177893b
feat: add badge rendering to NavItem component
DrJKL Jan 20, 2026
7c776d8
feat: pass badge prop to NavItem in LeftSidePanel
DrJKL Jan 20, 2026
6871930
feat: wire up session download count badge to Imported nav item
DrJKL Jan 20, 2026
6e94a2d
test: add session download tracking tests
DrJKL Jan 20, 2026
0d25d6d
[automated] Apply ESLint and Prettier fixes
actions-user Jan 20, 2026
d22b7d6
refactor: use production types with type guard in useAssetBrowser tests
DrJKL Jan 21, 2026
d87e547
feat: implement progressive pagination for Asset Browser model assets
DrJKL Jan 18, 2026
a0d4e38
fix: add defensive checks for undefined assets in useAssetWidgetData
DrJKL Jan 18, 2026
d36f045
fix: remove unused cn import from NavItem
DrJKL Jan 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions browser_tests/tests/templates.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,7 @@ test.describe('Templates', () => {
const templateGrid = comfyPage.page.locator(
'[data-testid="template-workflows-content"]'
)
const nav = comfyPage.page
.locator('header')
.filter({ hasText: 'Templates' })
const nav = comfyPage.page.locator('header', { hasText: 'Templates' })

await comfyPage.templates.waitForMinimumCardCount(1)
await expect(templateGrid).toBeVisible()
Expand All @@ -201,7 +199,8 @@ test.describe('Templates', () => {
await comfyPage.page.setViewportSize(mobileSize)
await comfyPage.templates.waitForMinimumCardCount(1)
await expect(templateGrid).toBeVisible()
await expect(nav).not.toBeVisible() // Nav should collapse at mobile size
// Nav header is clipped by overflow-hidden parent at mobile size
await expect(nav).not.toBeInViewport()

const tabletSize = { width: 1024, height: 800 }
await comfyPage.page.setViewportSize(tabletSize)
Expand Down
1 change: 1 addition & 0 deletions build/plugins/comfyAPIPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ function getModuleName(id: string): string {
export function comfyAPIPlugin(isDev: boolean): Plugin {
return {
name: 'comfy-api-plugin',
apply: 'build',
transform(code: string, id: string) {
if (isDev) return null

Expand Down
2 changes: 1 addition & 1 deletion build/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"noEmit": true,
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
Expand Down
4 changes: 4 additions & 0 deletions docs/testing/vitest-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ describe('MyStore', () => {

**Why `stubActions: false`?** By default, testing pinia stubs all actions. Set to `false` when testing actual store behavior.

## i18n in Component Tests

Use real `createI18n` with empty messages instead of mocking `vue-i18n`. See `SearchBox.test.ts` for example.

Comment on lines +33 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding a brief code example for the i18n testing pattern.

The recommendation to use real createI18n with empty messages is good guidance, but a minimal code example would help developers understand the setup quickly without needing to look up SearchBox.test.ts.

📚 Suggested addition
 ## i18n in Component Tests
 
-Use real `createI18n` with empty messages instead of mocking `vue-i18n`. See `SearchBox.test.ts` for example.
+Use real `createI18n` with empty messages instead of mocking `vue-i18n`:
+
+```typescript
+import { createI18n } from 'vue-i18n'
+
+const i18n = createI18n({
+  legacy: false,
+  locale: 'en',
+  messages: { en: {} }
+})
+
+// In mount options:
+mount(MyComponent, {
+  global: { plugins: [i18n] }
+})
+```
+
+See `SearchBox.test.ts` for a complete example.
🤖 Prompt for AI Agents
In `@docs/testing/vitest-patterns.md` around lines 33 - 36, Add a minimal code
example showing how to create a real i18n instance and pass it to component
mounts: demonstrate using createI18n to build an i18n object (e.g., with
legacy:false, locale:'en', messages:{en:{}}), assign it to a variable like i18n
and show mounting a component (e.g., mount(MyComponent, { global: { plugins:
[i18n] } })); reference SearchBox.test.ts as the full example. Keep it concise
and place it under the "i18n in Component Tests" section.

## Mock Patterns

### Reset all mocks at once
Expand Down
10 changes: 5 additions & 5 deletions src/components/common/EditableText.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('EditableText', () => {
isEditing: true
})
await wrapper.findComponent(InputText).setValue('New Text')
await wrapper.findComponent(InputText).trigger('keyup.enter')
await wrapper.findComponent(InputText).trigger('keydown.enter')
// Blur event should have been triggered
expect(wrapper.findComponent(InputText).element).not.toBe(
document.activeElement
Expand Down Expand Up @@ -79,7 +79,7 @@ describe('EditableText', () => {
await wrapper.findComponent(InputText).setValue('Modified Text')

// Press escape
await wrapper.findComponent(InputText).trigger('keyup.escape')
await wrapper.findComponent(InputText).trigger('keydown.escape')

// Should emit cancel event
expect(wrapper.emitted('cancel')).toBeTruthy()
Expand All @@ -103,7 +103,7 @@ describe('EditableText', () => {
await wrapper.findComponent(InputText).setValue('Modified Text')

// Press escape (which triggers blur internally)
await wrapper.findComponent(InputText).trigger('keyup.escape')
await wrapper.findComponent(InputText).trigger('keydown.escape')

// Manually trigger blur to simulate the blur that happens after escape
await wrapper.findComponent(InputText).trigger('blur')
Expand All @@ -120,7 +120,7 @@ describe('EditableText', () => {
isEditing: true
})
await enterWrapper.findComponent(InputText).setValue('Saved Text')
await enterWrapper.findComponent(InputText).trigger('keyup.enter')
await enterWrapper.findComponent(InputText).trigger('keydown.enter')
// Trigger blur that happens after enter
await enterWrapper.findComponent(InputText).trigger('blur')
expect(enterWrapper.emitted('edit')).toBeTruthy()
Expand All @@ -133,7 +133,7 @@ describe('EditableText', () => {
isEditing: true
})
await escapeWrapper.findComponent(InputText).setValue('Cancelled Text')
await escapeWrapper.findComponent(InputText).trigger('keyup.escape')
await escapeWrapper.findComponent(InputText).trigger('keydown.escape')
expect(escapeWrapper.emitted('cancel')).toBeTruthy()
expect(escapeWrapper.emitted('edit')).toBeFalsy()
})
Expand Down
6 changes: 3 additions & 3 deletions src/components/common/EditableText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<span v-if="!isEditing">
{{ modelValue }}
</span>
<!-- Avoid double triggering finishEditing event when keyup.enter is triggered -->
<!-- Avoid double triggering finishEditing event when keydown.enter is triggered -->
<InputText
v-else
ref="inputRef"
Expand All @@ -18,8 +18,8 @@
...inputAttrs
}
}"
@keyup.enter.capture.stop="blurInputElement"
@keyup.escape.stop="cancelEditing"
@keydown.enter.capture.stop="blurInputElement"
@keydown.escape.capture.stop="cancelEditing"
@click.stop
@contextmenu.stop
@pointerdown.stop.capture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,32 @@ import { cn } from '@/utils/tailwindUtil'

import TransitionCollapse from './TransitionCollapse.vue'

const props = defineProps<{
const {
disabled,
label,
enableEmptyState,
tooltip,
class: className
} = defineProps<{
disabled?: boolean
label?: string
enableEmptyState?: boolean
tooltip?: string
class?: string
}>()

const isCollapse = defineModel<boolean>('collapse', { default: false })

const isExpanded = computed(() => !isCollapse.value && !props.disabled)
const isExpanded = computed(() => !isCollapse.value && !disabled)

const tooltipConfig = computed(() => {
if (!props.tooltip) return undefined
return { value: props.tooltip, showDelay: 1000 }
if (!tooltip) return undefined
return { value: tooltip, showDelay: 1000 }
})
</script>

<template>
<div class="flex flex-col bg-comfy-menu-bg">
<div :class="cn('flex flex-col bg-comfy-menu-bg', className)">
<div
class="sticky top-0 z-10 flex items-center justify-between backdrop-blur-xl bg-inherit"
>
Expand Down
6 changes: 6 additions & 0 deletions src/components/ui/tags-input/TagsInputInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ const showInput = computed(() => isEditing.value || isEmpty)
const { forwardRef, currentElement } = useForwardExpose()
const registerFocus = inject(tagsInputFocusKey, undefined)

function handleEscape() {
currentElement.value?.blur()
isEditing.value = false
}

onMounted(() => {
registerFocus?.(() => currentElement.value?.focus())
})
Expand All @@ -44,5 +49,6 @@ onUnmounted(() => {
className
)
"
@keydown.escape.stop="handleEscape"
/>
</template>
Loading
Loading