Compare commits
39 Commits
pr-6563
...
fix/zendes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ee8f166a9 | ||
|
|
267e7d57b1 | ||
|
|
693fbbd3e4 | ||
|
|
4c5ae9cb8f | ||
|
|
47688fe363 | ||
|
|
7c2a768d83 | ||
|
|
a4fc68a9eb | ||
|
|
6c9743c1a6 | ||
|
|
4ab1e824b7 | ||
|
|
adecd258b6 | ||
|
|
2c4280a28d | ||
|
|
c2150c6046 | ||
|
|
cdffcfaa33 | ||
|
|
eae93c2a79 | ||
|
|
4cfa5b4b5d | ||
|
|
a10c01db4c | ||
|
|
f38255bff4 | ||
|
|
befa84130b | ||
|
|
dc8a0e04a5 | ||
|
|
9cd7a39f09 | ||
|
|
4810b5728a | ||
|
|
6fe88dba54 | ||
|
|
8df0a3885d | ||
|
|
8dfdac3fc4 | ||
|
|
0692253e90 | ||
|
|
3cded2c45f | ||
|
|
fd236b3587 | ||
|
|
e1f707ffe2 | ||
|
|
fddebd4a73 | ||
|
|
704de20245 | ||
|
|
feda2d47b0 | ||
|
|
2d22c9f7f0 | ||
|
|
d808998863 | ||
|
|
7bf8e5af38 | ||
|
|
31e405abc8 | ||
|
|
0e9c29e7b7 | ||
|
|
8d27c4fb1b | ||
|
|
02aaf577ec | ||
|
|
431594a6fc |
@@ -25,3 +25,6 @@ e3bb29ceb8174b8bbca9e48ec7d42cd540f40efa
|
||||
|
||||
# [refactor] Improve updates/notifications domain organization (#5590)
|
||||
27ab355f9c73415dc39f4d3f512b02308f847801
|
||||
|
||||
# Migrate Tailwind styles to design-system package
|
||||
9f19d8fb4bd22518879343b49c05634dca777df0
|
||||
|
||||
33
.github/workflows/pr-claude-review.yaml
vendored
@@ -17,40 +17,9 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
wait-for-ci:
|
||||
claude-review:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.label.name == 'claude-review'
|
||||
outputs:
|
||||
should-proceed: ${{ steps.check-status.outputs.proceed }}
|
||||
steps:
|
||||
- name: Wait for other CI checks
|
||||
uses: lewagon/wait-on-check-action@e106e5c43e8ca1edea6383a39a01c5ca495fd812
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
check-regexp: '^(lint-and-format|test|playwright-tests)'
|
||||
allowed-conclusions: success,skipped,failure,cancelled,neutral,action_required,timed_out,stale
|
||||
wait-interval: 30
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check if we should proceed
|
||||
id: check-status
|
||||
run: |
|
||||
CHECK_RUNS=$(gh api repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs --jq '.check_runs[] | select(.name | test("lint-and-format")) | {name, conclusion}')
|
||||
|
||||
if echo "$CHECK_RUNS" | grep -Eq '"conclusion": "(failure|cancelled|timed_out|action_required)"'; then
|
||||
echo "Some CI checks failed - skipping Claude review"
|
||||
echo "proceed=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "All CI checks passed - proceeding with Claude review"
|
||||
echo "proceed=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
claude-review:
|
||||
needs: wait-for-ci
|
||||
if: needs.wait-for-ci.outputs.should-proceed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
1
.gitignore
vendored
@@ -18,6 +18,7 @@ yarn.lock
|
||||
.stylelintcache
|
||||
|
||||
node_modules
|
||||
.pnpm-store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 78 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@comfyorg/comfyui-frontend",
|
||||
"private": true,
|
||||
"version": "1.32.0",
|
||||
"version": "1.32.1",
|
||||
"type": "module",
|
||||
"repository": "https://github.com/Comfy-Org/ComfyUI_frontend",
|
||||
"homepage": "https://comfy.org",
|
||||
|
||||
@@ -241,6 +241,18 @@
|
||||
--border-subtle: var(--color-smoke-400);
|
||||
--muted-background: var(--color-smoke-700);
|
||||
--accent-background: var(--color-smoke-800);
|
||||
|
||||
/* Default UI element color palette variables */
|
||||
--palette-contrast-mix-color: #fff;
|
||||
--palette-interface-panel-surface: var(--comfy-menu-bg);
|
||||
--palette-interface-stroke: color-mix(in srgb, var(--interface-panel-surface) 75.5%, var(--contrast-mix-color));
|
||||
|
||||
--palette-interface-panel-box-shadow: 1px 1px 8px 0 rgb(0 0 0 / 0.4);
|
||||
--palette-interface-panel-drop-shadow: 1px 1px 4px rgb(0 0 0 / 0.4);
|
||||
--palette-interface-panel-hover-surface: color-mix(in srgb, var(--interface-panel-surface) 92.5%, var(--contrast-mix-color));
|
||||
--palette-interface-panel-selected-surface: color-mix(in srgb, var(--interface-panel-surface) 87.5%, var(--contrast-mix-color));
|
||||
--palette-interface-button-hover-surface: color-mix(in srgb, var(--interface-panel-surface) 82%, var(--contrast-mix-color));
|
||||
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
@@ -497,36 +509,40 @@ body {
|
||||
.comfy-markdown {
|
||||
/* We assign the textarea and the Tiptap editor to the same CSS grid area to stack them on top of one another. */
|
||||
display: grid;
|
||||
}
|
||||
& > textarea,
|
||||
.tiptap {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
}
|
||||
|
||||
.comfy-markdown > textarea {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
}
|
||||
& > textarea {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.comfy-markdown .tiptap {
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
background-color: var(--comfy-input-bg);
|
||||
color: var(--input-text);
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
font-size: var(--comfy-textarea-font-size);
|
||||
height: 100%;
|
||||
padding: 0.5em;
|
||||
}
|
||||
&.editing {
|
||||
& > textarea {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.comfy-markdown.editing .tiptap {
|
||||
display: none;
|
||||
}
|
||||
.tiptap {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.comfy-markdown .tiptap :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.tiptap {
|
||||
overflow-y: auto;
|
||||
font-size: var(--comfy-textarea-font-size);
|
||||
|
||||
.comfy-markdown .tiptap :last-child {
|
||||
margin-bottom: 0;
|
||||
:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comfy-markdown .tiptap blockquote {
|
||||
|
||||
4
packages/design-system/src/icons/pin.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.13727 11.2201L6.27454 14.4398" stroke="var(--pin-color, var(--color-smoke-800, #8a8a8a))" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M6.28085 6.68385C6.21652 6.92342 6.08664 7.1403 5.9058 7.31009C5.72497 7.47989 5.50035 7.59587 5.25721 7.645L3.95569 7.91742C3.71254 7.96655 3.48793 8.08253 3.30709 8.25233C3.12626 8.42212 2.99637 8.639 2.93204 8.87857L2.80091 9.36797C2.75515 9.53876 2.7791 9.72073 2.86751 9.87385C2.95591 10.027 3.10153 10.1387 3.27231 10.1845L10.9997 12.255C11.1705 12.3008 11.3525 12.2768 11.5056 12.1884C11.6587 12.1 11.7705 11.9544 11.8162 11.7836L11.9474 11.2942C12.0114 11.0546 12.0074 10.8018 11.9357 10.5643C11.864 10.3269 11.7274 10.1141 11.5414 9.95001L10.5505 9.06333C10.3645 8.89921 10.2279 8.68646 10.1562 8.44899C10.0845 8.21153 10.0805 7.95877 10.1446 7.71913L10.7933 5.29787C10.8391 5.12709 10.9508 4.98148 11.1039 4.89307C11.2571 4.80466 11.439 4.78071 11.6098 4.82647C11.9514 4.91799 12.3153 4.87008 12.6216 4.69327C12.9278 4.51646 13.1513 4.22523 13.2428 3.88366C13.3343 3.54209 13.2864 3.17815 13.1096 2.8719C12.9328 2.56566 12.6416 2.34219 12.3 2.25067L7.1484 0.870299C6.80683 0.778775 6.44289 0.826689 6.13665 1.0035C5.8304 1.18031 5.60694 1.47154 5.51541 1.81311C5.42389 2.15468 5.4718 2.51862 5.64861 2.82487C5.82542 3.13111 6.11665 3.35458 6.45822 3.4461C6.62901 3.49186 6.77462 3.6036 6.86302 3.75672C6.95143 3.90984 6.97539 4.09181 6.92962 4.2626L6.28085 6.68385Z" fill="var(--handle-color, var(--color-gold-600, #fd9903))" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -4,6 +4,13 @@ import { addDynamicIconSelectors } from '@iconify/tailwind'
|
||||
import { iconCollection } from './src/iconCollection'
|
||||
|
||||
export default {
|
||||
theme: {
|
||||
extend: {
|
||||
boxShadow: {
|
||||
interface: 'var(--interface-panel-box-shadow)'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
addDynamicIconSelectors({
|
||||
iconSets: {
|
||||
|
||||
40403
packages/registry-types/src/comfyRegistryTypes.ts
generated
6
pnpm-lock.yaml
generated
@@ -12,15 +12,9 @@ catalogs:
|
||||
'@eslint/js':
|
||||
specifier: ^9.35.0
|
||||
version: 9.35.0
|
||||
'@iconify-json/lucide':
|
||||
specifier: ^1.1.178
|
||||
version: 1.2.66
|
||||
'@iconify/json':
|
||||
specifier: ^2.2.380
|
||||
version: 2.2.380
|
||||
'@iconify/tailwind':
|
||||
specifier: ^1.1.3
|
||||
version: 1.2.0
|
||||
'@intlify/eslint-plugin-vue-i18n':
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
|
||||
4
public/assets/images/comfy-cloud-logo.svg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/images/og-image.png
Normal file
|
After Width: | Height: | Size: 325 KiB |
@@ -52,7 +52,7 @@
|
||||
"comfy_base": {
|
||||
"fg-color": "#fff",
|
||||
"bg-color": "#202020",
|
||||
"comfy-menu-bg": "#353535",
|
||||
"comfy-menu-bg": "#11141a",
|
||||
"comfy-menu-secondary-bg": "#303030",
|
||||
"comfy-input-bg": "#222",
|
||||
"input-text": "#ddd",
|
||||
|
||||
@@ -68,7 +68,12 @@
|
||||
"content-fg": "#222",
|
||||
"content-hover-bg": "#adadad",
|
||||
"content-hover-fg": "#222",
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.25) 0 0 0.5rem"
|
||||
"bar-shadow": "rgba(16, 16, 16, 0.25) 0 0 0.5rem",
|
||||
"interface-panel-box-shadow": "1px 1px 8px 0 rgba(0, 0, 0, 0.2)",
|
||||
"interface-panel-drop-shadow": "1px 1px 4px rgba(0, 0, 0, 0.4)",
|
||||
"interface-panel-hover-surface": "var(--color-gray-200)",
|
||||
"interface-panel-selected-surface": "color-mix(in srgb, var(--interface-panel-surface) 78%, var(--contrast-mix-color))",
|
||||
"contrast-mix-color": "#000"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="actionbar-container pointer-events-auto mx-1 flex h-12 items-center rounded-lg px-2 shadow-md"
|
||||
class="actionbar-container pointer-events-auto mx-1 flex h-12 items-center rounded-lg border border-[var(--interface-stroke)] px-2 shadow-interface"
|
||||
>
|
||||
<!-- Support for legacy topbar elements attached by custom scripts, hidden if no elements present -->
|
||||
<div
|
||||
@@ -48,6 +48,5 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.actionbar-container {
|
||||
background-color: var(--comfy-menu-bg);
|
||||
border: 1px solid var(--p-panel-border-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -48,6 +48,7 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
|
||||
import { t } from '@/i18n'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import ComfyRunButton from './ComfyRunButton'
|
||||
@@ -132,6 +133,15 @@ watch(visible, async (newVisible) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Track run button handle drag start using mousedown on the drag handle.
|
||||
*/
|
||||
useEventListener(dragHandleRef, 'mousedown', () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'actionbar_run_handle_drag_start'
|
||||
})
|
||||
})
|
||||
|
||||
const lastDragState = ref({
|
||||
x: x.value,
|
||||
y: y.value,
|
||||
@@ -258,7 +268,9 @@ const panelClass = computed(() =>
|
||||
cn(
|
||||
'actionbar pointer-events-auto z1000',
|
||||
isDragging.value && 'select-none pointer-events-none',
|
||||
isDocked.value ? 'p-0 static mr-2 border-none bg-transparent' : 'fixed'
|
||||
isDocked.value
|
||||
? 'p-0 static mr-2 border-none bg-transparent'
|
||||
: 'fixed shadow-interface'
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -100,7 +100,7 @@ import BatchCountEdit from '../BatchCountEdit.vue'
|
||||
|
||||
const workspaceStore = useWorkspaceStore()
|
||||
const queueCountStore = storeToRefs(useQueuePendingTaskCountStore())
|
||||
const { mode: queueMode } = storeToRefs(useQueueSettingsStore())
|
||||
const { mode: queueMode, batchCount } = storeToRefs(useQueueSettingsStore())
|
||||
|
||||
const { t } = useI18n()
|
||||
const queueModeMenuItemLookup = computed(() => {
|
||||
@@ -118,6 +118,9 @@ const queueModeMenuItemLookup = computed(() => {
|
||||
label: `${t('menu.run')} (${t('menu.onChange')})`,
|
||||
tooltip: t('menu.onChangeTooltip'),
|
||||
command: () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'queue_mode_option_run_on_change_selected'
|
||||
})
|
||||
queueMode.value = 'change'
|
||||
}
|
||||
}
|
||||
@@ -128,6 +131,9 @@ const queueModeMenuItemLookup = computed(() => {
|
||||
label: `${t('menu.run')} (${t('menu.instant')})`,
|
||||
tooltip: t('menu.instantTooltip'),
|
||||
command: () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'queue_mode_option_run_instant_selected'
|
||||
})
|
||||
queueMode.value = 'instant'
|
||||
}
|
||||
}
|
||||
@@ -158,9 +164,18 @@ const queuePrompt = async (e: Event) => {
|
||||
? 'Comfy.QueuePromptFront'
|
||||
: 'Comfy.QueuePrompt'
|
||||
|
||||
useTelemetry()?.trackRunButton({ subscribe_to_run: false })
|
||||
if (batchCount.value > 1) {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'queue_run_multiple_batches_submitted'
|
||||
})
|
||||
}
|
||||
|
||||
await commandStore.execute(commandId)
|
||||
await commandStore.execute(commandId, {
|
||||
metadata: {
|
||||
subscribe_to_run: false,
|
||||
trigger_source: 'button'
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="subgraph-breadcrumb w-auto drop-shadow-md"
|
||||
class="subgraph-breadcrumb w-auto drop-shadow-[var(--interface-panel-drop-shadow)]"
|
||||
:class="{
|
||||
'subgraph-breadcrumb-collapse': collapseTabs,
|
||||
'subgraph-breadcrumb-overflow': overflowingTabs
|
||||
@@ -40,6 +40,7 @@ import { computed, onUpdated, ref, watch } from 'vue'
|
||||
|
||||
import SubgraphBreadcrumbItem from '@/components/breadcrumb/SubgraphBreadcrumbItem.vue'
|
||||
import { useOverflowObserver } from '@/composables/element/useOverflowObserver'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useSubgraphNavigationStore } from '@/stores/subgraphNavigationStore'
|
||||
@@ -73,6 +74,9 @@ const items = computed(() => {
|
||||
const items = navigationStore.navigationStack.map<MenuItem>((subgraph) => ({
|
||||
label: subgraph.name,
|
||||
command: () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'breadcrumb_subgraph_item_selected'
|
||||
})
|
||||
const canvas = useCanvasStore().getCanvas()
|
||||
if (!canvas.graph) throw new TypeError('Canvas has no graph')
|
||||
|
||||
@@ -97,6 +101,9 @@ const home = computed(() => ({
|
||||
key: 'root',
|
||||
isBlueprint: isBlueprint.value,
|
||||
command: () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'breadcrumb_subgraph_root_selected'
|
||||
})
|
||||
const canvas = useCanvasStore().getCanvas()
|
||||
if (!canvas.graph) throw new TypeError('Canvas has no graph')
|
||||
|
||||
@@ -201,8 +208,8 @@ onUpdated(() => {
|
||||
:deep(.p-breadcrumb-separator),
|
||||
:deep(.p-breadcrumb-item) {
|
||||
@apply h-12;
|
||||
border-top: 1px solid var(--p-panel-border-color);
|
||||
border-bottom: 1px solid var(--p-panel-border-color);
|
||||
border-top: 1px solid var(--interface-stroke);
|
||||
border-bottom: 1px solid var(--interface-stroke);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
}
|
||||
|
||||
@@ -214,7 +221,7 @@ onUpdated(() => {
|
||||
@apply rounded-l-lg;
|
||||
/* Then collapse the root workflow */
|
||||
flex-shrink: 5000;
|
||||
border-left: 1px solid var(--p-panel-border-color);
|
||||
border-left: 1px solid var(--interface-stroke);
|
||||
|
||||
.p-breadcrumb-item-link {
|
||||
padding-left: var(--p-breadcrumb-item-padding);
|
||||
@@ -225,7 +232,7 @@ onUpdated(() => {
|
||||
@apply rounded-r-lg;
|
||||
/* Then collapse the active item */
|
||||
flex-shrink: 1;
|
||||
border-right: 1px solid var(--p-panel-border-color);
|
||||
border-right: 1px solid var(--interface-stroke);
|
||||
}
|
||||
|
||||
:deep(.p-breadcrumb-item-link:hover),
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('UserAvatar', () => {
|
||||
const avatar = wrapper.findComponent(Avatar)
|
||||
expect(avatar.exists()).toBe(true)
|
||||
expect(avatar.props('image')).toBeNull()
|
||||
expect(avatar.props('icon')).toBe('pi pi-user')
|
||||
expect(avatar.props('icon')).toBe('icon-[lucide--user]')
|
||||
})
|
||||
|
||||
it('renders with default icon when provided photo Url is null', () => {
|
||||
@@ -67,7 +67,7 @@ describe('UserAvatar', () => {
|
||||
const avatar = wrapper.findComponent(Avatar)
|
||||
expect(avatar.exists()).toBe(true)
|
||||
expect(avatar.props('image')).toBeNull()
|
||||
expect(avatar.props('icon')).toBe('pi pi-user')
|
||||
expect(avatar.props('icon')).toBe('icon-[lucide--user]')
|
||||
})
|
||||
|
||||
it('falls back to icon when image fails to load', async () => {
|
||||
@@ -82,7 +82,7 @@ describe('UserAvatar', () => {
|
||||
avatar.vm.$emit('error')
|
||||
await nextTick()
|
||||
|
||||
expect(avatar.props('icon')).toBe('pi pi-user')
|
||||
expect(avatar.props('icon')).toBe('icon-[lucide--user]')
|
||||
})
|
||||
|
||||
it('uses provided ariaLabel', () => {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<Avatar
|
||||
class="bg-gray-200 dark-theme:bg-[var(--interface-panel-selected-surface)]"
|
||||
:image="photoUrl ?? undefined"
|
||||
:icon="hasAvatar ? undefined : 'pi pi-user'"
|
||||
:icon="hasAvatar ? undefined : 'icon-[lucide--user]'"
|
||||
:pt:icon:class="{ 'size-4': !hasAvatar }"
|
||||
shape="circle"
|
||||
:aria-label="ariaLabel ?? $t('auth.login.userAvatar')"
|
||||
@error="handleImageError"
|
||||
|
||||
@@ -68,17 +68,17 @@
|
||||
</template>
|
||||
</MultiSelect>
|
||||
|
||||
<!-- License Filter -->
|
||||
<!-- Runs On Filter -->
|
||||
<MultiSelect
|
||||
v-model="selectedLicenseObjects"
|
||||
:label="licenseFilterLabel"
|
||||
:options="licenseOptions"
|
||||
v-model="selectedRunsOnObjects"
|
||||
:label="runsOnFilterLabel"
|
||||
:options="runsOnOptions"
|
||||
:show-search-box="true"
|
||||
:show-selected-count="true"
|
||||
:show-clear-button="true"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--file-text]" />
|
||||
<i class="icon-[lucide--server]" />
|
||||
</template>
|
||||
</MultiSelect>
|
||||
</div>
|
||||
@@ -528,12 +528,12 @@ const {
|
||||
searchQuery,
|
||||
selectedModels,
|
||||
selectedUseCases,
|
||||
selectedLicenses,
|
||||
selectedRunsOn,
|
||||
sortBy,
|
||||
filteredTemplates,
|
||||
availableModels,
|
||||
availableUseCases,
|
||||
availableLicenses,
|
||||
availableRunsOn,
|
||||
filteredCount,
|
||||
totalCount,
|
||||
resetFilters
|
||||
@@ -561,15 +561,15 @@ const selectedUseCaseObjects = computed({
|
||||
}
|
||||
})
|
||||
|
||||
const selectedLicenseObjects = computed({
|
||||
const selectedRunsOnObjects = computed({
|
||||
get() {
|
||||
return selectedLicenses.value.map((license) => ({
|
||||
name: license,
|
||||
value: license
|
||||
return selectedRunsOn.value.map((runsOn) => ({
|
||||
name: runsOn,
|
||||
value: runsOn
|
||||
}))
|
||||
},
|
||||
set(value: { name: string; value: string }[]) {
|
||||
selectedLicenses.value = value.map((item) => item.value)
|
||||
selectedRunsOn.value = value.map((item) => item.value)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -602,10 +602,10 @@ const useCaseOptions = computed(() =>
|
||||
}))
|
||||
)
|
||||
|
||||
const licenseOptions = computed(() =>
|
||||
availableLicenses.value.map((license) => ({
|
||||
name: license,
|
||||
value: license
|
||||
const runsOnOptions = computed(() =>
|
||||
availableRunsOn.value.map((runsOn) => ({
|
||||
name: runsOn,
|
||||
value: runsOn
|
||||
}))
|
||||
)
|
||||
|
||||
@@ -634,14 +634,14 @@ const useCaseFilterLabel = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const licenseFilterLabel = computed(() => {
|
||||
if (selectedLicenseObjects.value.length === 0) {
|
||||
return t('templateWorkflows.licenseFilter', 'License')
|
||||
} else if (selectedLicenseObjects.value.length === 1) {
|
||||
return selectedLicenseObjects.value[0].name
|
||||
const runsOnFilterLabel = computed(() => {
|
||||
if (selectedRunsOnObjects.value.length === 0) {
|
||||
return t('templateWorkflows.runsOnFilter', 'Runs On')
|
||||
} else if (selectedRunsOnObjects.value.length === 1) {
|
||||
return selectedRunsOnObjects.value[0].name
|
||||
} else {
|
||||
return t('templateWorkflows.licensesSelected', {
|
||||
count: selectedLicenseObjects.value.length
|
||||
return t('templateWorkflows.runsOnSelected', {
|
||||
count: selectedRunsOnObjects.value.length
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -708,7 +708,7 @@ watch(
|
||||
sortBy,
|
||||
selectedModels,
|
||||
selectedUseCases,
|
||||
selectedLicenses
|
||||
selectedRunsOn
|
||||
],
|
||||
() => {
|
||||
resetPagination()
|
||||
|
||||
@@ -87,7 +87,13 @@ const repoOwner = 'comfyanonymous'
|
||||
const repoName = 'ComfyUI'
|
||||
const reportContent = ref('')
|
||||
const reportOpen = ref(false)
|
||||
/**
|
||||
* Open the error report content and track telemetry.
|
||||
*/
|
||||
const showReport = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'error_dialog_show_report_clicked'
|
||||
})
|
||||
reportOpen.value = true
|
||||
}
|
||||
const toast = useToast()
|
||||
@@ -99,6 +105,9 @@ const title = computed<string>(
|
||||
() => error.nodeType ?? error.exceptionType ?? t('errorDialog.defaultTitle')
|
||||
)
|
||||
|
||||
/**
|
||||
* Open contact support flow from error dialog and track telemetry.
|
||||
*/
|
||||
const showContactSupport = async () => {
|
||||
telemetry?.trackHelpResourceClicked({
|
||||
resource_type: 'help_feedback',
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
import Button from 'primevue/button'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
const props = defineProps<{
|
||||
errorMessage: string
|
||||
repoOwner: string
|
||||
@@ -19,7 +21,13 @@ const props = defineProps<{
|
||||
|
||||
const queryString = computed(() => props.errorMessage + ' is:issue')
|
||||
|
||||
/**
|
||||
* Open GitHub issues search and track telemetry.
|
||||
*/
|
||||
const openGitHubIssues = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'error_dialog_find_existing_issues_clicked'
|
||||
})
|
||||
const query = encodeURIComponent(queryString.value)
|
||||
const url = `https://github.com/${props.repoOwner}/${props.repoName}/issues?q=${query}`
|
||||
window.open(url, '_blank')
|
||||
|
||||
@@ -164,6 +164,8 @@ watch(
|
||||
)
|
||||
|
||||
const handlePurchaseCreditsClick = () => {
|
||||
// Track purchase credits entry from Settings > Credits panel
|
||||
useTelemetry()?.trackAddApiCreditButtonClicked()
|
||||
dialogService.showTopUpCreditsDialog()
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
></div>
|
||||
|
||||
<ButtonGroup
|
||||
class="absolute right-0 bottom-0 z-[1200] flex-row gap-1 border-[1px] border-node-border bg-interface-panel-surface p-2"
|
||||
:style="stringifiedMinimapStyles.buttonGroupStyles"
|
||||
class="absolute right-0 bottom-0 z-[1200] flex-row gap-1 border-[1px] border-[var(--interface-stroke)] bg-interface-panel-surface p-2"
|
||||
:style="{
|
||||
...stringifiedMinimapStyles.buttonGroupStyles
|
||||
}"
|
||||
@wheel="canvasInteractions.handleWheel"
|
||||
>
|
||||
<CanvasModeSelector
|
||||
@@ -61,7 +63,7 @@
|
||||
data-testid="toggle-minimap-button"
|
||||
:style="stringifiedMinimapStyles.buttonStyles"
|
||||
:class="minimapButtonClass"
|
||||
@click="() => commandStore.execute('Comfy.Canvas.ToggleMinimap')"
|
||||
@click="onMinimapToggleClick"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--map] h-4 w-4" />
|
||||
@@ -82,7 +84,7 @@
|
||||
:aria-label="linkVisibilityAriaLabel"
|
||||
data-testid="toggle-link-visibility-button"
|
||||
:style="stringifiedMinimapStyles.buttonStyles"
|
||||
@click="() => commandStore.execute('Comfy.Canvas.ToggleLinkVisibility')"
|
||||
@click="onLinkVisibilityToggleClick"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--route-off] h-4 w-4" />
|
||||
@@ -101,6 +103,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useZoomControls } from '@/composables/useZoomControls'
|
||||
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
|
||||
import { useMinimap } from '@/renderer/extensions/minimap/composables/useMinimap'
|
||||
@@ -218,6 +221,26 @@ onMounted(() => {
|
||||
canvasStore.initScaleSync()
|
||||
})
|
||||
|
||||
/**
|
||||
* Track minimap toggle button click and execute the command.
|
||||
*/
|
||||
const onMinimapToggleClick = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'graph_menu_minimap_toggle_clicked'
|
||||
})
|
||||
void commandStore.execute('Comfy.Canvas.ToggleMinimap')
|
||||
}
|
||||
|
||||
/**
|
||||
* Track hide/show links button click and execute the command.
|
||||
*/
|
||||
const onLinkVisibilityToggleClick = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'graph_menu_hide_links_toggle_clicked'
|
||||
})
|
||||
void commandStore.execute('Comfy.Canvas.ToggleLinkVisibility')
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
canvasStore.cleanupScaleSync()
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@click="toggleBypass"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--ban] h-4 w-4" />
|
||||
<i class="icon-[lucide--redo-dot] h-4 w-4" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
data-testid="info-button"
|
||||
text
|
||||
severity="secondary"
|
||||
@click="toggleHelp"
|
||||
@click="onInfoClick"
|
||||
>
|
||||
<i class="icon-[lucide--info] h-4 w-4" />
|
||||
</Button>
|
||||
@@ -17,6 +17,17 @@
|
||||
import Button from 'primevue/button'
|
||||
|
||||
import { useSelectionState } from '@/composables/graph/useSelectionState'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
const { showNodeHelp: toggleHelp } = useSelectionState()
|
||||
|
||||
/**
|
||||
* Track node info button click and toggle node help.
|
||||
*/
|
||||
const onInfoClick = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'selection_toolbox_node_info_opened'
|
||||
})
|
||||
toggleHelp()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
:width="size"
|
||||
:height="size"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.8193 0.600586C15.1248 0.600586 15.3296 0.70893 15.459 0.881836C15.5914 1.05888 15.6471 1.33774 15.5527 1.66895L14.8037 4.30176C14.7063 4.64386 14.4729 4.97024 14.1641 5.21191C13.8544 5.45415 13.496 5.58984 13.1699 5.58984H13.1689L9.5791 5.59668H7.90625C7.52654 5.59668 7.19496 5.84986 7.09082 6.21289L5.69434 11.0889C5.63007 11.3133 5.66134 11.5534 5.77734 11.7529L5.83203 11.8359C5.99177 12.0491 6.24252 12.1758 6.50977 12.1758H6.51074L8.88281 12.1709H11.4971C11.7643 12.171 11.9541 12.254 12.084 12.3906L12.1357 12.4521C12.2685 12.6295 12.3249 12.9089 12.2305 13.2402L11.4805 15.8721C11.383 16.2144 11.1498 16.5415 10.8408 16.7832C10.5314 17.0252 10.1736 17.161 9.84766 17.1611H9.84668L6.25684 17.168H3.64258C3.33762 17.1679 3.13349 17.0588 3.00391 16.8857C2.87135 16.7087 2.81482 16.43 2.90918 16.0986L3.39551 14.3887C3.46841 14.1327 3.41794 13.8576 3.25879 13.6445V13.6436C3.09901 13.4303 2.84745 13.3037 2.58008 13.3037H1.18066C0.875088 13.3037 0.670398 13.1953 0.541016 13.0225C0.408483 12.8451 0.351891 12.5655 0.446289 12.2344L2.11914 6.38965L2.30371 5.74707V5.74609C2.40139 5.40341 2.63456 5.07671 2.94336 4.83496C3.25302 4.59258 3.61143 4.45705 3.9375 4.45703H5.6123C5.94484 4.45703 6.24083 4.26316 6.37891 3.9707L6.42773 3.83984L6.98145 1.89551C7.07894 1.55317 7.31212 1.22614 7.62109 0.984375C7.93074 0.742127 8.2892 0.606445 8.61523 0.606445H8.61621L12.1982 0.600586H14.8193Z"
|
||||
:stroke="color"
|
||||
stroke-width="1"
|
||||
v-bind="attributes"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -22,11 +20,18 @@ interface Props {
|
||||
size?: number | string
|
||||
color?: string
|
||||
class?: string
|
||||
mode?: 'outline' | 'fill'
|
||||
}
|
||||
const {
|
||||
size = 16,
|
||||
color = 'currentColor',
|
||||
mode = 'outline',
|
||||
class: className
|
||||
} = defineProps<Props>()
|
||||
const iconClass = computed(() => className || '')
|
||||
const attributes = computed(() => ({
|
||||
stroke: mode === 'outline' ? color : undefined,
|
||||
strokeWidth: mode === 'outline' ? 1 : undefined,
|
||||
fill: mode === 'fill' ? color : 'none'
|
||||
}))
|
||||
</script>
|
||||
@@ -1,21 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="comfy-menu-button-wrapper flex shrink-0 cursor-pointer flex-col items-center justify-center rounded-t-md p-2 transition-colors"
|
||||
v-tooltip="{
|
||||
value: t('sideToolbar.labels.menu'),
|
||||
showDelay: 300,
|
||||
hideDelay: 300
|
||||
}"
|
||||
class="comfy-menu-button-wrapper flex shrink-0 cursor-pointer flex-col items-center justify-center p-2 transition-colors"
|
||||
:class="{
|
||||
'comfy-menu-button-active': menuRef?.visible
|
||||
}"
|
||||
@click="menuRef?.toggle($event)"
|
||||
@click="onLogoMenuClick($event)"
|
||||
>
|
||||
<ComfyLogoTransparent
|
||||
alt="ComfyUI Logo"
|
||||
class="comfyui-logo h-[18px] w-[18px]"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-if="!isSmall"
|
||||
class="side-bar-button-label mt-1 text-center text-[10px]"
|
||||
>{{ t('sideToolbar.labels.menu') }}</span
|
||||
>
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-black">
|
||||
<ComfyLogo
|
||||
alt="ComfyUI Logo"
|
||||
class="comfyui-logo h-[18px] w-[18px] text-white"
|
||||
mode="fill"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TieredMenu
|
||||
@@ -75,9 +77,10 @@ import { computed, nextTick, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader.vue'
|
||||
import ComfyLogoTransparent from '@/components/icons/ComfyLogoTransparent.vue'
|
||||
import ComfyLogo from '@/components/icons/ComfyLogo.vue'
|
||||
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
|
||||
import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useColorPaletteService } from '@/services/colorPaletteService'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
@@ -96,14 +99,19 @@ const colorPaletteService = useColorPaletteService()
|
||||
const dialogStore = useDialogStore()
|
||||
const managerState = useManagerState()
|
||||
|
||||
const { isSmall = false } = defineProps<{
|
||||
isSmall?: boolean
|
||||
}>()
|
||||
|
||||
const menuRef = ref<
|
||||
({ dirty: boolean } & TieredMenuMethods & TieredMenuState) | null
|
||||
>(null)
|
||||
|
||||
const telemetry = useTelemetry()
|
||||
|
||||
function onLogoMenuClick(event: MouseEvent) {
|
||||
telemetry?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_comfy_menu_opened'
|
||||
})
|
||||
menuRef.value?.toggle(event)
|
||||
}
|
||||
|
||||
const translateMenuItem = (item: MenuItem): MenuItem => {
|
||||
const label = typeof item.label === 'function' ? item.label() : item.label
|
||||
const translatedLabel = label
|
||||
@@ -167,7 +175,12 @@ const extraMenuItems = computed(() => [
|
||||
key: 'settings',
|
||||
label: t('g.settings'),
|
||||
icon: 'mdi mdi-cog-outline',
|
||||
command: () => showSettings()
|
||||
command: () => {
|
||||
telemetry?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_settings_menu_opened'
|
||||
})
|
||||
showSettings()
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'manage-extensions',
|
||||
@@ -277,12 +290,12 @@ const hasActiveStateSiblings = (item: MenuItem): boolean => {
|
||||
}
|
||||
|
||||
.comfy-menu-button-wrapper:hover {
|
||||
background: var(--p-button-text-secondary-hover-background);
|
||||
background: var(--interface-panel-hover-surface);
|
||||
}
|
||||
|
||||
.comfy-menu-button-active,
|
||||
.comfy-menu-button-active:hover {
|
||||
background-color: var(--content-hover-bg);
|
||||
background: var(--interface-panel-selected-surface);
|
||||
}
|
||||
|
||||
.keybinding-tag {
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
'small-sidebar': isSmall,
|
||||
'connected-sidebar': isConnected,
|
||||
'floating-sidebar': !isConnected,
|
||||
'overflowing-sidebar': isOverflowing
|
||||
'overflowing-sidebar': isOverflowing,
|
||||
'border-r border-[var(--interface-stroke)] shadow-interface': isConnected
|
||||
}"
|
||||
>
|
||||
<div
|
||||
@@ -18,7 +19,7 @@
|
||||
"
|
||||
>
|
||||
<div ref="topToolbarRef" :class="groupClasses">
|
||||
<ComfyMenuButton :is-small="isSmall" />
|
||||
<ComfyMenuButton />
|
||||
<SidebarIcon
|
||||
v-for="tab in tabs"
|
||||
:key="tab.id"
|
||||
@@ -57,6 +58,7 @@ import ComfyMenuButton from '@/components/sidebar/ComfyMenuButton.vue'
|
||||
import SidebarBottomPanelToggleButton from '@/components/sidebar/SidebarBottomPanelToggleButton.vue'
|
||||
import SidebarShortcutsToggleButton from '@/components/sidebar/SidebarShortcutsToggleButton.vue'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useKeybindingStore } from '@/stores/keybindingStore'
|
||||
@@ -97,10 +99,40 @@ const isConnected = computed(
|
||||
const tabs = computed(() => workspaceStore.getSidebarTabs())
|
||||
const selectedTab = computed(() => workspaceStore.sidebarTab.activeSidebarTab)
|
||||
|
||||
const onTabClick = async (item: SidebarTabExtension) =>
|
||||
/**
|
||||
* Handle sidebar tab icon click.
|
||||
* - Emits UI button telemetry for known tabs
|
||||
* - Delegates to the corresponding toggle command
|
||||
*/
|
||||
const onTabClick = async (item: SidebarTabExtension) => {
|
||||
const telemetry = useTelemetry()
|
||||
|
||||
const isNodeLibraryTab = item.id === 'node-library'
|
||||
const isModelLibraryTab = item.id === 'model-library'
|
||||
const isWorkflowsTab = item.id === 'workflows'
|
||||
const isAssetsTab = item.id === 'assets'
|
||||
|
||||
if (isNodeLibraryTab)
|
||||
telemetry?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_tab_node_library_selected'
|
||||
})
|
||||
else if (isModelLibraryTab)
|
||||
telemetry?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_tab_model_library_selected'
|
||||
})
|
||||
else if (isWorkflowsTab)
|
||||
telemetry?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_tab_workflows_selected'
|
||||
})
|
||||
else if (isAssetsTab)
|
||||
telemetry?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_tab_assets_media_selected'
|
||||
})
|
||||
|
||||
await commandStore.commands
|
||||
.find((cmd) => cmd.id === `Workspace.ToggleSidebarTab.${item.id}`)
|
||||
?.function?.()
|
||||
}
|
||||
|
||||
const keybindingStore = useKeybindingStore()
|
||||
const getTabTooltipSuffix = (tab: SidebarTabExtension) => {
|
||||
@@ -114,7 +146,7 @@ const isOverflowing = ref(false)
|
||||
const groupClasses = computed(() =>
|
||||
cn(
|
||||
'sidebar-item-group pointer-events-auto flex flex-col items-center overflow-hidden flex-shrink-0' +
|
||||
(isConnected.value ? '' : ' rounded-lg shadow-md')
|
||||
(isConnected.value ? '' : ' rounded-lg shadow-interface')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -183,7 +215,7 @@ onMounted(() => {
|
||||
--sidebar-padding: 4px;
|
||||
--sidebar-icon-size: 1rem;
|
||||
|
||||
--sidebar-default-floating-width: 56px;
|
||||
--sidebar-default-floating-width: 48px;
|
||||
--sidebar-default-connected-width: calc(
|
||||
var(--sidebar-default-floating-width) + var(--sidebar-padding) * 2
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
:label="$t('sideToolbar.labels.console')"
|
||||
:tooltip="$t('menu.toggleBottomPanel')"
|
||||
:selected="bottomPanelStore.activePanel == 'terminal'"
|
||||
@click="bottomPanelStore.toggleBottomPanel"
|
||||
@click="toggleConsole"
|
||||
>
|
||||
<template #icon>
|
||||
<i-ph:terminal-bold />
|
||||
@@ -12,9 +12,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
|
||||
|
||||
import SidebarIcon from './SidebarIcon.vue'
|
||||
|
||||
const bottomPanelStore = useBottomPanelStore()
|
||||
|
||||
/**
|
||||
* Toggle console bottom panel and track UI button click.
|
||||
*/
|
||||
const toggleConsole = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_bottom_panel_console_toggled'
|
||||
})
|
||||
bottomPanelStore.toggleBottomPanel()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -65,6 +65,7 @@ import { computed, onMounted, toRefs } from 'vue'
|
||||
|
||||
import HelpCenterMenuContent from '@/components/helpcenter/HelpCenterMenuContent.vue'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useReleaseStore } from '@/platform/updates/common/releaseStore'
|
||||
import ReleaseNotificationToast from '@/platform/updates/components/ReleaseNotificationToast.vue'
|
||||
import WhatsNewPopup from '@/platform/updates/components/WhatsNewPopup.vue'
|
||||
@@ -104,7 +105,13 @@ const sidebarLocation = computed(() =>
|
||||
settingStore.get('Comfy.Sidebar.Location')
|
||||
)
|
||||
|
||||
/**
|
||||
* Toggle Help Center and track UI button click.
|
||||
*/
|
||||
const toggleHelpCenter = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_help_center_toggled'
|
||||
})
|
||||
helpCenterStore.toggle()
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,11 @@ const computedTooltip = computed(() => t(tooltip) + tooltipSuffix)
|
||||
}
|
||||
|
||||
.side-bar-button-selected {
|
||||
background-color: var(--content-hover-bg);
|
||||
background-color: var(--interface-panel-selected-surface);
|
||||
color: var(--content-hover-fg);
|
||||
}
|
||||
.side-bar-button:hover {
|
||||
background-color: var(--interface-panel-hover-surface);
|
||||
color: var(--content-hover-fg);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useCommandStore } from '@/stores/commandStore'
|
||||
import { useBottomPanelStore } from '@/stores/workspace/bottomPanelStore'
|
||||
|
||||
@@ -34,7 +35,13 @@ const tooltipText = computed(
|
||||
() => `${t('shortcuts.keyboardShortcuts')} (${formatKeySequence(command)})`
|
||||
)
|
||||
|
||||
/**
|
||||
* Toggle keyboard shortcuts panel and track UI button click.
|
||||
*/
|
||||
const toggleShortcutsPanel = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_shortcuts_panel_toggled'
|
||||
})
|
||||
bottomPanelStore.togglePanel('shortcuts')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { computed } from 'vue'
|
||||
|
||||
import { useWorkflowTemplateSelectorDialog } from '@/composables/useWorkflowTemplateSelectorDialog'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
|
||||
import SidebarIcon from './SidebarIcon.vue'
|
||||
|
||||
@@ -23,7 +24,13 @@ const isSmall = computed(
|
||||
() => settingStore.get('Comfy.Sidebar.Size') === 'small'
|
||||
)
|
||||
|
||||
/**
|
||||
* Open templates dialog from sidebar and track UI button click.
|
||||
*/
|
||||
const openTemplates = () => {
|
||||
useTelemetry()?.trackUiButtonClicked({
|
||||
button_id: 'sidebar_templates_dialog_opened'
|
||||
})
|
||||
useWorkflowTemplateSelectorDialog().show('sidebar')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -170,7 +170,7 @@ import { formatDuration, getMediaTypeFromFilename } from '@/utils/formatUtil'
|
||||
|
||||
import AssetsSidebarTemplate from './AssetSidebarTemplate.vue'
|
||||
|
||||
const activeTab = ref<'input' | 'output'>('input')
|
||||
const activeTab = ref<'input' | 'output'>('output')
|
||||
const folderPromptId = ref<string | null>(null)
|
||||
const folderExecutionTime = ref<number | undefined>(undefined)
|
||||
const isInFolderView = computed(() => folderPromptId.value !== null)
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
<div>
|
||||
<Button
|
||||
v-if="isLoggedIn"
|
||||
class="user-profile-button p-1"
|
||||
class="user-profile-button p-1 hover:bg-transparent"
|
||||
severity="secondary"
|
||||
text
|
||||
:aria-label="$t('g.currentUser')"
|
||||
@click="popover?.toggle($event)"
|
||||
>
|
||||
<div class="flex items-center rounded-full bg-(--p-content-background)">
|
||||
<div
|
||||
class="flex items-center gap-1 rounded-full hover:bg-[var(--interface-button-hover-surface)]"
|
||||
>
|
||||
<UserAvatar :photo-url="photoURL" />
|
||||
|
||||
<i class="pi pi-chevron-down px-1" :style="{ fontSize: '0.5rem' }" />
|
||||
<i class="pi pi-chevron-down px-1" :style="{ fontSize: '0.6rem' }" />
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthAction
|
||||
import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import { useDialogService } from '@/services/dialogService'
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -136,6 +137,8 @@ const handleOpenPlanAndCreditsSettings = () => {
|
||||
}
|
||||
|
||||
const handleTopUp = () => {
|
||||
// Track purchase credits entry from avatar popover
|
||||
useTelemetry()?.trackAddApiCreditButtonClicked()
|
||||
dialogService.showTopUpCreditsDialog()
|
||||
emit('close')
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<template>
|
||||
<Button
|
||||
v-if="!isLoggedIn"
|
||||
:label="t('auth.login.loginButton')"
|
||||
outlined
|
||||
rounded
|
||||
severity="secondary"
|
||||
class="text-neutral border-black/50 px-4 capitalize dark-theme:border-white/50 dark-theme:text-white"
|
||||
class="size-8 border-black/50 bg-transparent text-black hover:bg-[var(--interface-panel-hover-surface)] dark-theme:border-white/50 dark-theme:text-white"
|
||||
@click="handleSignIn()"
|
||||
@mouseenter="showPopover"
|
||||
@mouseleave="hidePopover"
|
||||
/>
|
||||
|
||||
>
|
||||
<template #icon>
|
||||
<i class="icon-[lucide--user] size-4" />
|
||||
</template>
|
||||
</Button>
|
||||
<Popover
|
||||
ref="popoverRef"
|
||||
class="p-2"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="workflow-tabs-container flex h-full max-w-full flex-auto flex-row overflow-hidden"
|
||||
class="workflow-tabs-container flex h-full max-w-full flex-auto flex-row overflow-hidden border-b border-[var(--interface-stroke)] shadow-interface"
|
||||
:class="{ 'workflow-tabs-container-desktop': isDesktop }"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { FirebaseError } from 'firebase/app'
|
||||
import { AuthErrorCodes } from 'firebase/auth'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useErrorHandling } from '@/composables/useErrorHandling'
|
||||
import type { ErrorRecoveryStrategy } from '@/composables/useErrorHandling'
|
||||
@@ -61,8 +60,7 @@ export const useFirebaseAuthActions = () => {
|
||||
|
||||
if (isCloud) {
|
||||
try {
|
||||
const router = useRouter()
|
||||
await router.push({ name: 'cloud-login' })
|
||||
window.location.href = '/cloud/login'
|
||||
} catch (error) {
|
||||
// needed for local development until we bring in cloud login pages.
|
||||
window.location.reload()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useCurrentUser } from '@/composables/auth/useCurrentUser'
|
||||
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
|
||||
import { useSelectedLiteGraphItems } from '@/composables/canvas/useSelectedLiteGraphItems'
|
||||
import { useModelSelectorDialog } from '@/composables/useModelSelectorDialog'
|
||||
@@ -18,10 +19,11 @@ import {
|
||||
import type { Point } from '@/lib/litegraph/src/litegraph'
|
||||
import { useAssetBrowserDialog } from '@/platform/assets/composables/useAssetBrowserDialog'
|
||||
import { createModelNodeFromAsset } from '@/platform/assets/utils/createModelNodeFromAsset'
|
||||
import { isCloud } from '@/platform/distribution/types'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { SUPPORT_URL } from '@/platform/support/config'
|
||||
import { useTelemetry } from '@/platform/telemetry'
|
||||
import type { ExecutionTriggerSource } from '@/platform/telemetry/types'
|
||||
import { useToastStore } from '@/platform/updates/common/toastStore'
|
||||
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
|
||||
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
|
||||
@@ -466,7 +468,11 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
label: 'Queue Prompt',
|
||||
versionAdded: '1.3.7',
|
||||
category: 'essentials' as const,
|
||||
function: async () => {
|
||||
function: async (metadata?: {
|
||||
subscribe_to_run?: boolean
|
||||
trigger_source?: ExecutionTriggerSource
|
||||
}) => {
|
||||
useTelemetry()?.trackRunButton(metadata)
|
||||
if (!isActiveSubscription.value) {
|
||||
showSubscriptionDialog()
|
||||
return
|
||||
@@ -485,7 +491,11 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
label: 'Queue Prompt (Front)',
|
||||
versionAdded: '1.3.7',
|
||||
category: 'essentials' as const,
|
||||
function: async () => {
|
||||
function: async (metadata?: {
|
||||
subscribe_to_run?: boolean
|
||||
trigger_source?: ExecutionTriggerSource
|
||||
}) => {
|
||||
useTelemetry()?.trackRunButton(metadata)
|
||||
if (!isActiveSubscription.value) {
|
||||
showSubscriptionDialog()
|
||||
return
|
||||
@@ -503,7 +513,11 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
icon: 'pi pi-play',
|
||||
label: 'Queue Selected Output Nodes',
|
||||
versionAdded: '1.19.6',
|
||||
function: async () => {
|
||||
function: async (metadata?: {
|
||||
subscribe_to_run?: boolean
|
||||
trigger_source?: ExecutionTriggerSource
|
||||
}) => {
|
||||
useTelemetry()?.trackRunButton(metadata)
|
||||
if (!isActiveSubscription.value) {
|
||||
showSubscriptionDialog()
|
||||
return
|
||||
@@ -526,6 +540,17 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
// Get execution IDs for all selected output nodes and their descendants
|
||||
const executionIds =
|
||||
getExecutionIdsForSelectedNodes(selectedOutputNodes)
|
||||
|
||||
if (executionIds.length === 0) {
|
||||
toastStore.add({
|
||||
severity: 'error',
|
||||
summary: t('toastMessages.failedToQueue'),
|
||||
detail: t('toastMessages.failedExecutionPathResolution'),
|
||||
life: 3000
|
||||
})
|
||||
return
|
||||
}
|
||||
useTelemetry()?.trackWorkflowExecution()
|
||||
await app.queuePrompt(0, batchCount, executionIds)
|
||||
}
|
||||
},
|
||||
@@ -816,7 +841,26 @@ export function useCoreCommands(): ComfyCommand[] {
|
||||
label: 'Contact Support',
|
||||
versionAdded: '1.17.8',
|
||||
function: () => {
|
||||
window.open(SUPPORT_URL, '_blank')
|
||||
// OSS link for all non-cloud versions (portable, desktop, localhost)
|
||||
// Cloud link only for cloud distribution
|
||||
const { userEmail, resolvedUserInfo } = useCurrentUser()
|
||||
|
||||
const params = new URLSearchParams({
|
||||
tf_42243568391700: isCloud ? 'ccloud' : 'oss'
|
||||
})
|
||||
|
||||
// Add user email and ID if available
|
||||
if (userEmail.value) {
|
||||
params.append('tf_anonymous_requester_email', userEmail.value)
|
||||
params.append('tf_40029135130388', userEmail.value)
|
||||
}
|
||||
if (resolvedUserInfo.value?.id) {
|
||||
params.append('tf_42515251051412', resolvedUserInfo.value.id)
|
||||
}
|
||||
|
||||
const baseUrl = 'https://support.comfy.org/hc/en-us/requests/new'
|
||||
const zendeskUrl = `${baseUrl}?${params.toString()}`
|
||||
window.open(zendeskUrl, '_blank')
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ export function useTemplateFiltering(
|
||||
const searchQuery = ref('')
|
||||
const selectedModels = ref<string[]>([])
|
||||
const selectedUseCases = ref<string[]>([])
|
||||
const selectedLicenses = ref<string[]>([])
|
||||
const selectedRunsOn = ref<string[]>([])
|
||||
const sortBy = ref<
|
||||
| 'default'
|
||||
| 'alphabetical'
|
||||
@@ -63,8 +63,8 @@ export function useTemplateFiltering(
|
||||
return Array.from(tagSet).sort()
|
||||
})
|
||||
|
||||
const availableLicenses = computed(() => {
|
||||
return ['Open Source', 'Closed Source (API Nodes)']
|
||||
const availableRunsOn = computed(() => {
|
||||
return ['ComfyUI', 'External or Remote API']
|
||||
})
|
||||
|
||||
const debouncedSearchQuery = refDebounced(searchQuery, 50)
|
||||
@@ -108,22 +108,23 @@ export function useTemplateFiltering(
|
||||
})
|
||||
})
|
||||
|
||||
const filteredByLicenses = computed(() => {
|
||||
if (selectedLicenses.value.length === 0) {
|
||||
const filteredByRunsOn = computed(() => {
|
||||
if (selectedRunsOn.value.length === 0) {
|
||||
return filteredByUseCases.value
|
||||
}
|
||||
|
||||
return filteredByUseCases.value.filter((template) => {
|
||||
// Check if template has API in its tags or name (indicating it's a closed source API node)
|
||||
const isApiTemplate =
|
||||
template.tags?.includes('API') ||
|
||||
template.name?.toLowerCase().includes('api_')
|
||||
// Use openSource field to determine where template runs
|
||||
// openSource === false -> External/Remote API
|
||||
// openSource !== false -> ComfyUI (includes true and undefined)
|
||||
const isExternalAPI = template.openSource === false
|
||||
const isComfyUI = template.openSource !== false
|
||||
|
||||
return selectedLicenses.value.some((selectedLicense) => {
|
||||
if (selectedLicense === 'Closed Source (API Nodes)') {
|
||||
return isApiTemplate
|
||||
} else if (selectedLicense === 'Open Source') {
|
||||
return !isApiTemplate
|
||||
return selectedRunsOn.value.some((selectedRunsOn) => {
|
||||
if (selectedRunsOn === 'External or Remote API') {
|
||||
return isExternalAPI
|
||||
} else if (selectedRunsOn === 'ComfyUI') {
|
||||
return isComfyUI
|
||||
}
|
||||
return false
|
||||
})
|
||||
@@ -142,7 +143,7 @@ export function useTemplateFiltering(
|
||||
}
|
||||
|
||||
const sortedTemplates = computed(() => {
|
||||
const templates = [...filteredByLicenses.value]
|
||||
const templates = [...filteredByRunsOn.value]
|
||||
|
||||
switch (sortBy.value) {
|
||||
case 'alphabetical':
|
||||
@@ -195,7 +196,7 @@ export function useTemplateFiltering(
|
||||
searchQuery.value = ''
|
||||
selectedModels.value = []
|
||||
selectedUseCases.value = []
|
||||
selectedLicenses.value = []
|
||||
selectedRunsOn.value = []
|
||||
sortBy.value = 'default'
|
||||
}
|
||||
|
||||
@@ -207,8 +208,8 @@ export function useTemplateFiltering(
|
||||
selectedUseCases.value = selectedUseCases.value.filter((t) => t !== tag)
|
||||
}
|
||||
|
||||
const removeLicenseFilter = (license: string) => {
|
||||
selectedLicenses.value = selectedLicenses.value.filter((l) => l !== license)
|
||||
const removeRunsOnFilter = (runsOn: string) => {
|
||||
selectedRunsOn.value = selectedRunsOn.value.filter((r) => r !== runsOn)
|
||||
}
|
||||
|
||||
const filteredCount = computed(() => filteredTemplates.value.length)
|
||||
@@ -220,7 +221,7 @@ export function useTemplateFiltering(
|
||||
search_query: searchQuery.value || undefined,
|
||||
selected_models: selectedModels.value,
|
||||
selected_use_cases: selectedUseCases.value,
|
||||
selected_licenses: selectedLicenses.value,
|
||||
selected_runs_on: selectedRunsOn.value,
|
||||
sort_by: sortBy.value,
|
||||
filtered_count: filteredCount.value,
|
||||
total_count: totalCount.value
|
||||
@@ -229,14 +230,14 @@ export function useTemplateFiltering(
|
||||
|
||||
// Watch for filter changes and track them
|
||||
watch(
|
||||
[searchQuery, selectedModels, selectedUseCases, selectedLicenses, sortBy],
|
||||
[searchQuery, selectedModels, selectedUseCases, selectedRunsOn, sortBy],
|
||||
() => {
|
||||
// Only track if at least one filter is active (to avoid tracking initial state)
|
||||
const hasActiveFilters =
|
||||
searchQuery.value.trim() !== '' ||
|
||||
selectedModels.value.length > 0 ||
|
||||
selectedUseCases.value.length > 0 ||
|
||||
selectedLicenses.value.length > 0 ||
|
||||
selectedRunsOn.value.length > 0 ||
|
||||
sortBy.value !== 'default'
|
||||
|
||||
if (hasActiveFilters) {
|
||||
@@ -251,14 +252,14 @@ export function useTemplateFiltering(
|
||||
searchQuery,
|
||||
selectedModels,
|
||||
selectedUseCases,
|
||||
selectedLicenses,
|
||||
selectedRunsOn,
|
||||
sortBy,
|
||||
|
||||
// Computed
|
||||
filteredTemplates,
|
||||
availableModels,
|
||||
availableUseCases,
|
||||
availableLicenses,
|
||||
availableRunsOn,
|
||||
filteredCount,
|
||||
totalCount,
|
||||
|
||||
@@ -266,6 +267,6 @@ export function useTemplateFiltering(
|
||||
resetFilters,
|
||||
removeModelFilter,
|
||||
removeUseCaseFilter,
|
||||
removeLicenseFilter
|
||||
removeRunsOnFilter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1694,21 +1694,22 @@ const ext: ComfyExtension = {
|
||||
label: 'Convert selected nodes to group node',
|
||||
icon: 'pi pi-sitemap',
|
||||
versionAdded: '1.3.17',
|
||||
function: convertSelectedNodesToGroupNode
|
||||
function: () => convertSelectedNodesToGroupNode()
|
||||
},
|
||||
{
|
||||
id: 'Comfy.GroupNode.UngroupSelectedGroupNodes',
|
||||
label: 'Ungroup selected group nodes',
|
||||
icon: 'pi pi-sitemap',
|
||||
versionAdded: '1.3.17',
|
||||
function: ungroupSelectedGroupNodes
|
||||
function: () => ungroupSelectedGroupNodes()
|
||||
},
|
||||
{
|
||||
id: 'Comfy.GroupNode.ManageGroupNodes',
|
||||
label: 'Manage group nodes',
|
||||
icon: 'pi pi-cog',
|
||||
versionAdded: '1.3.17',
|
||||
function: manageGroupNodes
|
||||
function: (...args: unknown[]) =>
|
||||
manageGroupNodes(args[0] as string | undefined)
|
||||
}
|
||||
],
|
||||
keybindings: [
|
||||
|
||||
@@ -2169,7 +2169,7 @@ export class LGraphCanvas
|
||||
}
|
||||
}
|
||||
|
||||
processMouseDown(e: PointerEvent): void {
|
||||
processMouseDown(e: MouseEvent): void {
|
||||
if (
|
||||
this.dragZoomEnabled &&
|
||||
e.ctrlKey &&
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "منظور"
|
||||
},
|
||||
"clearRecording": "مسح التسجيل",
|
||||
"edgeThreshold": "عتبة الحواف",
|
||||
"export": "تصدير",
|
||||
"exportModel": "تصدير النموذج",
|
||||
"exportRecording": "تصدير التسجيل",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "وضع المادة",
|
||||
"materialModes": {
|
||||
"depth": "العمق",
|
||||
"lineart": "رسم الخطوط",
|
||||
"normal": "عادي",
|
||||
"original": "أصلي",
|
||||
"wireframe": "إطار سلكي"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "تحميل ثلاثي الأبعاد - حركة",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "الارتفاع"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "ملف النموذج"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "العرض"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "معلومات الكاميرا"
|
||||
},
|
||||
"image": {
|
||||
"name": "الصورة"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "ملف النموذج"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "رؤية الشبكة الابتدائية",
|
||||
"tooltip": "يتحكم في ظهور الشبكة بشكل افتراضي عند إنشاء عنصر ثلاثي الأبعاد جديد."
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "رؤية المعاينة الابتدائية",
|
||||
"tooltip": "يتحكم في ظهور شاشة المعاينة بشكل افتراضي عند إنشاء عنصر ثلاثي الأبعاد جديد."
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "اللغة"
|
||||
},
|
||||
|
||||
@@ -180,6 +180,10 @@
|
||||
"title": "Title",
|
||||
"edit": "Edit",
|
||||
"copy": "Copy",
|
||||
"copyJobId": "Copy Job ID",
|
||||
"copied": "Copied",
|
||||
"jobIdCopied": "Job ID copied to clipboard",
|
||||
"failedToCopyJobId": "Failed to copy job ID",
|
||||
"imageUrl": "Image URL",
|
||||
"clear": "Clear",
|
||||
"clearAll": "Clear all",
|
||||
@@ -607,17 +611,8 @@
|
||||
"nodes": "Nodes",
|
||||
"models": "Models",
|
||||
"workflows": "Workflows",
|
||||
"templates": "Templates",
|
||||
"console": "Console",
|
||||
"menu": "Menu",
|
||||
"assets": "Assets",
|
||||
"imported": "Imported",
|
||||
"generated": "Generated"
|
||||
"templates": "Templates"
|
||||
},
|
||||
"noFilesFound": "No files found",
|
||||
"noImportedFiles": "No imported files found",
|
||||
"noGeneratedFiles": "No generated files found",
|
||||
"noFilesFoundMessage": "Upload files or generate content to see them here",
|
||||
"browseTemplates": "Browse example templates",
|
||||
"openWorkflow": "Open workflow in local file system",
|
||||
"newBlankWorkflow": "Create a new blank workflow",
|
||||
@@ -754,7 +749,314 @@
|
||||
"Image API": "Image API",
|
||||
"Video API": "Video API",
|
||||
"LLM API": "LLM API",
|
||||
"All": "All Templates"
|
||||
"All": "All Templates",
|
||||
"Extensions": "Extensions",
|
||||
"Partner Nodes": "Partner Nodes",
|
||||
"Generation Type": "Generation Type"
|
||||
},
|
||||
"templateDescription": {
|
||||
"Basics": {
|
||||
"default": "Generate images from text prompts.",
|
||||
"image2image": "Transform existing images using text prompts.",
|
||||
"lora": "Generate images with LoRA models for specialized styles or subjects.",
|
||||
"lora_multiple": "Generate images by combining multiple LoRA models.",
|
||||
"inpaint_example": "Edit specific parts of images seamlessly.",
|
||||
"inpaint_model_outpainting": "Extend images beyond their original boundaries.",
|
||||
"embedding_example": "Generate images using textual inversion for consistent styles.",
|
||||
"gligen_textbox_example": "Generate images with precise object placement using text boxes."
|
||||
},
|
||||
"Flux": {
|
||||
"flux_kontext_dev_basic": "Edit image using Flux Kontext with full node visibility, perfect for learning the workflow.",
|
||||
"flux_kontext_dev_grouped": "Streamlined version of Flux Kontext with grouped nodes for cleaner workspace.",
|
||||
"flux_dev_checkpoint_example": "Generate images using Flux Dev fp8 quantized version. Suitable for devices with limited VRAM, requires only one model file, but image quality is slightly lower than the full version.",
|
||||
"flux_schnell": "Quickly generate images with Flux Schnell fp8 quantized version. Ideal for low-end hardware, requires only 4 steps to generate images.",
|
||||
"flux_dev_full_text_to_image": "Generate high-quality images with Flux Dev full version. Requires larger VRAM and multiple model files, but provides the best prompt following capability and image quality.",
|
||||
"flux_schnell_full_text_to_image": "Generate images quickly with Flux Schnell full version. Uses Apache2.0 license, requires only 4 steps to generate images while maintaining good image quality.",
|
||||
"flux_fill_inpaint_example": "Fill missing parts of images using Flux inpainting.",
|
||||
"flux_fill_outpaint_example": "Extend images beyond boundaries using Flux outpainting.",
|
||||
"flux_canny_model_example": "Generate images guided by edge detection using Flux Canny.",
|
||||
"flux_depth_lora_example": "Generate images guided by depth information using Flux LoRA.",
|
||||
"flux_redux_model_example": "Generate images by transferring style from reference images using Flux Redux."
|
||||
},
|
||||
"Image": {
|
||||
"image_omnigen2_t2i": "Generate high-quality images from text prompts using OmniGen2's unified 7B multimodal model with dual-path architecture.",
|
||||
"image_omnigen2_image_edit": "Edit images with natural language instructions using OmniGen2's advanced image editing capabilities and text rendering support.",
|
||||
"image_cosmos_predict2_2B_t2i": "Generate images with Cosmos-Predict2 2B T2I, delivering physically accurate, high-fidelity, and detail-rich image generation.",
|
||||
"image_chroma_text_to_image": "Chroma is modified from flux and has some changes in the architecture.",
|
||||
"hidream_i1_dev": "Generate images with HiDream I1 Dev - Balanced version with 28 inference steps, suitable for medium-range hardware.",
|
||||
"hidream_i1_fast": "Generate images quickly with HiDream I1 Fast - Lightweight version with 16 inference steps, ideal for rapid previews on lower-end hardware.",
|
||||
"hidream_i1_full": "Generate images with HiDream I1 Full - Complete version with 50 inference steps for highest quality output.",
|
||||
"hidream_e1_full": "Edit images with HiDream E1 - Professional natural language image editing model.",
|
||||
"sd3_5_simple_example": "Generate images using SD 3.5.",
|
||||
"sd3_5_large_canny_controlnet_example": "Generate images guided by edge detection using SD 3.5 Canny ControlNet.",
|
||||
"sd3_5_large_depth": "Generate images guided by depth information using SD 3.5.",
|
||||
"sd3_5_large_blur": "Generate images guided by blurred reference images using SD 3.5.",
|
||||
"sdxl_simple_example": "Generate high-quality images using SDXL.",
|
||||
"sdxl_refiner_prompt_example": "Enhance SDXL images using refiner models.",
|
||||
"sdxl_revision_text_prompts": "Generate images by transferring concepts from reference images using SDXL Revision.",
|
||||
"sdxl_revision_zero_positive": "Generate images using both text prompts and reference images with SDXL Revision.",
|
||||
"sdxlturbo_example": "Generate images in a single step using SDXL Turbo.",
|
||||
"image_lotus_depth_v1_1": "Run Lotus Depth in ComfyUI for zero-shot, efficient monocular depth estimation with high detail retention."
|
||||
},
|
||||
"Video": {
|
||||
"video_cosmos_predict2_2B_video2world_480p_16fps": "Generate videos with Cosmos-Predict2 2B Video2World, generating physically accurate, high-fidelity, and consistent video simulations.",
|
||||
"video_wan_vace_14B_t2v": "Transform text descriptions into high-quality videos. Supports both 480p and 720p with VACE-14B model.",
|
||||
"video_wan_vace_14B_ref2v": "Create videos that match the style and content of a reference image. Perfect for style-consistent video generation.",
|
||||
"video_wan_vace_14B_v2v": "Generate videos by controlling input videos and reference images using Wan VACE.",
|
||||
"video_wan_vace_outpainting": "Generate extended videos by expanding video size using Wan VACE outpainting.",
|
||||
"video_wan_vace_flf2v": "Generate smooth video transitions by defining start and end frames. Supports custom keyframe sequences.",
|
||||
"video_wan_vace_inpainting": "Edit specific regions in videos while preserving surrounding content. Great for object removal or replacement.",
|
||||
"video_wan2_1_fun_camera_v1_1_1_3B": "Generate dynamic videos with cinematic camera movements using Wan 2.1 Fun Camera 1.3B model.",
|
||||
"video_wan2_1_fun_camera_v1_1_14B": "Generate high-quality videos with advanced camera control using the full 14B model",
|
||||
"text_to_video_wan": "Generate videos from text prompts using Wan 2.1.",
|
||||
"image_to_video_wan": "Generate videos from images using Wan 2.1.",
|
||||
"wan2_1_fun_inp": "Generate videos from start and end frames using Wan 2.1 inpainting.",
|
||||
"wan2_1_fun_control": "Generate videos guided by pose, depth, and edge controls using Wan 2.1 ControlNet.",
|
||||
"wan2_1_flf2v_720_f16": "Generate videos by controlling first and last frames using Wan 2.1 FLF2V.",
|
||||
"ltxv_text_to_video": "Generate videos from text prompts.",
|
||||
"ltxv_image_to_video": "Generate videos from still images.",
|
||||
"mochi_text_to_video_example": "Generate videos from text prompts using Mochi model.",
|
||||
"hunyuan_video_text_to_video": "Generate videos from text prompts using Hunyuan model.",
|
||||
"image_to_video": "Generate videos from still images.",
|
||||
"txt_to_image_to_video": "Generate videos by first creating images from text prompts."
|
||||
},
|
||||
"Image API": {
|
||||
"api_bfl_flux_1_kontext_multiple_images_input": "Input multiple images and edit them with Flux.1 Kontext.",
|
||||
"api_bfl_flux_1_kontext_pro_image": "Edit images with Flux.1 Kontext pro image.",
|
||||
"api_bfl_flux_1_kontext_max_image": "Edit images with Flux.1 Kontext max image.",
|
||||
"api_bfl_flux_pro_t2i": "Generate images with excellent prompt following and visual quality using FLUX.1 Pro.",
|
||||
"api_luma_photon_i2i": "Guide image generation using a combination of images and prompt.",
|
||||
"api_luma_photon_style_ref": "Generate images by blending style references with precise control using Luma Photon.",
|
||||
"api_recraft_image_gen_with_color_control": "Generate images with custom color palettes and brand-specific visuals using Recraft.",
|
||||
"api_recraft_image_gen_with_style_control": "Control style with visual examples, align positioning, and fine-tune objects. Store and share styles for perfect brand consistency.",
|
||||
"api_recraft_vector_gen": "Generate high-quality vector images from text prompts using Recraft's AI vector generator.",
|
||||
"api_runway_text_to_image": "Generate high-quality images from text prompts using Runway's AI model.",
|
||||
"api_runway_reference_to_image": "Generate new images based on reference styles and compositions with Runway's AI.",
|
||||
"api_stability_ai_stable_image_ultra_t2i": "Generate high quality images with excellent prompt adherence. Perfect for professional use cases at 1 megapixel resolution.",
|
||||
"api_stability_ai_i2i": "Transform images with high-quality generation using Stability AI, perfect for professional editing and style transfer.",
|
||||
"api_stability_ai_sd3_5_t2i": "Generate high quality images with excellent prompt adherence. Perfect for professional use cases at 1 megapixel resolution.",
|
||||
"api_stability_ai_sd3_5_i2i": "Generate high quality images with excellent prompt adherence. Perfect for professional use cases at 1 megapixel resolution.",
|
||||
"api_ideogram_v3_t2i": "Generate professional-quality images with excellent prompt alignment, photorealism, and text rendering using Ideogram V3.",
|
||||
"api_openai_image_1_t2i": "Generate images from text prompts using OpenAI GPT Image 1 API.",
|
||||
"api_openai_image_1_i2i": "Generate images from input images using OpenAI GPT Image 1 API.",
|
||||
"api_openai_image_1_inpaint": "Edit images using inpainting with OpenAI GPT Image 1 API.",
|
||||
"api_openai_image_1_multi_inputs": "Generate images from multiple inputs using OpenAI GPT Image 1 API.",
|
||||
"api_openai_dall_e_2_t2i": "Generate images from text prompts using OpenAI Dall-E 2 API.",
|
||||
"api_openai_dall_e_2_inpaint": "Edit images using inpainting with OpenAI Dall-E 2 API.",
|
||||
"api_openai_dall_e_3_t2i": "Generate images from text prompts using OpenAI Dall-E 3 API."
|
||||
},
|
||||
"Video API": {
|
||||
"api_moonvalley_text_to_video": "Generate cinematic, 1080p videos from text prompts through a model trained exclusively on licensed data.",
|
||||
"api_moonvalley_image_to_video": "Generate cinematic, 1080p videos with an image through a model trained exclusively on licensed data.",
|
||||
"api_kling_i2v": "Generate videos with excellent prompt adherence for actions, expressions, and camera movements using Kling.",
|
||||
"api_kling_effects": "Generate dynamic videos by applying visual effects to images using Kling.",
|
||||
"api_kling_flf": "Generate videos through controlling the first and last frames.",
|
||||
"api_luma_i2v": "Take static images and instantly create magical high quality animations.",
|
||||
"api_luma_t2v": "High-quality videos can be generated using simple prompts.",
|
||||
"api_hailuo_minimax_t2v": "Generate high-quality videos directly from text prompts. Explore MiniMax's advanced AI capabilities to create diverse visual narratives with professional CGI effects and stylistic elements to bring your descriptions to life.",
|
||||
"api_hailuo_minimax_i2v": "Generate refined videos from images and text with CGI integration using MiniMax.",
|
||||
"api_pixverse_i2v": "Generate dynamic videos from static images with motion and effects using PixVerse.",
|
||||
"api_pixverse_template_i2v": "Generate dynamic videos from static images with motion and effects using PixVerse.",
|
||||
"api_pixverse_t2v": "Generate videos with accurate prompt interpretation and stunning video dynamics.",
|
||||
"api_runway_gen3a_turbo_image_to_video": "Generate cinematic videos from static images using Runway Gen3a Turbo.",
|
||||
"api_runway_gen4_turo_image_to_video": "Generate dynamic videos from images using Runway Gen4 Turbo.",
|
||||
"api_runway_first_last_frame": "Generate smooth video transitions between two keyframes with Runway's precision.",
|
||||
"api_pika_i2v": "Generate smooth animated videos from single static images using Pika AI.",
|
||||
"api_pika_scene": "Generate videos that incorporate multiple input images using Pika Scenes.",
|
||||
"api_veo2_i2v": "Generate videos from images using Google Veo2 API."
|
||||
},
|
||||
"3D API": {
|
||||
"api_rodin_image_to_model": "Generate detailed 3D models from single photos using Rodin AI.",
|
||||
"api_rodin_multiview_to_model": "Sculpt comprehensive 3D models using Rodin's multi-angle reconstruction.",
|
||||
"api_tripo_text_to_model": "Craft 3D objects from descriptions with Tripo's text-driven modeling.",
|
||||
"api_tripo_image_to_model": "Generate professional 3D assets from 2D images using Tripo engine.",
|
||||
"api_tripo_multiview_to_model": "Build 3D models from multiple angles with Tripo's advanced scanner."
|
||||
},
|
||||
"LLM API": {
|
||||
"api_openai_chat": "Engage with OpenAI's advanced language models for intelligent conversations.",
|
||||
"api_google_gemini": "Experience Google's multimodal AI with Gemini's reasoning capabilities."
|
||||
},
|
||||
"Upscaling": {
|
||||
"hiresfix_latent_workflow": "Upscale images by enhancing quality in latent space.",
|
||||
"esrgan_example": "Upscale images using ESRGAN models to enhance quality.",
|
||||
"hiresfix_esrgan_workflow": "Upscale images using ESRGAN models during intermediate generation steps.",
|
||||
"latent_upscale_different_prompt_model": "Upscale images while changing prompts across generation passes."
|
||||
},
|
||||
"ControlNet": {
|
||||
"controlnet_example": "Generate images guided by scribble reference images using ControlNet.",
|
||||
"2_pass_pose_worship": "Generate images guided by pose references using ControlNet.",
|
||||
"depth_controlnet": "Generate images guided by depth information using ControlNet.",
|
||||
"depth_t2i_adapter": "Generate images guided by depth information using T2I adapter.",
|
||||
"mixing_controlnets": "Generate images by combining multiple ControlNet models."
|
||||
},
|
||||
"Area Composition": {
|
||||
"area_composition": "Generate images by controlling composition with defined areas.",
|
||||
"area_composition_square_area_for_subject": "Generate images with consistent subject placement using area composition."
|
||||
},
|
||||
"3D": {
|
||||
"3d_hunyuan3d_image_to_model": "Generate 3D models from single images using Hunyuan3D 2.0.",
|
||||
"3d_hunyuan3d_multiview_to_model": "Generate 3D models from multiple views using Hunyuan3D 2.0 MV.",
|
||||
"3d_hunyuan3d_multiview_to_model_turbo": "Generate 3D models from multiple views using Hunyuan3D 2.0 MV Turbo.",
|
||||
"stable_zero123_example": "Generate 3D views from single images using Stable Zero123."
|
||||
},
|
||||
"Audio": {
|
||||
"audio_stable_audio_example": "Generate audio from text prompts using Stable Audio.",
|
||||
"audio_ace_step_1_t2a_instrumentals": "Generate instrumental music from text prompts using ACE-Step v1.",
|
||||
"audio_ace_step_1_t2a_song": "Generate songs with vocals from text prompts using ACE-Step v1, supporting multilingual and style customization.",
|
||||
"audio_ace_step_1_m2m_editing": "Edit existing songs to change style and lyrics using ACE-Step v1 M2M."
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"Basics": {
|
||||
"default": "Image Generation",
|
||||
"image2image": "Image to Image",
|
||||
"lora": "LoRA",
|
||||
"lora_multiple": "LoRA Multiple",
|
||||
"inpaint_example": "Inpaint",
|
||||
"inpaint_model_outpainting": "Outpaint",
|
||||
"embedding_example": "Embedding",
|
||||
"gligen_textbox_example": "Gligen Textbox"
|
||||
},
|
||||
"Flux": {
|
||||
"flux_kontext_dev_basic": "Flux Kontext Dev(Basic)",
|
||||
"flux_kontext_dev_grouped": "Flux Kontext Dev(Grouped)",
|
||||
"flux_dev_checkpoint_example": "Flux Dev fp8",
|
||||
"flux_schnell": "Flux Schnell fp8",
|
||||
"flux_dev_full_text_to_image": "Flux Dev full text to image",
|
||||
"flux_schnell_full_text_to_image": "Flux Schnell full text to image",
|
||||
"flux_fill_inpaint_example": "Flux Inpaint",
|
||||
"flux_fill_outpaint_example": "Flux Outpaint",
|
||||
"flux_canny_model_example": "Flux Canny Model",
|
||||
"flux_depth_lora_example": "Flux Depth LoRA",
|
||||
"flux_redux_model_example": "Flux Redux Model"
|
||||
},
|
||||
"Image": {
|
||||
"image_omnigen2_t2i": "OmniGen2 Text to Image",
|
||||
"image_omnigen2_image_edit": "OmniGen2 Image Edit",
|
||||
"image_cosmos_predict2_2B_t2i": "Cosmos Predict2 2B T2I",
|
||||
"image_chroma_text_to_image": "Chroma text to image",
|
||||
"hidream_i1_dev": "HiDream I1 Dev",
|
||||
"hidream_i1_fast": "HiDream I1 Fast",
|
||||
"hidream_i1_full": "HiDream I1 Full",
|
||||
"hidream_e1_full": "HiDream E1 Full",
|
||||
"sd3_5_simple_example": "SD3.5 Simple",
|
||||
"sd3_5_large_canny_controlnet_example": "SD3.5 Large Canny ControlNet",
|
||||
"sd3_5_large_depth": "SD3.5 Large Depth",
|
||||
"sd3_5_large_blur": "SD3.5 Large Blur",
|
||||
"sdxl_simple_example": "SDXL Simple",
|
||||
"sdxl_refiner_prompt_example": "SDXL Refiner Prompt",
|
||||
"sdxl_revision_text_prompts": "SDXL Revision Text Prompts",
|
||||
"sdxl_revision_zero_positive": "SDXL Revision Zero Positive",
|
||||
"sdxlturbo_example": "SDXL Turbo",
|
||||
"image_lotus_depth_v1_1": "Lotus Depth"
|
||||
},
|
||||
"Video": {
|
||||
"video_cosmos_predict2_2B_video2world_480p_16fps": "Cosmos Predict2 2B Video2World 480p 16fps",
|
||||
"video_wan_vace_14B_t2v": "Wan VACE Text to Video",
|
||||
"video_wan_vace_14B_ref2v": "Wan VACE Reference to Video",
|
||||
"video_wan_vace_14B_v2v": "Wan VACE Control Video",
|
||||
"video_wan_vace_outpainting": "Wan VACE Outpainting",
|
||||
"video_wan_vace_flf2v": "Wan VACE First-Last Frame",
|
||||
"video_wan_vace_inpainting": "Wan VACE Inpainting",
|
||||
"video_wan2_1_fun_camera_v1_1_1_3B": "Wan 2.1 Fun Camera 1.3B",
|
||||
"video_wan2_1_fun_camera_v1_1_14B": "Wan 2.1 Fun Camera 14B",
|
||||
"text_to_video_wan": "Wan 2.1 Text to Video",
|
||||
"image_to_video_wan": "Wan 2.1 Image to Video",
|
||||
"wan2_1_fun_inp": "Wan 2.1 Inpainting",
|
||||
"wan2_1_fun_control": "Wan 2.1 ControlNet",
|
||||
"wan2_1_flf2v_720_f16": "Wan 2.1 FLF2V 720p F16",
|
||||
"ltxv_text_to_video": "LTXV Text to Video",
|
||||
"ltxv_image_to_video": "LTXV Image to Video",
|
||||
"mochi_text_to_video_example": "Mochi Text to Video",
|
||||
"hunyuan_video_text_to_video": "Hunyuan Video Text to Video",
|
||||
"image_to_video": "SVD Image to Video",
|
||||
"txt_to_image_to_video": "SVD Text to Image to Video"
|
||||
},
|
||||
"Image API": {
|
||||
"api_bfl_flux_1_kontext_multiple_images_input": "BFL Flux.1 Kontext Multiple Image Input",
|
||||
"api_bfl_flux_1_kontext_pro_image": "BFL Flux.1 Kontext Pro",
|
||||
"api_bfl_flux_1_kontext_max_image": "BFL Flux.1 Kontext Max",
|
||||
"api_bfl_flux_pro_t2i": "BFL Flux[Pro]: Text to Image",
|
||||
"api_luma_photon_i2i": "Luma Photon: Image to Image",
|
||||
"api_luma_photon_style_ref": "Luma Photon: Style Reference",
|
||||
"api_recraft_image_gen_with_color_control": "Recraft: Color Control Image Generation",
|
||||
"api_recraft_image_gen_with_style_control": "Recraft: Style Control Image Generation",
|
||||
"api_recraft_vector_gen": "Recraft: Vector Generation",
|
||||
"api_runway_text_to_image": "Runway: Text to Image",
|
||||
"api_runway_reference_to_image": "Runway: Reference to Image",
|
||||
"api_stability_ai_stable_image_ultra_t2i": "Stability AI: Stable Image Ultra Text to Image",
|
||||
"api_stability_ai_i2i": "Stability AI: Image to Image",
|
||||
"api_stability_ai_sd3_5_t2i": "Stability AI: SD3.5 Text to Image",
|
||||
"api_stability_ai_sd3_5_i2i": "Stability AI: SD3.5 Image to Image",
|
||||
"api_ideogram_v3_t2i": "Ideogram V3: Text to Image",
|
||||
"api_openai_image_1_t2i": "OpenAI: GPT-Image-1 Text to Image",
|
||||
"api_openai_image_1_i2i": "OpenAI: GPT-Image-1 Image to Image",
|
||||
"api_openai_image_1_inpaint": "OpenAI: GPT-Image-1 Inpaint",
|
||||
"api_openai_image_1_multi_inputs": "OpenAI: GPT-Image-1 Multi Inputs",
|
||||
"api_openai_dall_e_2_t2i": "OpenAI: Dall-E 2 Text to Image",
|
||||
"api_openai_dall_e_2_inpaint": "OpenAI: Dall-E 2 Inpaint",
|
||||
"api_openai_dall_e_3_t2i": "OpenAI: Dall-E 3 Text to Image"
|
||||
},
|
||||
"Video API": {
|
||||
"api_moonvalley_text_to_video": "Moonvalley: Text to Video",
|
||||
"api_moonvalley_image_to_video": "Moonvalley: Image to Video",
|
||||
"api_kling_i2v": "Kling: Image to Video",
|
||||
"api_kling_effects": "Kling: Video Effects",
|
||||
"api_kling_flf": "Kling: FLF2V",
|
||||
"api_luma_i2v": "Luma: Image to Video",
|
||||
"api_luma_t2v": "Luma: Text to Video",
|
||||
"api_hailuo_minimax_t2v": "MiniMax: Text to Video",
|
||||
"api_hailuo_minimax_i2v": "MiniMax: Image to Video",
|
||||
"api_pixverse_i2v": "PixVerse: Image to Video",
|
||||
"api_pixverse_template_i2v": "PixVerse Templates: Image to Video",
|
||||
"api_pixverse_t2v": "PixVerse: Text to Video",
|
||||
"api_runway_gen3a_turbo_image_to_video": "Runway: Gen3a Turbo Image to Video",
|
||||
"api_runway_gen4_turo_image_to_video": "Runway: Gen4 Turbo Image to Video",
|
||||
"api_runway_first_last_frame": "Runway: First Last Frame to Video",
|
||||
"api_pika_i2v": "Pika: Image to Video",
|
||||
"api_pika_scene": "Pika Scenes: Images to Video",
|
||||
"api_veo2_i2v": "Veo2: Image to Video"
|
||||
},
|
||||
"3D API": {
|
||||
"api_rodin_image_to_model": "Rodin: Image to Model",
|
||||
"api_rodin_multiview_to_model": "Rodin: Multiview to Model",
|
||||
"api_tripo_text_to_model": "Tripo: Text to Model",
|
||||
"api_tripo_image_to_model": "Tripo: Image to Model",
|
||||
"api_tripo_multiview_to_model": "Tripo: Multiview to Model"
|
||||
},
|
||||
"LLM API": {
|
||||
"api_openai_chat": "OpenAI: Chat",
|
||||
"api_google_gemini": "Google Gemini: Chat"
|
||||
},
|
||||
"Upscaling": {
|
||||
"hiresfix_latent_workflow": "Upscale",
|
||||
"esrgan_example": "ESRGAN",
|
||||
"hiresfix_esrgan_workflow": "HiresFix ESRGAN Workflow",
|
||||
"latent_upscale_different_prompt_model": "Latent Upscale Different Prompt Model"
|
||||
},
|
||||
"ControlNet": {
|
||||
"controlnet_example": "Scribble ControlNet",
|
||||
"2_pass_pose_worship": "Pose ControlNet 2 Pass",
|
||||
"depth_controlnet": "Depth ControlNet",
|
||||
"depth_t2i_adapter": "Depth T2I Adapter",
|
||||
"mixing_controlnets": "Mixing ControlNets"
|
||||
},
|
||||
"Area Composition": {
|
||||
"area_composition": "Area Composition",
|
||||
"area_composition_square_area_for_subject": "Area Composition Square Area for Subject"
|
||||
},
|
||||
"3D": {
|
||||
"3d_hunyuan3d_image_to_model": "Hunyuan3D 2.0",
|
||||
"3d_hunyuan3d_multiview_to_model": "Hunyuan3D 2.0 MV",
|
||||
"3d_hunyuan3d_multiview_to_model_turbo": "Hunyuan3D 2.0 MV Turbo",
|
||||
"stable_zero123_example": "Stable Zero123"
|
||||
},
|
||||
"Audio": {
|
||||
"audio_stable_audio_example": "Stable Audio",
|
||||
"audio_ace_step_1_t2a_instrumentals": "ACE-Step v1 Text to Instrumentals Music",
|
||||
"audio_ace_step_1_t2a_song": "ACE Step v1 Text to Song",
|
||||
"audio_ace_step_1_m2m_editing": "ACE Step v1 M2M Editing"
|
||||
}
|
||||
},
|
||||
"categories": "Categories",
|
||||
"resetFilters": "Clear Filters",
|
||||
@@ -766,7 +1068,8 @@
|
||||
"modelFilter": "Model Filter",
|
||||
"modelsSelected": "{count} Models",
|
||||
"useCasesSelected": "{count} Use Cases",
|
||||
"licensesSelected": "{count} Licenses",
|
||||
"runsOnSelected": "{count} Runs On",
|
||||
"runsOnFilter": "Runs on",
|
||||
"resultsCount": "Showing {count} of {total} templates",
|
||||
"sort": {
|
||||
"recommended": "Recommended",
|
||||
@@ -776,6 +1079,9 @@
|
||||
"vramLowToHigh": "VRAM Usage (Low to High)",
|
||||
"modelSizeLowToHigh": "Model Size (Low to High)",
|
||||
"default": "Default"
|
||||
},
|
||||
"error": {
|
||||
"templateNotFound": "Template \"{templateName}\" not found"
|
||||
}
|
||||
},
|
||||
"graphCanvasMenu": {
|
||||
@@ -1428,6 +1734,7 @@
|
||||
"camera": "Camera",
|
||||
"light": "Light",
|
||||
"switchingMaterialMode": "Switching Material Mode...",
|
||||
"edgeThreshold": "Edge Threshold",
|
||||
"export": "Export",
|
||||
"exportModel": "Export Model",
|
||||
"exportingModel": "Exporting model...",
|
||||
@@ -1438,7 +1745,8 @@
|
||||
"normal": "Normal",
|
||||
"wireframe": "Wireframe",
|
||||
"original": "Original",
|
||||
"depth": "Depth"
|
||||
"depth": "Depth",
|
||||
"lineart": "Lineart"
|
||||
},
|
||||
"upDirections": {
|
||||
"original": "Original"
|
||||
@@ -1464,14 +1772,13 @@
|
||||
"exportSettings": "Export Settings",
|
||||
"modelSettings": "Model Settings"
|
||||
},
|
||||
"openIn3DViewer": "Open in 3D Viewer",
|
||||
"dropToLoad": "Drop 3D model to load",
|
||||
"unsupportedFileType": "Unsupported file type (supports .gltf, .glb, .obj, .fbx, .stl)",
|
||||
"uploadingModel": "Uploading 3D model..."
|
||||
"openIn3DViewer": "Open in 3D Viewer"
|
||||
},
|
||||
"toastMessages": {
|
||||
"nothingToQueue": "Nothing to queue",
|
||||
"pleaseSelectOutputNodes": "Please select output nodes",
|
||||
"failedToQueue": "Failed to queue",
|
||||
"failedExecutionPathResolution": "Could not resolve path to selected nodes",
|
||||
"no3dScene": "No 3D scene to apply texture",
|
||||
"failedToApplyTexture": "Failed to apply texture",
|
||||
"no3dSceneToExport": "No 3D scene to export",
|
||||
@@ -1550,7 +1857,12 @@
|
||||
"confirmPasswordLabel": "Confirm Password",
|
||||
"confirmPasswordPlaceholder": "Enter the same password again",
|
||||
"forgotPassword": "Forgot password?",
|
||||
"loginButton": "Log in",
|
||||
"passwordResetInstructions": "Enter your email address and we'll send you a link to reset your password.",
|
||||
"sendResetLink": "Send reset link",
|
||||
"backToLogin": "Back to login",
|
||||
"didntReceiveEmail": "Didn't receive an email? Contact us at",
|
||||
"passwordResetError": "Failed to send password reset email. Please try again.",
|
||||
"loginButton": "Sign in",
|
||||
"orContinueWith": "Or continue with",
|
||||
"loginWithGoogle": "Log in with Google",
|
||||
"loginWithGithub": "Log in with Github",
|
||||
@@ -1587,6 +1899,20 @@
|
||||
"success": "Password Updated",
|
||||
"successDetail": "Your password has been updated successfully"
|
||||
},
|
||||
"errors": {
|
||||
"auth/invalid-email": "Please enter a valid email address.",
|
||||
"auth/user-disabled": "This account has been disabled. Please contact support.",
|
||||
"auth/user-not-found": "No account found with this email. Would you like to create a new account?",
|
||||
"auth/wrong-password": "The password you entered is incorrect. Please try again.",
|
||||
"auth/email-already-in-use": "An account with this email already exists. Try signing in instead.",
|
||||
"auth/weak-password": "Password is too weak. Please use a stronger password with at least 6 characters.",
|
||||
"auth/too-many-requests": "Too many login attempts. Please wait a moment and try again.",
|
||||
"auth/operation-not-allowed": "This sign-in method is not currently supported.",
|
||||
"auth/invalid-credential": "Invalid login credentials. Please check your email and password.",
|
||||
"auth/network-request-failed": "Network error. Please check your connection and try again.",
|
||||
"auth/popup-closed-by-user": "Sign-in was cancelled. Please try again.",
|
||||
"auth/cancelled-popup-request": "Sign-in was cancelled. Please try again."
|
||||
},
|
||||
"deleteAccount": {
|
||||
"deleteAccount": "Delete Account",
|
||||
"confirmTitle": "Delete Account",
|
||||
@@ -1687,7 +2013,8 @@
|
||||
"waitingForSubscription": "Complete your subscription in the new tab. We'll automatically detect when you're done!",
|
||||
"subscribe": "Subscribe"
|
||||
},
|
||||
"subscribeToRun": "Subscribe to Run",
|
||||
"subscribeToRun": "Subscribe",
|
||||
"subscribeToRunFull": "Subscribe to Run",
|
||||
"subscribeNow": "Subscribe Now",
|
||||
"subscribeToComfyCloud": "Subscribe to Comfy Cloud",
|
||||
"partnerNodesCredits": "Partner Nodes credits"
|
||||
@@ -1768,6 +2095,128 @@
|
||||
"renderBypassState": "Render Bypass State",
|
||||
"renderErrorState": "Render Error State"
|
||||
},
|
||||
"cloudOnboarding": {
|
||||
"survey": {
|
||||
"title": "Cloud Survey",
|
||||
"placeholder": "Survey questions placeholder",
|
||||
"steps": {
|
||||
"familiarity": "How familiar are you with ComfyUI?",
|
||||
"purpose": "What will you primarily use ComfyUI for?",
|
||||
"industry": "What's your primary industry?",
|
||||
"making": "What do you plan on making?"
|
||||
},
|
||||
"questions": {
|
||||
"familiarity": "How familiar are you with ComfyUI?",
|
||||
"purpose": "What will you primarily use ComfyUI for?",
|
||||
"industry": "What's your primary industry?",
|
||||
"making": "What do you plan on making?"
|
||||
},
|
||||
"options": {
|
||||
"familiarity": {
|
||||
"new": "New to ComfyUI (never used it before)",
|
||||
"starting": "Just getting started (following tutorials)",
|
||||
"basics": "Comfortable with basics",
|
||||
"advanced": "Advanced user (custom workflows)",
|
||||
"expert": "Expert (help others)"
|
||||
},
|
||||
"purpose": {
|
||||
"personal": "Personal projects / hobby",
|
||||
"community": "Community contributions (nodes, workflows, etc.)",
|
||||
"client": "Client work (freelance)",
|
||||
"inhouse": "My own workplace (in-house)",
|
||||
"research": "Academic research"
|
||||
},
|
||||
"industry": {
|
||||
"film_tv_animation": "Film, TV, & animation",
|
||||
"gaming": "Gaming",
|
||||
"marketing": "Marketing & advertising",
|
||||
"architecture": "Architecture",
|
||||
"product_design": "Product & graphic design",
|
||||
"fine_art": "Fine art & illustration",
|
||||
"software": "Software & technology",
|
||||
"education": "Education",
|
||||
"other": "Other",
|
||||
"otherPlaceholder": "Please specify"
|
||||
},
|
||||
"making": {
|
||||
"images": "Images",
|
||||
"video": "Video & animation",
|
||||
"3d": "3D assets",
|
||||
"audio": "Audio / music",
|
||||
"custom_nodes": "Custom nodes & workflows"
|
||||
}
|
||||
}
|
||||
},
|
||||
"forgotPassword": {
|
||||
"title": "Forgot Password",
|
||||
"instructions": "Enter your email address and we'll send you a link to reset your password.",
|
||||
"emailLabel": "Email",
|
||||
"emailPlaceholder": "Enter your email",
|
||||
"sendResetLink": "Send reset link",
|
||||
"backToLogin": "Back to login",
|
||||
"didntReceiveEmail": "Didn't receive an email? Contact us at",
|
||||
"passwordResetSent": "Password reset email sent",
|
||||
"passwordResetError": "Failed to send password reset email. Please try again.",
|
||||
"emailRequired": "Email is required"
|
||||
},
|
||||
"privateBeta": {
|
||||
"title": "Cloud is currently in private beta",
|
||||
"desc": "Sign in to join the waitlist. We’ll notify you when it’s your turn. Already been notified? Sign in start using Cloud."
|
||||
},
|
||||
"start": {
|
||||
"title": "start creating in seconds",
|
||||
"desc": "Zero setup required. Works on any device.",
|
||||
"explain": "Generate multiple outputs at once. Share workflows with ease.",
|
||||
"learnAboutButton": "Learn about Cloud",
|
||||
"wantToRun": "Want to run ComfyUI locally instead?",
|
||||
"download": "Download ComfyUI"
|
||||
},
|
||||
"checkingStatus": "Checking your account status...",
|
||||
"retrying": "Retrying...",
|
||||
"retry": "Try Again",
|
||||
"authTimeout": {
|
||||
"title": "Connection Taking Too Long",
|
||||
"message": "We're having trouble connecting to ComfyUI Cloud. This could be due to a slow connection or temporary service issue.",
|
||||
"restart": "Sign Out & Try Again",
|
||||
"troubleshooting": "Common causes:",
|
||||
"causes": [
|
||||
"Corporate firewall or proxy blocking authentication services",
|
||||
"VPN or network restrictions",
|
||||
"Browser extensions interfering with requests",
|
||||
"Regional network limitations",
|
||||
"Try a different browser or network"
|
||||
],
|
||||
"technicalDetails": "Technical Details",
|
||||
"helpText": "Need help? Contact",
|
||||
"supportLink": "support"
|
||||
}
|
||||
},
|
||||
"cloudFooter_needHelp": "Need Help?",
|
||||
"cloudStart_title": "start creating in seconds",
|
||||
"cloudStart_desc": "Zero setup required. Works on any device.",
|
||||
"cloudStart_explain": "Generate multiple outputs at once. Share workflows with ease.",
|
||||
"cloudStart_learnAboutButton": "Learn about Cloud",
|
||||
"cloudStart_wantToRun": "Want to run ComfyUI locally instead?",
|
||||
"cloudStart_download": "Download ComfyUI",
|
||||
"cloudWaitlist_questionsText": "Questions? Contact us",
|
||||
"cloudWaitlist_contactLink": "here",
|
||||
"cloudSorryContactSupport_title": "Sorry, contact support",
|
||||
"cloudPrivateBeta_title": "Cloud is currently in private beta",
|
||||
"cloudPrivateBeta_desc": "Sign in to join the waitlist. We'll notify you when it's your turn. Already been notified? Sign in start using Cloud.",
|
||||
"cloudForgotPassword_title": "Forgot Password",
|
||||
"cloudForgotPassword_instructions": "Enter your email address and we'll send you a link to reset your password.",
|
||||
"cloudForgotPassword_emailLabel": "Email",
|
||||
"cloudForgotPassword_emailPlaceholder": "Enter your email",
|
||||
"cloudForgotPassword_sendResetLink": "Send reset link",
|
||||
"cloudForgotPassword_backToLogin": "Back to login",
|
||||
"cloudForgotPassword_didntReceiveEmail": "Didn't receive an email?",
|
||||
"cloudForgotPassword_emailRequired": "Email is required",
|
||||
"cloudForgotPassword_passwordResetSent": "Password reset sent",
|
||||
"cloudForgotPassword_passwordResetError": "Failed to send password reset email",
|
||||
"cloudSurvey_steps_familiarity": "How familiar are you with ComfyUI?",
|
||||
"cloudSurvey_steps_purpose": "What will you primarily use ComfyUI for?",
|
||||
"cloudSurvey_steps_industry": "What's your primary industry?",
|
||||
"cloudSurvey_steps_making": "What do you plan on making?",
|
||||
"assetBrowser": {
|
||||
"assets": "Assets",
|
||||
"browseAssets": "Browse Assets",
|
||||
|
||||
@@ -170,10 +170,6 @@
|
||||
"name": "Initial Grid Visibility",
|
||||
"tooltip": "Controls whether the grid is visible by default when a new 3D widget is created. This default can still be toggled individually for each widget after creation."
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "Initial Preview Visibility",
|
||||
"tooltip": "Controls whether the preview screen is visible by default when a new 3D widget is created. This default can still be toggled individually for each widget after creation."
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "Language"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "Perspectiva"
|
||||
},
|
||||
"clearRecording": "Borrar grabación",
|
||||
"edgeThreshold": "Umbral de borde",
|
||||
"export": "Exportar",
|
||||
"exportModel": "Exportar modelo",
|
||||
"exportRecording": "Exportar grabación",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "Modo de material",
|
||||
"materialModes": {
|
||||
"depth": "Profundidad",
|
||||
"lineart": "Dibujo lineal",
|
||||
"normal": "Normal",
|
||||
"original": "Original",
|
||||
"wireframe": "Malla"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "Cargar 3D - Animación",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "alto"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "archivo_modelo"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "ancho"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "camera_info"
|
||||
},
|
||||
"image": {
|
||||
"name": "imagen"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "archivo_modelo"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "Mostrar Cuadrícula",
|
||||
"tooltip": "Cambiar para mostrar cuadrícula por defecto"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "Mostrar Previsualización",
|
||||
"tooltip": "Cambiar para mostrar previsualización por defecto"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "Idioma"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "Perspective"
|
||||
},
|
||||
"clearRecording": "Effacer l'enregistrement",
|
||||
"edgeThreshold": "Seuil de Bordure",
|
||||
"export": "Exportation",
|
||||
"exportModel": "Exportation du modèle",
|
||||
"exportRecording": "Exporter l'enregistrement",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "Mode Matériel",
|
||||
"materialModes": {
|
||||
"depth": "Profondeur",
|
||||
"lineart": "Dessin au trait",
|
||||
"normal": "Normal",
|
||||
"original": "Original",
|
||||
"wireframe": "Fil de fer"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "Charger 3D - Animation",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "hauteur"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "fichier_modèle"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "largeur"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "informations_camera"
|
||||
},
|
||||
"image": {
|
||||
"name": "image"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "fichier_modèle"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "Afficher la Grille",
|
||||
"tooltip": "Basculer pour afficher la grille par défaut"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "Afficher l'Aperçu",
|
||||
"tooltip": "Basculer pour afficher l'aperçu par défaut"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "Langue"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "パースペクティブ"
|
||||
},
|
||||
"clearRecording": "録画をクリア",
|
||||
"edgeThreshold": "エッジ閾値",
|
||||
"export": "エクスポート",
|
||||
"exportModel": "モデルをエクスポート",
|
||||
"exportRecording": "録画をエクスポート",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "マテリアルモード",
|
||||
"materialModes": {
|
||||
"depth": "深度",
|
||||
"lineart": "線画",
|
||||
"normal": "ノーマル",
|
||||
"original": "オリジナル",
|
||||
"wireframe": "ワイヤーフレーム"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "3D読み込み - アニメーション",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "高さ"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "モデルファイル"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "幅"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "カメラ情報"
|
||||
},
|
||||
"image": {
|
||||
"name": "画像"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "モデルファイル"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "グリッドを表示",
|
||||
"tooltip": "デフォルトでグリッドを表示するには切り替えます"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "プレビューを表示",
|
||||
"tooltip": "デフォルトでプレビューを表示するには切り替えます"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "言語"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "원근법"
|
||||
},
|
||||
"clearRecording": "녹화 지우기",
|
||||
"edgeThreshold": "엣지 임계값",
|
||||
"export": "내보내기",
|
||||
"exportModel": "모델 내보내기",
|
||||
"exportRecording": "녹화 내보내기",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "재질 모드",
|
||||
"materialModes": {
|
||||
"depth": "깊이",
|
||||
"lineart": "라인아트",
|
||||
"normal": "노멀(normal)",
|
||||
"original": "원본",
|
||||
"wireframe": "와이어프레임"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "3D 불러오기 - 애니메이션",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "높이"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "모델 파일"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "너비"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "카메라 정보"
|
||||
},
|
||||
"image": {
|
||||
"name": "이미지"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "모델 파일"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "그리드 표시",
|
||||
"tooltip": "기본적으로 그리드를 표시하도록 전환"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "미리보기 표시",
|
||||
"tooltip": "기본적으로 미리보기를 표시하도록 전환"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "언어"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "Перспектива"
|
||||
},
|
||||
"clearRecording": "Очистить запись",
|
||||
"edgeThreshold": "Пороговое значение края",
|
||||
"export": "Экспорт",
|
||||
"exportModel": "Экспорт модели",
|
||||
"exportRecording": "Экспортировать запись",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "Режим Материала",
|
||||
"materialModes": {
|
||||
"depth": "Глубина",
|
||||
"lineart": "Лайнарт",
|
||||
"normal": "Нормальный",
|
||||
"original": "Оригинал",
|
||||
"wireframe": "Каркас"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "Загрузить 3D - Анимация",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "высота"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "файл_модели"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "ширина"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "camera_info"
|
||||
},
|
||||
"image": {
|
||||
"name": "изображение"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "файл_модели"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "Показать сетку",
|
||||
"tooltip": "Переключиться, чтобы показывать сетку по умолчанию"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "Показать предварительный просмотр",
|
||||
"tooltip": "Переключиться, чтобы показывать предварительный просмотр по умолчанию"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "Язык"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "Perspektif"
|
||||
},
|
||||
"clearRecording": "Kaydı Temizle",
|
||||
"edgeThreshold": "Kenar Eşiği",
|
||||
"export": "Dışa Aktar",
|
||||
"exportModel": "Modeli Dışa Aktar",
|
||||
"exportRecording": "Kaydı Dışa Aktar",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "Malzeme Modu",
|
||||
"materialModes": {
|
||||
"depth": "Derinlik",
|
||||
"lineart": "Çizgi Sanatı",
|
||||
"normal": "Normal",
|
||||
"original": "Orijinal",
|
||||
"wireframe": "Tel Kafes"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "3D Yükle - Animasyon",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "yükseklik"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "model_dosyası"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "genişlik"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "kamera_bilgisi"
|
||||
},
|
||||
"image": {
|
||||
"name": "görüntü"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "model_dosyası"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "Başlangıç Izgara Görünürlüğü",
|
||||
"tooltip": "Yeni bir 3D widget oluşturulduğunda ızgaranın varsayılan olarak görünür olup olmadığını kontrol eder. Bu varsayılan, oluşturulduktan sonra her widget için ayrı ayrı değiştirilebilir."
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "Başlangıç Önizleme Görünürlüğü",
|
||||
"tooltip": "Yeni bir 3D widget oluşturulduğunda önizleme ekranının varsayılan olarak görünür olup olmadığını kontrol eder. Bu varsayılan, oluşturulduktan sonra her widget için ayrı ayrı değiştirilebilir."
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "Dil"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "透視"
|
||||
},
|
||||
"clearRecording": "清除錄影",
|
||||
"edgeThreshold": "邊緣閾值",
|
||||
"export": "匯出",
|
||||
"exportModel": "匯出模型",
|
||||
"exportRecording": "匯出錄影",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "材質模式",
|
||||
"materialModes": {
|
||||
"depth": "深度",
|
||||
"lineart": "線條藝術",
|
||||
"normal": "一般",
|
||||
"original": "原始",
|
||||
"wireframe": "線框"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "載入 3D - 動畫",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "高度"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "模型檔案"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "寬度"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "camera_info"
|
||||
},
|
||||
"image": {
|
||||
"name": "影像"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "model_file"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "初始網格可見性",
|
||||
"tooltip": "控制在建立新的 3D 元件時,網格是否預設可見。此預設值在建立後仍可針對每個元件單獨切換。"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "初始預覽可見性",
|
||||
"tooltip": "控制當新建 3D 元件時,預覽畫面預設是否顯示。此預設值在元件建立後仍可針對每個元件單獨切換。"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "語言"
|
||||
},
|
||||
|
||||
@@ -693,7 +693,6 @@
|
||||
"perspective": "透视"
|
||||
},
|
||||
"clearRecording": "清除录制",
|
||||
"edgeThreshold": "边缘阈值",
|
||||
"export": "导出",
|
||||
"exportModel": "导出模型",
|
||||
"exportRecording": "导出录制",
|
||||
@@ -706,7 +705,6 @@
|
||||
"materialMode": "材质模式",
|
||||
"materialModes": {
|
||||
"depth": "深度",
|
||||
"lineart": "线稿",
|
||||
"normal": "法线",
|
||||
"original": "原始",
|
||||
"wireframe": "线框"
|
||||
|
||||
@@ -3361,8 +3361,6 @@
|
||||
"Load3DAnimation": {
|
||||
"display_name": "加载3D动画",
|
||||
"inputs": {
|
||||
"clear": {
|
||||
},
|
||||
"height": {
|
||||
"name": "高度"
|
||||
},
|
||||
@@ -3372,8 +3370,6 @@
|
||||
"model_file": {
|
||||
"name": "模型文件"
|
||||
},
|
||||
"upload 3d model": {
|
||||
},
|
||||
"width": {
|
||||
"name": "宽度"
|
||||
}
|
||||
@@ -6352,9 +6348,6 @@
|
||||
"camera_info": {
|
||||
"name": "相机信息"
|
||||
},
|
||||
"image": {
|
||||
"name": "图像"
|
||||
},
|
||||
"model_file": {
|
||||
"name": "模型文件"
|
||||
}
|
||||
|
||||
@@ -155,10 +155,6 @@
|
||||
"name": "显示网格",
|
||||
"tooltip": "默认显示网格开关"
|
||||
},
|
||||
"Comfy_Load3D_ShowPreview": {
|
||||
"name": "显示预览",
|
||||
"tooltip": "默认显示预览开关"
|
||||
},
|
||||
"Comfy_Locale": {
|
||||
"name": "语言"
|
||||
},
|
||||
|
||||
@@ -24,18 +24,19 @@
|
||||
<div>
|
||||
<h3
|
||||
:id="titleId"
|
||||
v-tooltip.top="{ value: asset.name, showDelay: tooltipDelay }"
|
||||
:class="
|
||||
cn(
|
||||
'mb-2 m-0 text-base font-semibold line-clamp-2 wrap-anywhere',
|
||||
'text-base-foreground'
|
||||
)
|
||||
"
|
||||
:title="asset.name"
|
||||
>
|
||||
{{ asset.name }}
|
||||
</h3>
|
||||
<p
|
||||
:id="descId"
|
||||
v-tooltip.top="{ value: asset.description, showDelay: tooltipDelay }"
|
||||
:class="
|
||||
cn(
|
||||
'm-0 text-sm leading-6 overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:2] [display:-webkit-box]',
|
||||
@@ -43,7 +44,6 @@
|
||||
'dark-theme:text-slate-100'
|
||||
)
|
||||
"
|
||||
:title="asset.description"
|
||||
>
|
||||
{{ asset.description }}
|
||||
</p>
|
||||
@@ -76,6 +76,7 @@ import { computed, useId } from 'vue'
|
||||
|
||||
import AssetBadgeGroup from '@/platform/assets/components/AssetBadgeGroup.vue'
|
||||
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
|
||||
import { useSettingStore } from '@/platform/settings/settingStore'
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -83,9 +84,15 @@ const props = defineProps<{
|
||||
interactive?: boolean
|
||||
}>()
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const titleId = useId()
|
||||
const descId = useId()
|
||||
|
||||
const tooltipDelay = computed<number>(() =>
|
||||
settingStore.get('LiteGraph.Node.TooltipDelay')
|
||||
)
|
||||
|
||||
const { error } = useImage({
|
||||
src: props.asset.preview_url ?? '',
|
||||
alt: props.asset.name
|
||||
|
||||