Compare commits

...

2 Commits

Author SHA1 Message Date
CodeRabbit Fixer
cf399a556f fix: Refactor: Use Galleria lightbox for 'Open Image' instead of new tab (#9156)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:02:14 +01:00
Johnpaul Chiwetelu
7cb07f9b2d fix: standardize i18n pluralization to two-part English format (#9384)
## Summary

Standardize 5 English pluralization strings from incorrect 3-part format
to proper 2-part `"singular | plural"` format.

## Changes

- **What**: Convert `nodesCount`, `asset`, `errorCount`,
`downloadsFailed`, and `exportFailed` i18n keys from redundant 3-part
pluralization (zero/one/many) to standard 2-part English format
(singular/plural)

## Review Focus

The 3-part format (`a | b | a`) was redundant for English since the
first and third parts were identical. vue-i18n only needs 2 parts for
English pluralization.

Fixes #9277

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9384-fix-standardize-i18n-pluralization-to-two-part-English-format-3196d73d365081cf97c4e7cfa310ce8e)
by [Unito](https://www.unito.io)
2026-03-06 14:53:13 +01:00
6 changed files with 73 additions and 11 deletions

View File

@@ -1,7 +1,8 @@
import { useI18n } from 'vue-i18n'
import { downloadFile, openFileInNewTab } from '@/base/common/downloadUtil'
import { downloadFile } from '@/base/common/downloadUtil'
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import { useMediaAssetGalleryStore } from '@/platform/assets/composables/useMediaAssetGalleryStore'
import { useCommandStore } from '@/stores/commandStore'
import type { MenuOption } from './useMoreOptionsMenu'
@@ -23,7 +24,7 @@ export function useImageMenuOptions() {
if (!img) return
const url = new URL(img.src)
url.searchParams.delete('preview')
void openFileInNewTab(url.toString())
useMediaAssetGalleryStore().openUrl(url.toString())
}
const copyImage = async (node: LGraphNode) => {
@@ -87,7 +88,7 @@ export function useImageMenuOptions() {
},
{
label: t('contextMenu.Open Image'),
icon: 'icon-[lucide--external-link]',
icon: 'icon-[lucide--maximize]',
action: () => openImage(node)
},
{

View File

@@ -178,7 +178,7 @@
"uploadAlreadyInProgress": "Upload already in progress",
"capture": "capture",
"nodes": "Nodes",
"nodesCount": "{count} nodes | {count} node | {count} nodes",
"nodesCount": "{count} node | {count} nodes",
"addNode": "Add a node...",
"filterBy": "Filter by:",
"filterByType": "Filter by {type}...",
@@ -222,7 +222,7 @@
"failed": "Failed",
"cancelled": "Cancelled",
"job": "Job",
"asset": "{count} assets | {count} asset | {count} assets",
"asset": "{count} asset | {count} assets",
"untitled": "Untitled",
"emDash": "—",
"enabling": "Enabling {id}",
@@ -3347,7 +3347,7 @@
}
},
"errorOverlay": {
"errorCount": "{count} ERRORS | {count} ERROR | {count} ERRORS",
"errorCount": "{count} ERROR | {count} ERRORS",
"seeErrors": "See Errors"
},
"help": {
@@ -3357,7 +3357,7 @@
"progressToast": {
"importingModels": "Importing Models",
"downloadingModel": "Downloading model...",
"downloadsFailed": "{count} downloads failed | {count} download failed | {count} downloads failed",
"downloadsFailed": "{count} download failed | {count} downloads failed",
"allDownloadsCompleted": "All downloads completed",
"noImportsInQueue": "No {filter} in queue",
"failed": "Failed",
@@ -3374,7 +3374,7 @@
"exportingAssets": "Exporting Assets",
"preparingExport": "Preparing export...",
"exportError": "Export failed",
"exportFailed": "{count} export failed | {count} export failed | {count} exports failed",
"exportFailed": "{count} export failed | {count} exports failed",
"allExportsCompleted": "All exports completed",
"noExportsInQueue": "No {filter} exports in queue",
"exportStarted": "Preparing ZIP download...",

View File

@@ -160,6 +160,37 @@ describe('useMediaAssetGalleryStore', () => {
})
})
describe('openUrl', () => {
it('should create a ResultItemImpl with overridden url getter', () => {
const store = useMediaAssetGalleryStore()
const testUrl = 'https://example.com/node-image.png'
store.openUrl(testUrl)
expect(ResultItemImpl).toHaveBeenCalledWith({
filename: 'node-image.png',
subfolder: '',
type: 'output',
nodeId: '0',
mediaType: 'images'
})
expect(store.items).toHaveLength(1)
expect(store.items[0].url).toBe(testUrl)
expect(store.activeIndex).toBe(0)
})
it('should handle urls without a filename path', () => {
const store = useMediaAssetGalleryStore()
store.openUrl('https://example.com/')
expect(ResultItemImpl).toHaveBeenCalledWith(
expect.objectContaining({ filename: '' })
)
expect(store.items[0].url).toBe('https://example.com/')
})
})
describe('close', () => {
it('should reset activeIndex to -1', () => {
const store = useMediaAssetGalleryStore()

View File

@@ -37,11 +37,32 @@ export const useMediaAssetGalleryStore = defineStore(
activeIndex.value = 0
}
const openUrl = (url: string) => {
const resultItem = new ResultItemImpl({
filename: url.split('/').pop() ?? '',
subfolder: '',
type: 'output',
nodeId: '0',
mediaType: 'images'
})
Object.defineProperty(resultItem, 'url', {
get() {
return url
},
configurable: true
})
items.value = [resultItem]
activeIndex.value = 0
}
return {
activeIndex,
items,
close,
openSingle
openSingle,
openUrl
}
}
)

View File

@@ -1,6 +1,6 @@
import _ from 'es-toolkit/compat'
import { downloadFile, openFileInNewTab } from '@/base/common/downloadUtil'
import { downloadFile } from '@/base/common/downloadUtil'
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
import { useSubgraphOperations } from '@/composables/graph/useSubgraphOperations'
import { useNodeAnimatedImage } from '@/composables/node/useNodeAnimatedImage'
@@ -35,6 +35,7 @@ import type {
ISerialisedNode
} from '@/lib/litegraph/src/types/serialisation'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
import { useMediaAssetGalleryStore } from '@/platform/assets/composables/useMediaAssetGalleryStore'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useToastStore } from '@/platform/updates/common/toastStore'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
@@ -687,7 +688,7 @@ export const useLitegraphService = () => {
callback: () => {
const url = new URL(img.src)
url.searchParams.delete('preview')
void openFileInNewTab(url.toString())
useMediaAssetGalleryStore().openUrl(url.toString())
}
},
...getCopyImageOption(img),

View File

@@ -20,6 +20,11 @@
</template>
</div>
<ResultGallery
v-model:active-index="mediaAssetGalleryStore.activeIndex"
:all-gallery-items="mediaAssetGalleryStore.items"
/>
<GlobalToast />
<InviteAcceptedToast />
<RerouteMigrationToast />
@@ -50,6 +55,7 @@ import { runWhenGlobalIdle } from '@/base/common/async'
import MenuHamburger from '@/components/MenuHamburger.vue'
import UnloadWindowConfirmDialog from '@/components/dialog/UnloadWindowConfirmDialog.vue'
import GraphCanvas from '@/components/graph/GraphCanvas.vue'
import ResultGallery from '@/components/sidebar/tabs/queue/ResultGallery.vue'
import GlobalToast from '@/components/toast/GlobalToast.vue'
import InviteAcceptedToast from '@/platform/workspace/components/toasts/InviteAcceptedToast.vue'
import RerouteMigrationToast from '@/components/toast/RerouteMigrationToast.vue'
@@ -57,6 +63,7 @@ import { useBrowserTabTitle } from '@/composables/useBrowserTabTitle'
import { useCoreCommands } from '@/composables/useCoreCommands'
import { useQueuePolling } from '@/platform/remote/comfyui/useQueuePolling'
import { useErrorHandling } from '@/composables/useErrorHandling'
import { useMediaAssetGalleryStore } from '@/platform/assets/composables/useMediaAssetGalleryStore'
import { useProgressFavicon } from '@/composables/useProgressFavicon'
import { SERVER_CONFIG_ITEMS } from '@/constants/serverConfig'
import type { ServerConfig, ServerConfigValue } from '@/constants/serverConfig'
@@ -109,6 +116,7 @@ const colorPaletteStore = useColorPaletteStore()
const queueStore = useQueueStore()
const assetsStore = useAssetsStore()
const versionCompatibilityStore = useVersionCompatibilityStore()
const mediaAssetGalleryStore = useMediaAssetGalleryStore()
const graphCanvasContainerRef = ref<HTMLDivElement | null>(null)
const { isBuilderMode } = useAppMode()
const { linearMode } = storeToRefs(useCanvasStore())